Koa Beispiele

Koa Node.js Middleware-Framework-Beispiele einschließlich Middleware-Chaining, Context-Handling, async/await-Patterns und Ökosystem-Integration

💻 Hallo Welt Koa javascript

🟢 simple

Grundlegende Koa Server-Einrichtung mit Middleware, Routing, Async-Handlern und modernen JavaScript-Patterns

// Koa Hello World Examples

// 1. Basic Koa Server
const Koa = require('koa');
const app = new Koa();

// Middleware function
app.use(async ctx => {
  ctx.body = 'Hello Koa!';
});

app.listen(3000);
console.log('Server running on http://localhost:3000');

// 2. Koa with Multiple Middleware
const Koa = require('koa');
const app = new Koa();

// Logger middleware
app.use(async (ctx, next) => {
  console.log(`${new Date().toISOString()} ${ctx.method} ${ctx.url}`);
  await next();
  console.log(`Response status: ${ctx.status}`);
});

// Error handling middleware
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = { error: err.message };
    ctx.app.emit('error', err, ctx);
  }
});

// Response time middleware
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});

// Route handler
app.use(async ctx => {
  ctx.body = {
    message: 'Hello from Koa with middleware!',
    method: ctx.method,
    url: ctx.url,
    headers: ctx.headers
  };
});

app.listen(3000);
console.log('Server with middleware running on http://localhost:3000');

// 3. Koa Router with REST API
const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('koa-bodyparser');
const app = new Koa();
const router = new Router();

// Middleware
app.use(bodyParser());

// Mock data store
let users = [
  { id: 1, name: 'John Doe', email: '[email protected]', age: 30 },
  { id: 2, name: 'Jane Smith', email: '[email protected]', age: 25 }
];

// Routes
router.get('/api/users', async (ctx) => {
  ctx.body = { users, total: users.length };
});

router.get('/api/users/:id', async (ctx) => {
  const id = parseInt(ctx.params.id);
  const user = users.find(u => u.id === id);

  if (!user) {
    ctx.status = 404;
    ctx.body = { error: 'User not found' };
    return;
  }

  ctx.body = user;
});

router.post('/api/users', async (ctx) => {
  const { name, email, age } = ctx.request.body;

  if (!name || !email) {
    ctx.status = 400;
    ctx.body = { error: 'Name and email are required' };
    return;
  }

  const newUser = {
    id: users.length + 1,
    name,
    email,
    age: age || null
  };

  users.push(newUser);
  ctx.status = 201;
  ctx.body = newUser;
});

router.put('/api/users/:id', async (ctx) => {
  const id = parseInt(ctx.params.id);
  const userIndex = users.findIndex(u => u.id === id);

  if (userIndex === -1) {
    ctx.status = 404;
    ctx.body = { error: 'User not found' };
    return;
  }

  users[userIndex] = { ...users[userIndex], ...ctx.request.body };
  ctx.body = users[userIndex];
});

router.delete('/api/users/:id', async (ctx) => {
  const id = parseInt(ctx.params.id);
  const userIndex = users.findIndex(u => u.id === id);

  if (userIndex === -1) {
    ctx.status = 404;
    ctx.body = { error: 'User not found' };
    return;
  }

  users.splice(userIndex, 1);
  ctx.status = 204;
});

// Health check
router.get('/health', async (ctx) => {
  ctx.body = {
    status: 'ok',
    timestamp: new Date().toISOString(),
    uptime: process.uptime()
  };
});

// Mount routes
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);
console.log('REST API server running on http://localhost:3000');

// 4. Koa with Custom Middleware
const Koa = require('koa');
const app = new Koa();

// Custom authentication middleware
const authMiddleware = (options = {}) => {
  return async (ctx, next) => {
    // Skip authentication for certain paths
    if (ctx.path.startsWith('/public') || ctx.path === '/') {
      await next();
      return;
    }

    const token = ctx.headers.authorization?.replace('Bearer ', '');

    if (!token) {
      ctx.status = 401;
      ctx.body = { error: 'No token provided' };
      return;
    }

    // Mock authentication (use proper JWT validation in production)
    if (token !== 'valid-token') {
      ctx.status = 401;
      ctx.body = { error: 'Invalid token' };
      return;
    }

    // Set user in context
    ctx.state.user = { id: 1, name: 'John Doe', role: 'user' };
    await next();
  };
};

