name: backend-dev description: バックエンド開発(サーバーロジック、認証、マイクロサービス、サーバーレス関数、ミドルウェア、バックグラウンドジョブ、キャッシュ戦略、アーキテクチャパターン)のためのスキルです。キーワード: backend, server, API endpoint, middleware, authentication, authorization, microservice, serverless, background job, queue, caching, session でトリガー。
バックエンド開発
核となる原則
- ステートレスサービス — セッション状態をメモリに保存せず、Redisまたはデータベースを使用する
- フェイルファスト — 入力は境界で検証し、エラーは早期に返す
- べき等性 — POST/PUTエンドポイントは複数回呼び出しても安全であること
- 可観測性 — すべてのサービスにログ、メトリクス、トレースが必要
APIエンドポイントの構造
リクエストライフサイクル
リクエスト → レート制限 → 認証 → 認可 → バリデーション → ビジネスロジック → レスポンス
この順序を決して省略しないこと。認証はビジネスロジックより前に、バリデーションは処理より前に実行する。
レスポンス標準
// 成功
{ "data": { ... }, "meta": { "page": 1, "total": 100 } }
// エラー
{
"error": {
"code": "VALIDATION_ERROR",
"message": "人間が読める説明",
"details": [{ "field": "email", "issue": "Invalid format" }]
}
}
認証パターン
JWTのベストプラクティス
- アクセストークンのTTLは短く(15分)、リフレッシュトークンは長く(7日)設定
- リフレッシュトークンはhttpOnlyクッキーに保存し(localStorageは使わない)、署名、有効期限、発行者、対象者を検証する
- リフレッシュトークンは使用ごとにローテーション
- 失効リストを維持するか、短命トークンのみを使用
セッションベースの認証
- セッションIDをhttpOnly、Secure、SameSiteクッキーに保存
- セッションデータはTTL付きでRedisに保存
- ログイン後にセッションIDを再生成(セッション固定化防止)
認可
// 認可チェックはルートだけでなく、サービス層でも行う
async function getOrder(userId: string, orderId: string) {
const order = await db.orders.findById(orderId)
if (!order) throw new NotFoundError()
// 認可チェック — ユーザーは自身の注文のみ閲覧可能
if (order.userId !== userId) throw new ForbiddenError()
return order
}
バックグラウンドジョブとキュー
キューは以下の用途に使用する:
- メール送信
- 画像/ファイル処理
- 同期的である必要のない外部API呼び出し
- 長時間実行タスク
ジョブパターン
ファイア・アンド・フォーゲット: エンキューしてすぐに返却
スケジュール: 特定の時刻に実行(cron)
遅延: X分/時間後に実行
リトライ: 失敗時にバックオフ付きで再試行
常に以下を実装すること:
- 失敗したジョブのためのデッドレターキュー
- 重複処理を防ぐためのべき等性キー
- ジョブごとのタイムアウト
- 最大リトライ回数
エラーハンドリング
// 構造化されたエラー階層
class AppError extends Error {
constructor(
public message: string,
public statusCode: number,
public code: string
) { super(message) }
}
class ValidationError extends AppError {
constructor(details: ValidationDetail[]) {
super('Validation failed', 400, 'VALIDATION_ERROR')
this.details = details
}
}
// グローバルエラーハンドラー
app.use((err, req, res, next) => {
if (err instanceof AppError) {
return res.status(err.statusCode).json({ error: err })
}
// 予期しないエラーをログ出力
logger.error('Unexpected error', { err, req })
res.status(500).json({ error: { code: 'INTERNAL_ERROR' } })
})
ミドルウェアチェックリスト
すべての本番APIに必要なもの:
- リクエストログ(メソッド、パス、ステータス、期間)
- レート制限(IPごと、ユーザーごと、エンドポイントごと)
- CORS設定(明示的なホワイトリスト)
- Helmet / セキュリティヘッダー
- リクエストIDの伝播(分散トレーシング用)
- ボディサイズ制限
- タイムアウト処理
環境設定
// 設定をハードコードしない — 常に環境変数から取得
const config = {
db: {
url: process.env.DATABASE_URL, // 必須
poolSize: parseInt(process.env.DB_POOL_SIZE ?? '10')
},
jwt: {
secret: process.env.JWT_SECRET, // 必須
expiresIn: process.env.JWT_TTL ?? '15m'
}
}
// 必須設定は起動時にバリデーション — フェイルファスト
const required = ['DATABASE_URL', 'JWT_SECRET']
for (const key of required) {
if (!process.env[key]) throw new Error(`Missing required env: ${key}`)
}