Lesson 41-WinterJS Application Basics

Web Development Basics

Creating an HTTP Server

Basic HTTP Server

import { serve } from 'winter.serve';

// Create a basic HTTP server
const server = serve({
  port: 8080,
  hostname: '0.0.0.0'
});

console.log('Server running on http://localhost:8080');

// Request handling
for await (const req of server) {
  req.respond({
    status: 200,
    headers: { 'Content-Type': 'text/plain' },
    body: 'Hello World\n'
  });
}

Advanced Server Configuration

import { serve } from 'winter.serve';

const server = serve({
  port: 8080,
  maxConnections: 1000,       // Maximum connections
  keepAliveTimeout: 5000,     // Keep-Alive timeout (ms)
  security: {
    hsts: true,               // Enable HSTS
    csp: "default-src 'self'" // Content Security Policy
  }
});

Routing and Request Handling

Lightweight Middleware Mechanism

import { serve } from 'winter.serve';

// Middleware definition
async function loggerMiddleware(req, next) {
  console.log(`${req.method} ${req.url}`);
  await next();
}

async function authMiddleware(req, next) {
  if (!req.headers.get('Authorization')) {
    req.respond({ status: 401 });
    return;
  }
  await next();
}

// Request handling
async function handleRequest(req) {
  if (req.url === '/' && req.method === 'GET') {
    req.respond({ body: 'Home Page' });
  } else {
    req.respond({ status: 404 });
  }
}

// Middleware chain
const middlewareChain = [loggerMiddleware, authMiddleware];

// Request processing flow
for await (const req of server) {
  let index = 0;

  async function processMiddleware() {
    if (index < middlewareChain.length) {
      await middlewareChain[index++](req, processMiddleware);
    } else {
      await handleRequest(req);
    }
  }

  await processMiddleware();
}

Route Table Implementation

const routes = {
  'GET /': (req) => req.respond({ body: 'Home' }),
  'GET /about': (req) => req.respond({ body: 'About' }),
  'POST /api': (req) => handleApiRequest(req)
};

for await (const req of server) {
  const method = req.method;
  const path = req.url.split('?')[0];
  const handler = routes[`${method} ${path}`];

  if (handler) {
    handler(req);
  } else {
    req.respond({ status: 404 });
  }
}

Static File Serving

Basic Static File Configuration

import { serve } from 'winter.serve';

const server = serve({
  port: 8080,
  static: {
    root: './public',       // Static file root directory
    index: 'index.html',    // Default index file
    cacheControl: 'public, max-age=3600' // Cache control
  }
});

Advanced Static File Serving

const server = serve({
  static: {
    root: './public',
    extensions: ['html', 'htm'], // Auto-complete extensions
    setHeaders(res, path) {
      if (path.endsWith('.wasm')) {
        res.setHeader('Content-Type', 'application/wasm');
      }
    },
    redirect: true // Auto-redirect to /index.html
  }
});

Template Engine Integration

EJS Template Integration

import { serve } from 'winter.serve';
import ejs from 'ejs';

const server = serve({ port: 8080 });

// Template rendering middleware
async function renderTemplate(req, res, template, data) {
  const html = await ejs.renderFile(`./views/${template}.ejs`, data);
  res.respond({ body: html });
}

// Route handling
for await (const req of server) {
  if (req.url === '/' && req.method === 'GET') {
    await renderTemplate(req, {
      title: 'Home Page',
      items: ['Item 1', 'Item 2']
    }, 'home');
  }
}

Pug Template Integration

import pug from 'pug';

// Compile template
const compiledFunction = pug.compileFile('./views/home.pug');

// Route handling
for await (const req of server) {
  if (req.url === '/' && req.method === 'GET') {
    const html = compiledFunction({
      title: 'Home Page',
      message: 'Welcome'
    });
    req.respond({ body: html });
  }
}

RESTful API Design and Implementation

Basic RESTful API

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];

for await (const req of server) {
  const url = new URL(req.url);

  // Get all users
  if (req.method === 'GET' && url.pathname === '/api/users') {
    req.respond({
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(users)
    });
  }

  // Create user
  else if (req.method === 'POST' && url.pathname === '/api/users') {
    const data = await req.json();
    users.push(data);
    req.respond({ status: 201 });
  }
}

Complete RESTful Implementation

const users = [];
let nextId = 1;

// Middleware: JSON request body parsing
async function jsonBodyParser(req, next) {
  if (req.headers.get('Content-Type')?.includes('application/json')) {
    req.body = await req.json();
  }
  await next();
}