// Custom CORS middleware
const corsMiddleware = (options = {}) => {
  const defaults = {
    origin: '*',
    allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    allowHeaders: ['Content-Type', 'Authorization']
  };
  const config = { ...defaults, ...options };

  return async (ctx, next) => {
    ctx.set('Access-Control-Allow-Origin', config.origin);
    ctx.set('Access-Control-Allow-Methods', config.allowMethods.join(', '));
    ctx.set('Access-Control-Allow-Headers', config.allowHeaders.join(', '));

    if (ctx.method === 'OPTIONS') {
      ctx.status = 204;
      return;
    }

    await next();
  };
};

// Custom rate limiting middleware
const rateLimitMiddleware = (options = {}) => {
  const defaults = {
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // limit each IP to 100 requests per windowMs
    message: 'Too many requests, please try again later'
  };
  const config = { ...defaults, ...options };

  const requests = new Map();

  return async (ctx, next) => {
    const ip = ctx.ip;
    const now = Date.now();

    if (!requests.has(ip)) {
      requests.set(ip, { count: 0, resetTime: now + config.windowMs });
    }

    const requestData = requests.get(ip);

    if (now > requestData.resetTime) {
      requestData.count = 0;
      requestData.resetTime = now + config.windowMs;
    }

    requestData.count++;

    if (requestData.count > config.max) {
      ctx.status = 429;
      ctx.body = { error: config.message };
      return;
    }

    await next();
  };
};

// Apply middleware
app.use(corsMiddleware({ origin: 'http://localhost:3000' }));
app.use(rateLimitMiddleware({ max: 10, windowMs: 60000 })); // 10 requests per minute
app.use(authMiddleware());

// Routes
app.use(async (ctx) => {
  ctx.body = {
    message: 'Protected content',
    user: ctx.state.user,
    path: ctx.path,
    method: ctx.method
  };
});

// Public route
app.use(async (ctx, next) => {
  if (ctx.path === '/public/info') {
    ctx.body = { message: 'This is public information' };
    return;
  }
  await next();
});

app.listen(3000);
console.log('Server with custom middleware running on http://localhost:3000');

// 5. Koa with Error Handling and Validation
const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('koa-bodyparser');
const { Joi } = require('koa-joi-router');
const app = new Koa();
const router = new Router();

// Custom error class
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
  }
}

// Validation schema
const userSchema = Joi.object({
  name: Joi.string().min(2).max(50).required(),
  email: Joi.string().email().required(),
  age: Joi.number().integer().min(0).max(150).optional()
});

// Error handling middleware
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    console.error('Error:', err);

    if (err.isJoi) {
      // Validation error
      ctx.status = 400;
      ctx.body = {
        error: 'Validation Error',
        details: err.details.map(detail => ({
          field: detail.path.join('.'),
          message: detail.message
        }))
      };
    } else if (err.isOperational) {
      // Operational error
      ctx.status = err.statusCode || 500;
      ctx.body = { error: err.message };
    } else {
      // Programming error
      ctx.status = 500;
      ctx.body = { error: 'Internal Server Error' };
    }
  }
});

// Request validation middleware
const validate = (schema) => {
  return async (ctx, next) => {
    const { error, value } = schema.validate(ctx.request.body);
    if (error) {
      error.isJoi = true;
      throw error;
    }
    ctx.request.validatedBody = value;
    await next();
  };
};

// Middleware
app.use(bodyParser());

// Routes with validation
router.post('/api/users', validate(userSchema), async (ctx) => {
  const userData = ctx.request.validatedBody;

  // Simulate database operation
  const newUser = {
    id: Date.now(),
    ...userData,
    createdAt: new Date().toISOString()
  };

  ctx.status = 201;
  ctx.body = {
    message: 'User created successfully',
    user: newUser
  };
});

// Route that throws operational error
router.get('/api/error', async (ctx) => {
  throw new AppError('This is an operational error', 400);
});

