koa-typescript について
Koa.js と TypeScript で API を開発する人向けのコーディングスキルです。ミドルウェアの設計、型安全、検証、セキュリティ、テストの指針を扱います。
主な機能
- 01Koa の onion ミドルウェアパターン
- 02TypeScript の型安全ガイダンス
- 03ルーティングとプロジェクト構成
- 04エラー処理とリクエスト検証
- 05認証、セキュリティ、テスト
活用シーン
- 01Koa API のプロジェクト構成の設計
- 02非同期ミドルウェアとルートの実装
- 03エンドポイントへの検証と認証の追加
- 04テストとセキュリティ対策の計画
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mindrally/ skills --skill koa-typescriptインストール後、Claude Code で/koa-typescriptを入力して読み込まれたか確認します。
詳細
Koa TypeScript 開発
Koa.js と TypeScript 開発のエキスパートとして、Koa 独自のオニオンモデルを使った洗練されたミドルウェアベースの API 構築に関する深い知識を備えています。
TypeScript の一般的なガイドライン
基本原則
- コードとドキュメントにはすべて英語を使用します
- 変数と関数には必ず型を宣言します
any型の使用を避け、必要な型を定義します- 公開クラスとメソッドのドキュメントには JSDoc を使用します
- 簡潔で保守しやすく、技術的に正確なコードを書きます
- 関数型および宣言型のプログラミングパターンを使用します
- DRY の原則に従い、反復とモジュール化を優先します
命名規則
- 型とインターフェースには PascalCase を使用します
- 変数、関数、メソッドには camelCase を使用します
- ファイル名とディレクトリ名には kebab-case を使用します
- 環境変数には UPPERCASE を使用します
- 助動詞を含む、内容が明確な変数名を使用します
関数
- 関数は短くし、単一の目的を持たせます
- ミドルウェアにはアロー関数を使用します
- コードベース全体で一貫して async/await を使用します
- 複数のパラメーターには RO-RO パターンを使用します
Koa 固有のガイドライン
プロジェクト構成
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
ミドルウェアのパターン
Koa は独自の「オニオン」ミドルウェアモデルを使用します。ミドルウェアはスタックのように合成され、実行されます。
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`);
};
- ミドルウェアには必ず
async/awaitを使用します await next()を呼び出して、後続のミドルウェアに制御を渡しますawait next()の後のコードは「上流」フェーズで実行されます- リクエスト/レスポンスの変換にはこのパターンを使用します
コンテキスト(ctx)のベストプラクティス
- ミドルウェア間でデータを渡すには
ctx.stateを使用します - 型安全性を高めるため、コンテキストに型を付けます
- 可能な場合は、コンテキストを直接変更しないようにします
- リクエスト/レスポンスにアクセスするにはコンテキストを使用します
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();
};
アプリケーションのセットアップ
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;
koa-router によるルーティング
- 宣言的なルーティングには koa-router を使用します
- リソースごとにルートを整理します
- ルートハンドラーはシンプルに保ちます
- 横断的関心事にはミドルウェアを使用します
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;
エラー処理
- 一元的なエラー処理ミドルウェアを作成します
- ミドルウェアスタックの最上位にエラーハンドラーを配置します
- エラーの種類ごとにカスタムエラークラスを使用します
- 本番環境では内部エラーの詳細を決して公開しません
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);
}
};
リクエストの検証
- 検証には koa-joi-router または Zod を使用します
- body、query、params を検証します
- 明確な検証エラーメッセージを返します
- 再利用可能な検証ミドルウェアを作成します
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;
}
};
};
認証
- koa-jwt を使用して JWT 認証を実装します
- 認証済みユーザーを
ctx.state.userに保存します - ロールベースのアクセス制御には認可ミドルウェアを作成します
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();
};
};
セキュリティ
- セキュリティヘッダーには koa-helmet を使用します
- koa-ratelimit でレート制限を実装します
- @koa/cors で CORS を有効にします
- すべての入力を検証してサニタイズします
- 本番環境では HTTPS を使用します
テスト
- テストには Jest または Mocha を使用します
- 統合テストでは app.callback() と supertest を使用します
- ミドルウェアを個別にテストします
- 単体テストではコンテキストをモックします
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);
});
});
パフォーマンス
- レスポンスの圧縮には koa-compress を使用します
- koa-redis-cache でキャッシュを実装します
- データベースにはコネクションプーリングを使用します
- リストエンドポイントにはページネーションを実装します
- 静的ファイルの配信には koa-static の使用を検討します
環境設定
- 環境変数には dotenv を使用します
- 起動時に必須の環境変数を検証します
- 環境ごとに個別の設定を作成します
- シークレットをバージョン管理にコミットしないでください
コメント
Markdown 対応