// Route handling
for await (const req of server) {
  await jsonBodyParser(req, async () => {
    const url = new URL(req.url);

    // GET /api/users
    if (req.method === 'GET' && url.pathname === '/api/users') {
      req.respond({
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(users)
      });
    }

    // GET /api/users/:id
    else if (req.method === 'GET' && url.pathname.startsWith('/api/users/')) {
      const id = parseInt(url.pathname.split('/')[3]);
      const user = users.find(u => u.id === id);
      if (user) {
        req.respond({
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(user)
        });
      } else {
        req.respond({ status: 404 });
      }
    }

    // POST /api/users
    else if (req.method === 'POST' && url.pathname === '/api/users') {
      const newUser = { id: nextId++, ...req.body };
      users.push(newUser);
      req.respond({
        status: 201,
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newUser)
      });
    }

    // Other routes
    else {
      req.respond({ status: 404 });
    }
  });
}

Database Interaction

MySQL Database Connection and Queries

Basic Connection and Query

import mysql from 'mysql2/promise';

async function main() {
  // Create connection pool
  const pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'test'
  });

  // Execute query
  const [rows] = await pool.execute('SELECT * FROM users');
  console.log(rows);

  // Close connection pool
  await pool.end();
}

Transaction Processing

async function transferFunds(pool, fromId, toId, amount) {
  const conn = await pool.getConnection();
  try {
    await conn.beginTransaction();
    
    // Deduct funds
    await conn.execute(
      'UPDATE accounts SET balance = balance - ? WHERE id = ?',
      [amount, fromId]
    );
    
    // Add funds
    await conn.execute(
      'UPDATE accounts SET balance = balance + ? WHERE id = ?',
      [amount, toId]
    );
    
    await conn.commit();
  } catch (err) {
    await conn.rollback();
    throw err;
  } finally {
    conn.release();
  }
}

PostgreSQL Database Operations

Basic CRUD Operations

import { Pool } from 'pg';

const pool = new Pool({
  user: 'postgres',
  host: 'localhost',
  database: 'test',
  password: 'password',
  port: 5432,
});

// Query data
async function getUsers() {
  const { rows } = await pool.query('SELECT * FROM users');
  return rows;
}

// Insert data
async function createUser(name, email) {
  const { rows } = await pool.query(
    'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
    [name, email]
  );
  return rows[0];
}

Advanced Queries

// Transaction processing
async function transferFunds(fromId, toId, amount) {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    
    // Deduct funds
    await client.query(
      'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
      [amount, fromId]
    );
    
    // Add funds
    await client.query(
      'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
      [amount, toId]
    );
    
    await client.query('COMMIT');
  } catch (err) {
    await client.query('ROLLBACK');
    throw err;
  } finally {
    client.release();
  }
}

SQLite Database Operations

Basic Operations

import Database from 'better-sqlite3';

const db = new Database('test.db');

// Create table
db.exec(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL
  )