// Route that throws programming error
router.get('/api/crash', async (ctx) => {
  throw new Error('Unexpected error occurred');
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);
console.log('Server with error handling running on http://localhost:3000');

// 6. Koa with File Uploads and Static Files
const Koa = require('koa');
const Router = require('@koa/router');
const serve = require('koa-static');
const mount = require('koa-mount');
const formidable = require('koa-formidable');
const path = require('path');
const fs = require('fs').promises;
const app = new Koa();
const router = new Router();

// Serve static files
app.use(mount('/public', serve(path.join(__dirname, 'public'))));

// Ensure upload directory exists
const uploadDir = path.join(__dirname, 'uploads');
fs.mkdir(uploadDir, { recursive: true }).catch(console.error);

// File upload middleware
const uploadMiddleware = formidable({
  uploadDir,
  keepExtensions: true,
  maxFileSize: 10 * 1024 * 1024, // 10MB
  multiples: true
});

// Single file upload
router.post('/upload', uploadMiddleware, async (ctx) => {
  try {
    const files = ctx.request.files;
    const file = files.file; // 'file' is the field name

    if (!file) {
      ctx.status = 400;
      ctx.body = { error: 'No file uploaded' };
      return;
    }

    ctx.body = {
      message: 'File uploaded successfully',
      file: {
        name: file.name,
        path: file.path,
        size: file.size,
        type: file.type
      }
    };
  } catch (error) {
    console.error('Upload error:', error);
    ctx.status = 500;
    ctx.body = { error: 'File upload failed' };
  }
});

// Multiple files upload
router.post('/upload/multiple', uploadMiddleware, async (ctx) => {
  try {
    const files = ctx.request.files;
    const uploadedFiles = [];

    for (const [fieldName, fileArray] of Object.entries(files)) {
      if (Array.isArray(fileArray)) {
        fileArray.forEach(file => {
          uploadedFiles.push({
            field: fieldName,
            name: file.name,
            path: file.path,
            size: file.size,
            type: file.type
          });
        });
      } else {
        uploadedFiles.push({
          field: fieldName,
          name: fileArray.name,
          path: fileArray.path,
          size: fileArray.size,
          type: fileArray.type
        });
      }
    }

    ctx.body = {
      message: 'Files uploaded successfully',
      files: uploadedFiles,
      count: uploadedFiles.length
    };
  } catch (error) {
    console.error('Multiple upload error:', error);
    ctx.status = 500;
    ctx.body = { error: 'Multiple file upload failed' };
  }
});

// File download
router.get('/download/:filename', async (ctx) => {
  try {
    const filename = ctx.params.filename;
    const filePath = path.join(uploadDir, filename);

    const stats = await fs.stat(filePath);

    ctx.set('Content-Disposition', `attachment; filename="${filename}"`);
    ctx.set('Content-Type', 'application/octet-stream');
    ctx.body = await fs.readFile(filePath);
  } catch (error) {
    ctx.status = 404;
    ctx.body = { error: 'File not found' };
  }
});

// List uploaded files
router.get('/files', async (ctx) => {
  try {
    const files = await fs.readdir(uploadDir);
    const fileInfos = [];

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

      fileInfos.push({
        name: file,
        size: stats.size,
        createdAt: stats.birthtime.toISOString(),
        downloadUrl: `/download/${file}`
      });
    }

    ctx.body = {
      files: fileInfos,
      count: fileInfos.length
    };
  } catch (error) {
    ctx.status = 500;
    ctx.body = { error: 'Failed to list files' };
  }
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);
console.log('File server running on http://localhost:3000');

// 7. Koa with Session Management
const Koa = require('koa');
const Router = require('@koa/router');
const session = require('koa-session');
const bodyParser = require('koa-bodyparser');
const app = new Koa();
const router = new Router();

// Session configuration
const sessionConfig = {
  key: 'koa.sess', // cookie key
  maxAge: 86400000, // 24 hours
  autoCommit: true,
  overwrite: true,
  httpOnly: true,
  signed: true,
  rolling: false,
  renew: false,
  secure: false, // set to true in production with HTTPS
  sameSite: null
};

app.keys = ['your-secret-key'];

// Middleware
app.use(session(sessionConfig, app));
app.use(bodyParser());

// Authentication routes
router.post('/login', async (ctx) => {
  const { username, password } = ctx.request.body;

  // Mock authentication (use proper password hashing in production)
  if (username === 'admin' && password === 'password') {
    ctx.session.user = {
      id: 1,
      username: 'admin',
      role: 'admin'
    };

    ctx.body = {
      message: 'Login successful',
      user: ctx.session.user
    };
  } else {
    ctx.status = 401;
    ctx.body = { error: 'Invalid credentials' };
  }
});

router.post('/logout', async (ctx) => {
  ctx.session = null;
  ctx.body = { message: 'Logged out successfully' };
});

router.get('/profile', async (ctx) => {
  if (!ctx.session.user) {
    ctx.status = 401;
    ctx.body = { error: 'Not authenticated' };
    return;
  }

  ctx.body = {
    user: ctx.session.user,
    sessionId: ctx.sessionId,
    visits: (ctx.session.visits || 0) + 1
  };

  ctx.session.visits = (ctx.session.visits || 0) + 1;
});

// Shopping cart example
router.get('/cart', async (ctx) => {
  const cart = ctx.session.cart || [];
  ctx.body = { cart };
});

router.post('/cart/add', async (ctx) => {
  const { productId, name, price } = ctx.request.body;

  if (!ctx.session.cart) {
    ctx.session.cart = [];
  }

  const existingItem = ctx.session.cart.find(item => item.productId === productId);

  if (existingItem) {
    existingItem.quantity += 1;
  } else {
    ctx.session.cart.push({
      productId,
      name,
      price: parseFloat(price),
      quantity: 1
    });
  }

  ctx.body = {
    message: 'Item added to cart',
    cart: ctx.session.cart
  };
});

router.delete('/cart/:productId', async (ctx) => {
  const productId = parseInt(ctx.params.productId);

  if (ctx.session.cart) {
    ctx.session.cart = ctx.session.cart.filter(item => item.productId !== productId);
  }

  ctx.body = { message: 'Item removed from cart' };
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);
console.log('Session-enabled server running on http://localhost:3000');

// 8. Koa with Database Integration (MongoDB)
const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('koa-bodyparser');
const mongoose = require('mongoose');
const app = new Koa();
const router = new Router();

// MongoDB connection
mongoose.connect('mongodb://localhost:27017/koa-app', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

// User model
const UserSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  age: { type: Number, min: 0, max: 150 },
  createdAt: { type: Date, default: Date.now }
});

const User = mongoose.model('User', UserSchema);

// Middleware
app.use(bodyParser());
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = { error: err.message };
  }
});

