mcpField
B
backend-patterns

backend-patterns

github.com/kimny1143Development65 installs
Repository

About backend-patterns

backend-patterns is a collection of backend conventions and patterns for developers building API routes. It covers authentication, response formats, Zod validation, repositories, and error handling.

Key features

  1. 01Authenticated API route patterns
  2. 02Consistent success and error responses
  3. 03Zod request validation patterns
  4. 04Repository pattern examples
  5. 05Error handling and review checklist

Use cases

  1. 01Building a new authenticated API route
  2. 02Standardizing request validation and responses
  3. 03Checking route authorization and error handling

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/kimny1143/claude-code-template --skill backend-patterns

After installing, type/backend-patternsin Claude Code to confirm it loaded.

Details

From the project README

backend-patterns - バックエンドパターン集

バックエンドコード規約とパターン。

使い方

新しいAPIルートを作る時、このスキルを参照

1. APIルート構造

認証パターン(Next.js例)
// 認証ミドルウェアを使用
import { withAuth } from '@/lib/middleware/with-auth';
import { apiSuccess, apiError } from '@/lib/api-response';

export const GET = withAuth(async ({ userId, request }) => {
  const data = await repository.findByUserId(userId);
  return apiSuccess({ data });
});

export const POST = withAuth(async ({ userId, request }) => {
  const body = await request.json();
  // バリデーション & 処理
  return apiSuccess({ created: result });
});
Express.js例
import { Router } from 'express';
import { authenticate } from '@/middleware/auth';
import { validate } from '@/middleware/validate';

const router = Router();

router.get('/', authenticate, async (req, res) => {
  const data = await repository.findByUserId(req.user.id);
  res.json({ success: true, data });
});

router.post('/', authenticate, validate(createSchema), async (req, res) => {
  const result = await repository.create(req.body);
  res.status(201).json({ success: true, data: result });
});

2. レスポンス形式

統一レスポンス
// lib/api-response.ts
export function apiSuccess<T>(data: T, options?: { status?: number }) {
  return Response.json(
    { success: true, data, timestamp: new Date().toISOString() },
    { status: options?.status ?? 200 }
  );
}

export function apiError(message: string, options?: { status?: number; code?: string }) {
  return Response.json(
    { success: false, error: message, code: options?.code, timestamp: new Date().toISOString() },
    { status: options?.status ?? 400 }
  );
}
レスポンス形式
// 成功
{
  "success": true,
  "data": { ... },
  "timestamp": "2026-01-19T12:00:00.000Z"
}

// エラー
{
  "success": false,
  "error": "Error message",
  "code": "ERROR_CODE",
  "timestamp": "2026-01-19T12:00:00.000Z"
}

3. バリデーション

Zodスキーマ
import { z } from 'zod';

const createUserSchema = z.object({
  email: z.string().email('Invalid email'),
  name: z.string().min(1, 'Name is required').max(100),
  role: z.enum(['user', 'admin']).optional(),
});

// APIルートで使用
const validation = createUserSchema.safeParse(body);
if (!validation.success) {
  return apiError('Invalid input', { status: 400 });
}
const { email, name, role } = validation.data;
共通スキーマ
// UUID
const idSchema = z.string().uuid('Invalid ID format');

// ページネーション
const paginationSchema = z.object({
  page: z.coerce.number().min(1).default(1),
  limit: z.coerce.number().min(1).max(100).default(20),
});

4. Repository パターン

基本構造
// lib/repositories/user.repository.ts
import { db } from '@/db';
import { users } from '@/db/schema';
import { eq } from 'drizzle-orm';

export interface CreateUserInput {
  email: string;
  name: string;
}

export const userRepository = {
  async create(input: CreateUserInput) {
    const [result] = await db.insert(users).values(input).returning();
    return result;
  },

  async findById(id: string) {
    const [result] = await db
      .select()
      .from(users)
      .where(eq(users.id, id))
      .limit(1);
    return result ?? null;
  },

  async findByEmail(email: string) {
    const [result] = await db
      .select()
      .from(users)
      .where(eq(users.email, email))
      .limit(1);
    return result ?? null;
  },

  async update(id: string, input: Partial<CreateUserInput>) {
    const [result] = await db
      .update(users)
      .set({ ...input, updatedAt: new Date() })
      .where(eq(users.id, id))
      .returning();
    return result;
  },

  async delete(id: string) {
    await db.delete(users).where(eq(users.id, id));
  },
};

5. エラーハンドリング

export const POST = withAuth(async ({ userId, request }) => {
  try {
    const body = await request.json();
    const result = await repository.create(body);
    return apiSuccess({ result });
  } catch (error) {
    console.error('[API] Error:', error);

    if (error instanceof z.ZodError) {
      return apiError('Validation failed', { status: 400 });
    }

    if (error instanceof Error && error.message.includes('unique')) {
      return apiError('Already exists', { status: 409 });
    }

    return apiError('Internal server error', { status: 500 });
  }
});

6. チェックリスト

新しいAPIルート作成時:

  • 認証ミドルウェア適用
  • リクエストバリデーション(Zod)
  • 統一レスポンス形式使用
  • エラーハンドリング(try-catch)
  • 認可チェック(リソース所有者確認)
  • ログ出力

7. アンチパターン

// ❌ 認証なし
export async function GET(request: Request) {
  const data = await db.select().from(users);
  return Response.json(data);
}

// ❌ バリデーションなし
const { email } = await request.json();
await db.insert(users).values({ email });

// ❌ エラー握りつぶし
try {
  await riskyOperation();
} catch (e) {
  // 何もしない
}

Comments

Markdown supported