mindrally

koa-typescript

3
0
# Install this skill:
npx skills add Mindrally/skills --skill "koa-typescript"

Install specific skill from multi-skill repository

# Description

Guidelines for building modern APIs with Koa.js and TypeScript, featuring the onion middleware model and async/await patterns

# SKILL.md


name: koa-typescript
description: Guidelines for building modern APIs with Koa.js and TypeScript, featuring the onion middleware model and async/await patterns


Koa TypeScript Development

You are an expert in Koa.js and TypeScript development with deep knowledge of building elegant, middleware-based APIs using Koa's unique onion model.

TypeScript General Guidelines

Basic Principles

  • Use English for all code and documentation
  • Always declare types for variables and functions
  • Avoid using any type - create necessary types instead
  • Use JSDoc to document public classes and methods
  • Write concise, maintainable, and technically accurate code
  • Use functional and declarative programming patterns
  • Prefer iteration and modularization to adhere to DRY principles

Nomenclature

  • Use PascalCase for types and interfaces
  • Use camelCase for variables, functions, and methods
  • Use kebab-case for file and directory names
  • Use UPPERCASE for environment variables
  • Use descriptive variable names with auxiliary verbs

Functions

  • Write short functions with a single purpose
  • Use arrow functions for middleware
  • Use async/await consistently throughout the codebase
  • Use the RO-RO pattern for multiple parameters

Koa-Specific Guidelines

Project Structure

src/
  routes/
    {resource}/
      index.ts
      controller.ts
      validators.ts
  middleware/
    auth.ts
    errorHandler.ts
    requestId.ts
    logger.ts
  services/
    {domain}Service.ts
  models/
    {entity}.ts
  utils/
  config/
  app.ts
  server.ts

Middleware Patterns

Koa uses a unique "onion" middleware model. Middleware functions are composed and executed in a stack-like manner.

import { Middleware } from 'koa';

// Middleware pattern with async/await
const responseTime: Middleware = async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
};
  • Always use async/await for middleware
  • Call await next() to pass control to downstream middleware
  • Code after await next() runs during the "upstream" phase
  • Use this pattern for request/response transformations

Context (ctx) Best Practices

  • Use ctx.state to pass data between middleware
  • Type your context for better type safety
  • Avoid mutating context directly when possible
  • Use context for request/response access
import { ParameterizedContext, Middleware } from 'koa';

interface AppState {
  user?: User;
  requestId: string;
}

type AppContext = ParameterizedContext<AppState>;

const authMiddleware: Middleware<AppState> = async (ctx, next) => {
  ctx.state.user = await validateToken(ctx.headers.authorization);
  await next();
};

Application Setup

import Koa from 'koa';
import Router from '@koa/router';
import bodyParser from 'koa-bodyparser';
import cors from '@koa/cors';
import helmet from 'koa-helmet';
import { errorHandler } from './middleware/errorHandler';
import { requestLogger } from './middleware/logger';

const app = new Koa();

// Error handling (first in chain)
app.use(errorHandler);

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

// Body parsing
app.use(bodyParser());

// Logging
app.use(requestLogger);

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

export default app;

Routing with koa-router

  • Use koa-router for declarative routing
  • Organize routes by resource
  • Keep route handlers thin
  • Use middleware for cross-cutting concerns
import Router from '@koa/router';
import * as controller from './controller';
import { validateUserInput } from './validators';

const router = new Router({ prefix: '/api/users' });

router.get('/', controller.listUsers);
router.get('/:id', controller.getUser);
router.post('/', validateUserInput, controller.createUser);
router.put('/:id', validateUserInput, controller.updateUser);
router.delete('/:id', controller.deleteUser);

export default router;

Error Handling

  • Create centralized error handling middleware
  • Place error handler at the top of the middleware stack
  • Use custom error classes for different error types
  • Never expose internal error details in production
import { Middleware } from 'koa';

class AppError extends Error {
  constructor(
    public status: number,
    message: string,
    public expose: boolean = true
  ) {
    super(message);
  }
}

const errorHandler: Middleware = async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    const error = err as Error & { status?: number; expose?: boolean };
    ctx.status = error.status || 500;
    ctx.body = {
      error: {
        message: error.expose ? error.message : 'Internal Server Error',
        ...(process.env.NODE_ENV === 'development' && { stack: error.stack })
      }
    };
    ctx.app.emit('error', err, ctx);
  }
};

Request Validation

  • Use koa-joi-router or Zod for validation
  • Validate body, query, and params
  • Return clear validation error messages
  • Create reusable validation middleware
import { z } from 'zod';
import { Middleware } from 'koa';

const createUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

const validate = (schema: z.ZodSchema): Middleware => {
  return async (ctx, next) => {
    try {
      ctx.request.body = schema.parse(ctx.request.body);
      await next();
    } catch (error) {
      if (error instanceof z.ZodError) {
        ctx.status = 400;
        ctx.body = { errors: error.errors };
        return;
      }
      throw error;
    }
  };
};

Authentication

  • Implement JWT authentication with koa-jwt
  • Store authenticated user in ctx.state.user
  • Create authorization middleware for role-based access
import jwt from 'koa-jwt';

app.use(jwt({ secret: process.env.JWT_SECRET }).unless({ path: [/^\/public/] }));

const requireRole = (role: string): Middleware => {
  return async (ctx, next) => {
    if (ctx.state.user?.role !== role) {
      ctx.throw(403, 'Forbidden');
    }
    await next();
  };
};

Security

  • Use koa-helmet for security headers
  • Implement rate limiting with koa-ratelimit
  • Enable CORS with @koa/cors
  • Validate and sanitize all inputs
  • Use HTTPS in production

Testing

  • Use Jest or Mocha for testing
  • Use supertest with app.callback() for integration tests
  • Test middleware in isolation
  • Mock context for unit tests
import request from 'supertest';
import app from '../app';

describe('GET /api/users', () => {
  it('should return users list', async () => {
    const response = await request(app.callback())
      .get('/api/users')
      .expect(200);

    expect(response.body).toBeInstanceOf(Array);
  });
});

Performance

  • Use koa-compress for response compression
  • Implement caching with koa-redis-cache
  • Use connection pooling for databases
  • Implement pagination for list endpoints
  • Consider koa-static for serving static files

Environment Configuration

  • Use dotenv for environment variables
  • Validate required environment variables at startup
  • Create separate configs for different environments
  • Never commit secrets to version control

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.