// CRUD routes
router.post('/api/users', async (ctx) => {
  try {
    const user = new User(ctx.request.body);
    await user.save();
    ctx.status = 201;
    ctx.body = user;
  } catch (err) {
    ctx.status = 400;
    ctx.body = { error: err.message };
  }
});

router.get('/api/users', async (ctx) => {
  const users = await User.find();
  ctx.body = { users, total: users.length };
});

router.get('/api/users/:id', async (ctx) => {
  try {
    const user = await User.findById(ctx.params.id);
    if (!user) {
      ctx.status = 404;
      ctx.body = { error: 'User not found' };
      return;
    }
    ctx.body = user;
  } catch (err) {
    ctx.status = 400;
    ctx.body = { error: 'Invalid user ID' };
  }
});

router.put('/api/users/:id', async (ctx) => {
  try {
    const user = await User.findByIdAndUpdate(
      ctx.params.id,
      ctx.request.body,
      { new: true, runValidators: true }
    );
    if (!user) {
      ctx.status = 404;
      ctx.body = { error: 'User not found' };
      return;
    }
    ctx.body = user;
  } catch (err) {
    ctx.status = 400;
    ctx.body = { error: err.message };
  }
});

router.delete('/api/users/:id', async (ctx) => {
  try {
    const user = await User.findByIdAndDelete(ctx.params.id);
    if (!user) {
      ctx.status = 404;
      ctx.body = { error: 'User not found' };
      return;
    }
    ctx.status = 204;
  } catch (err) {
    ctx.status = 400;
    ctx.body = { error: 'Invalid user ID' };
  }
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);
console.log('Database-enabled server running on http://localhost:3000');

// 9. Koa with WebSocket Support
const Koa = require('koa');
const Router = require('@koa/router');
const WebSocket = require('ws');
const http = require('http');
const app = new Koa();
const router = new Router();
const server = http.createServer(app.callback());

// WebSocket server
const wss = new WebSocket.Server({ server });

// Store connected clients
const clients = new Set();

wss.on('connection', (ws) => {
  clients.add(ws);
  console.log('Client connected, total clients:', clients.size);

  // Send welcome message
  ws.send(JSON.stringify({
    type: 'welcome',
    message: 'Connected to chat server',
    timestamp: new Date().toISOString()
  }));

  // Handle incoming messages
  ws.on('message', (data) => {
    try {
      const message = JSON.parse(data);

      // Broadcast message to all clients
      const broadcast = {
        type: 'message',
        data: message,
        timestamp: new Date().toISOString()
      };

      clients.forEach(client => {
        if (client.readyState === WebSocket.OPEN) {
          client.send(JSON.stringify(broadcast));
        }
      });
    } catch (error) {
      console.error('Invalid message format:', error);
    }
  });

  // Handle client disconnection
  ws.on('close', () => {
    clients.delete(ws);
    console.log('Client disconnected, total clients:', clients.size);

    // Notify other clients
    const notification = {
      type: 'notification',
      message: 'A user left the chat',
      userCount: clients.size,
      timestamp: new Date().toISOString()
    };

    clients.forEach(client => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify(notification));
      }
    });
  });

  ws.on('error', (error) => {
    console.error('WebSocket error:', error);
    clients.delete(ws);
  });
});