`);

// Insert data
const stmt = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
stmt.run('Alice', 'alice@example.com');

// Query data
const rows = db.prepare('SELECT * FROM users').all();
console.log(rows);

Transaction Processing

db.transaction(() => {
  const stmt1 = db.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?');
  const stmt2 = db.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?');

  stmt1.run(100, 1);
  stmt2.run(100, 2);
})();

Database Connection Pool and Performance Optimization

Connection Pool Configuration

// MySQL connection pool optimization configuration
const pool = mysql.createPool({
  connectionLimit: 10,          // Maximum connections
  queueLimit: 0,                // Unlimited queue
  acquireTimeout: 10000,        // Connection acquisition timeout (ms)
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'test'
});

// PostgreSQL connection pool configuration
const pool = new Pool({
  max: 20,                      // Maximum connections
  idleTimeoutMillis: 30000,     // Idle connection timeout (ms)
  connectionTimeoutMillis: 2000 // Connection timeout (ms)
});

Performance Optimization Techniques

// Bulk insert optimization
async function bulkInsertUsers(users) {
  const conn = await pool.getConnection();
  try {
    await conn.beginTransaction();

    // Use bulk insert statement
    const query = 'INSERT INTO users (name, email) VALUES ?';
    const values = users.map(user => [user.name, user.email]);
    await conn.query(query, [values]);

    await conn.commit();
  } finally {
    conn.release();
  }
}

// Query caching
const queryCache = new Map();

async function getCachedUsers() {
  const cacheKey = 'users';
  if (queryCache.has(cacheKey)) {
    return queryCache.get(cacheKey);
  }

  const users = await pool.query('SELECT * FROM users');
  queryCache.set(cacheKey, users);
  return users;
}

ORM and ODM Selection and Comparison

Prisma Basic Usage

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// Query example
async function getUsers() {
  return prisma.user.findMany({
    where: { active: true },
    select: { id: true, name: true }
  });
}

// Create example
async function createUser(data) {
  return prisma.user.create({ data });
}

TypeORM Basic Usage

import { DataSource } from 'typeorm';

const AppDataSource = new DataSource({
  type: 'mysql',
  host: 'localhost',
  port: 3306,
  username: 'root',
  password: 'password',
  database: 'test',
});

// Entity definition
@Entity()
class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}

// Query example
async function getUsers() {
  const userRepository = AppDataSource.getRepository(User);
  return userRepository.find({ where: { active: true } });
}

ORM Comparison Table

FeaturePrismaTypeORMSequelize
Type SupportFull TypeScript supportGood TypeScript supportBasic TypeScript support
Query BuilderChained APIDecorators + Query BuilderChained API
Migration ToolsBuilt-in migration systemBuilt-in migration systemBuilt-in migration system
PerformanceExcellentGoodModerate
Learning CurveLowModerateLow

Command-Line Tool Development

Command-Line Argument Parsing

Using the flags Library

import flags from '@oclif/flags';

class MyCommand {
  static flags = {
    version: flags.boolean({ char: 'v' }),
    config: flags.string({ description: 'Config file path' })
  };

  async run() {
    const { flags } = await this.parse(MyCommand);
    if (flags.version) {
      console.log('v1.0.0');
      return;
    }
    console.log('Config:', flags.config);
  }
}

Using yargs

import yargs from 'yargs/yargs';
import { hideBin } from 'yargs/helpers';

const argv = yargs(hideBin(process.argv))
  .option('input', {
    alias: 'i',
    type: 'string',
    description: 'Input file'
  })
  .option('output', {
    alias: 'o',
    type: 'string',
    description: 'Output file'
  })
  .argv;

console.log('Input:', argv.input);
console.log('Output:', argv.output);

File System Operations and Interaction

File Operation Example

import fs from 'fs/promises';
import path from 'path';

async function processFiles(dir) {
  const files = await fs.readdir(dir);

  for (const file of files) {
    const fullPath = path.join(dir, file);
    const stats = await fs.stat(fullPath);

    if (stats.isDirectory()) {
      await processFiles(fullPath);
    } else {
      const content = await fs.readFile(fullPath, 'utf8');
      console.log(`File: ${file}, Size: ${content.length} bytes`);
    }
  }
}

Directory Operations

import fs from 'fs/promises';

async function createProjectStructure(baseDir) {
  await fs.mkdir(baseDir, { recursive: true });

  const dirs = ['src', 'tests', 'docs'];
  for (const dir of dirs) {
    await fs.mkdir(path.join(baseDir, dir));
  }

  const files = [
    { path: 'package.json', content: '{"name": "project"}' },
    { path: 'src/index.js', content: '// Main file' }
  ];

  for (const file of files) {
    await fs.writeFile(
      path.join(baseDir, file.path),
      file.content
    );
  }
}

Colored Output and Progress Bar

Using chalk

import chalk from 'chalk';

console.log(chalk.blue('Hello world'));
console.log(chalk.red.bold('Error message'));
console.log(chalk.green('Success message'));
console.log(chalk.yellow.inverse('Warning'));

Using ora Progress Bar

import ora from 'ora';

const spinner = ora('Loading unicorns').start();

setTimeout(() => {
  spinner.color = 'yellow';
  spinner.text = 'Loading rainbows';
}, 1000);

setTimeout(() => {
  spinner.succeed('Done!');
}, 2000);

Scaffolding Tool Development

Creating a Generator with Yeoman

import Generator from 'yeoman-generator';

export default class MyGenerator extends Generator {
  async prompting() {
    this.answers = await this.prompt([
      {
        type: 'input',
        name: 'name',
        message: 'Your project name'
      },
      {
        type: 'confirm',
        name: 'installDeps',
        message: 'Install dependencies?'
      }
    ]);
  }

  writing() {
    // Copy template files
    this.fs.copyTpl(
      this.templatePath('package.json'),
      this.destinationPath('package.json'),
      { name: this.answers.name }
    );

    // Create directory structure
    this.fs.copyTpl(
      this.templatePath('src'),
      this.destinationPath('src')
    );
  }

  install() {
    if (this.answers.installDeps) {
      this.installDependencies({
        npm: true,
        bower: false,
        yarn: false
      });
    }
  }
}

Using a Local Generator

import { execSync } from 'child_process';

// Link to local generator
execSync('npm link', { cwd: '/path/to/generator' });

// Run generator
execSync('yo my-generator', { stdio: 'inherit' });

Global vs. Local Installation

Installation Method Comparison

FeatureGlobal InstallationLocal Installation
Install Commandnpm install -gnpm install
Executable LocationSystem PATH directoryProject node_modules/.bin
Usage MethodDirect CLI invocationVia npx or package.json scripts
Version ManagementSingle global versionProject-specific versions
Typical Use CaseCLI toolsProject dependencies

Practical Usage Example

// package.json
{
  "scripts": {
    "lint": "eslint .",       // Locally installed ESLint
    "build": "webpack"        // Locally installed Webpack
  }
}

// Global tool usage example
$ create-react-app my-app    // Globally installed create-react-app
$ my-cli-tool                // Globally installed custom CLI

Best Practice Recommendations

  1. Development Dependencies: Always install project-specific tools locally.
  2. System Tools: Globally install commonly used CLI tools (e.g., git-lfs).
  3. Version Control: Use nvm to manage Node.js versions.
  4. Cross-Platform: Define scripts in package.json instead of relying on global commands.
  5. Portability: Ensure the project includes all necessary local dependencies.
Share your love