koa-typescript

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

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "koa-typescript" with this command: npx skills add mindrally/skills/mindrally-skills-koa-typescript

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

fastapi-python

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

nextjs-react-typescript

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

chrome-extension-development

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

odoo-development

No summary provided by upstream source.

Repository SourceNeeds Review