// HTTP routes
router.get('/', async (ctx) => {
  ctx.body = {
    message: 'Koa WebSocket Server',
    connectedClients: clients.size,
    endpoints: [
      'GET / - Server info',
      'GET /clients - Connected clients count',
      'WebSocket ws://localhost:3000 - WebSocket endpoint'
    ]
  };
});

router.get('/clients', async (ctx) => {
  ctx.body = {
    connectedClients: clients.size,
    timestamp: new Date().toISOString()
  };
});

// Broadcast API
router.post('/broadcast', async (ctx) => {
  const { message, type = 'notification' } = ctx.request.body;

  const broadcast = {
    type,
    message,
    timestamp: new Date().toISOString()
  };

  clients.forEach(client => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(JSON.stringify(broadcast));
    }
  });

  ctx.body = {
    message: 'Broadcast sent to all clients',
    clientCount: clients.size
  };
});

app.use(router.routes());
app.use(router.allowedMethods());

// Start server
server.listen(3000, () => {
  console.log('WebSocket server running on http://localhost:3000');
  console.log('Connect with WebSocket client to ws://localhost:3000');
});

// 10. Koa Production Best Practices
const Koa = require('koa');
const Router = require('@koa/router');
const helmet = require('koa-helmet');
const compress = require('koa-compress');
const cors = require('@koa/cors');
const bodyParser = require('koa-bodyparser');
const rateLimit = require('koa-ratelimit');
const app = new Koa();
const router = new Router();

// Security middleware
app.use(helmet());

// Compression middleware
app.use(compress({
  filter: (contentType) => {
    return /text|javascript|json/i.test(contentType);
  },
  threshold: 1024,
  gzip: {
    flush: require('zlib').constants.Z_SYNC_FLUSH
  },
  deflate: {
    flush: require('zlib').constants.Z_SYNC_FLUSH
  },
  br: false // disable brotli
}));

// CORS middleware
app.use(cors({
  origin: process.env.CORS_ORIGIN || '*',
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
  credentials: true
}));

// Rate limiting
app.use(rateLimit({
  driver: 'memory',
  db: new Map(),
  duration: 60000, // 1 minute
  max: 100, // 100 requests per minute
  id: (ctx) => ctx.ip,
  disableHeader: false,
  whitelist: (ctx) => {
    // Whitelist local development
    return ctx.ip === '127.0.0.1' || ctx.ip === '::1';
  },
  blacklist: (ctx) => {
    // Example: block specific IPs
    return false;
  }
}));

// Body parser with security options
app.use(bodyParser({
  enableTypes: ['json', 'form'],
  jsonLimit: '10mb',
  formLimit: '10mb',
  textLimit: '10mb',
  strict: true, // only accept objects and arrays
  detectJSON: (ctx) => {
    return ctx.request.is('json');
  }
}));

// Request logging
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;

  console.log(`${new Date().toISOString()} ${ctx.method} ${ctx.url} - ${ctx.status} - ${ms}ms`);
});

// Health check endpoint
router.get('/health', async (ctx) => {
  ctx.body = {
    status: 'ok',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    environment: process.env.NODE_ENV || 'development'
  };
});

// API versioning
router.get('/api/v1/info', async (ctx) => {
  ctx.body = {
    name: 'Koa Production API',
    version: '1.0.0',
    environment: process.env.NODE_ENV || 'development',
    nodeVersion: process.version,
    uptime: process.uptime()
  };
});

// Graceful shutdown
const gracefulShutdown = (signal) => {
  console.log(`
Received ${signal}. Starting graceful shutdown...`);

  server.close(() => {
    console.log('HTTP server closed');
    process.exit(0);
  });

  // Force close after 10 seconds
  setTimeout(() => {
    console.error('Could not close connections in time, forcefully shutting down');
    process.exit(1);
  }, 10000);
};

// Handle shutdown signals
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

// Handle uncaught exceptions
process.on('uncaughtException', (err) => {
  console.error('Uncaught Exception:', err);
  gracefulShutdown('uncaughtException');
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

app.use(router.routes());
app.use(router.allowedMethods());

const server = app.listen(3000, () => {
  console.log('🚀 Production-ready Koa server running on http://localhost:3000');
});