API 共通仕様
面接調整システムの REST API に共通する規約。個別エンドポイントの仕様は左メニューの各ページを参照。
基本情報
| 項目 | 内容 |
|---|---|
| ベース URL(ローカル) | http://localhost:8080 |
| ベース URL(dev) | https://interview-arrange-api.heineken.dev.4digit.ai |
| API プレフィックス | /api/v1 |
| OpenAPI 定義 | GET /openapi.json(ルート定義から自動生成) |
| Swagger UI | GET /docs |
| ヘルスチェック | GET /health / GET /api/v1/ping |
| OpenAPI バージョン | 3.0.3 |
メソッド
REST メソッドを採用しています。
| メソッド | 用途 |
|---|---|
| GET | 取得 |
| POST | 作成・実行(インポート、送信、同期の起動など) |
| PATCH | 部分更新 |
| PUT | 全体置換(アンケートインポート設定、候補者選定の一括更新) |
| DELETE | 削除 |
命名規則
クエリパラメータとノードの命名を統一し、可読性を向上させるため、リクエスト時の URI と JSON 内のノードには snake_case を使用します。
レスポンスは camelCase
リクエストボディの公開フィールドは snake_case ですが、レスポンスのデータ部は camelCase です(interviewDurationMinutes)。この変換はルートハンドラ内で明示的に行われます。一部のレスポンス(sent_count / failed_count / file_id など)は snake_case のまま返る点に注意してください。
リクエストとレスポンス
ヘッダー
リクエストヘッダー
| ヘッダー | 必須 | 内容 |
|---|---|---|
Authorization |
公開エンドポイント以外で必須 | Bearer <access_token> |
Content-Type |
ボディを持つ場合 | application/json(TSV アップロードのみ multipart/form-data) |
X-Request-ID |
任意 | 指定するとレスポンスとログで同じ ID が使われる |
レスポンスヘッダー
| ヘッダー | 内容 |
|---|---|
Content-Type |
application/json |
X-Request-ID |
リクエスト追跡用 ID |
CORS
ALLOWED_ORIGINS に列挙したオリジンのみ許可。許可メソッドは GET / POST / PUT / PATCH / DELETE / OPTIONS、credentials: true、プリフライトのキャッシュは 24 時間。
レスポンス封筒
全レスポンスが共通の封筒形式で返ります。実データは常に data の中にあります。
成功時(200 / 201)
{
"success": true,
"status": "success",
"statusCode": 200,
"timestamp": "2026-01-15T10:00:00.000Z",
"requestId": "1a2b3c4d",
"data": { "id": 1, "name": "Engineering Interview 2026" },
"path": "/api/v1/projects/1",
"method": "GET"
}
ページネーション時
data が配列になり、pagination が追加されます。
{
"success": true,
"status": "success",
"statusCode": 200,
"timestamp": "2026-01-15T10:00:00.000Z",
"data": [ { "id": 1 }, { "id": 2 } ],
"pagination": {
"page": 1,
"limit": 20,
"totalCount": 100,
"totalPages": 5,
"hasNext": true,
"hasPrevious": false
},
"path": "/api/v1/projects",
"method": "GET"
}
エラー時
{
"success": false,
"status": "fail",
"statusCode": 404,
"timestamp": "2026-01-15T10:00:00.000Z",
"requestId": "1a2b3c4d",
"data": {
"code": "NOT_FOUND",
"error": "Not Found",
"message": "Project not found"
},
"path": "/api/v1/projects/999",
"method": "GET"
}
204 No Content
削除系(DELETE)はボディを持たず 204 を返します。封筒形式は適用されません。
フロントエンドでの取り出し
Orval の custom-fetch が { data, status, headers } 形で返すため、画面側は response.data.data を辿ります。lib/utils.ts の apiData() ヘルパーを使ってください。
ページネーション
一覧系エンドポイントの共通クエリパラメータ。
| パラメータ | 型 | 既定値 | 制約 | 説明 |
|---|---|---|---|---|
page |
integer | 1 |
正の整数 | ページ番号 |
limit |
integer | 20 |
1〜100 | 1 ページあたりの件数 |
sort |
string | - | - | createdAt:desc 形式 |
アンケート系は offset/limit
/api/v1/surveys/* の一覧は page ではなく offset / limit を使い、レスポンスも封筒の data 内に { items, total, limit, offset } を持つ独自形式です。
認証要件
Google OAuth 2.0 で取得したユーザー情報をもとにバックエンドが発行する JWT を用いた認証です。Authorization ヘッダーに有効な Bearer トークンが必要です。
sequenceDiagram
participant FE as フロントエンド
participant API as Backend API
participant G as Google OAuth
participant DB as PostgreSQL
FE->>API: GET /api/v1/auth/google
API-->>FE: 302 Google 認可画面へ
FE->>G: ログイン・同意
G->>API: GET /api/v1/auth/google/callback?code=...
API->>G: code をトークンに交換
API->>DB: ユーザーを検索/作成
API-->>FE: 302 FRONTEND_URL へ(トークン付き)
FE->>API: 以降 Authorization: Bearer <token>
API->>API: JWT 検証(HS256)
| 項目 | 内容 |
|---|---|
| 署名アルゴリズム | HS256 |
| アクセストークン有効期限 | JWT_EXPIRES_IN(既定 1 時間) |
| リフレッシュトークン有効期限 | JWT_REFRESH_EXPIRES_IN(既定 7 日) |
| ペイロード | userId / googleId / email / name / role |
| 更新 | POST /api/v1/auth/refresh |
| フロントの保管場所 | localStorage の accessToken |
認可(ロール)
ロールは階層構造で、admin(3)> member(2)> viewer(1)。各ルータで POST / PATCH / PUT / DELETE に対して member 以上を要求します。
| ルータ | 認証 | 更新系に必要なロール |
|---|---|---|
/api/v1/auth/* |
一部のみ(/logout /me) |
- |
/api/v1/users |
必要 | - |
/api/v1/projects/* |
必要 | member |
/api/v1/projects/{id}/candidates/* |
必要 | member |
/api/v1/projects/{id}/interviewers/* |
必要 | member |
/api/v1/projects/{id}/email-templates/* |
必要 | member |
/api/v1/projects/{id}/emails/* |
必要 | member |
/api/v1/projects/{id}/reservations/* |
必要 | member(PATCH / PUT / DELETE) |
/api/v1/email-templates |
必要 | member |
/api/v1/surveys/* |
必要 | なし(参照専用だが viewer も全件閲覧可) |
/api/v1/scheduling/* |
不要(公開) | - |
例外処理
例外時のステータスコードは次のとおりです。
| 説明 | ステータスコード | ステータス名 |
|---|---|---|
| バリデーションエラー | 400 | Bad Request |
| トークンが欠落または無効 | 401 | Unauthorized |
| ロール不足 | 403 | Forbidden |
| リソースが存在しない | 404 | Not Found |
| 重複・枠競合 | 409 | Conflict |
| レート超過 | 429 | Too Many Requests |
| サーバ内部エラー | 500 | Internal Server Error |
| 外部 API エラー | 502 | Bad Gateway |
| DB / Databricks 接続不可 | 503 | Service Unavailable |
| 外部 API タイムアウト | 504 | Gateway Timeout |
エラーコード一覧
data.code に入る値。汎用エラーは src/lib/errors.ts、業務固有エラーは src/error.ts に定義されています。
汎用
| コード | ステータス | 説明 |
|---|---|---|
BAD_REQUEST |
400 | 不正なリクエスト |
VALIDATION_ERROR |
400 | Zod バリデーション失敗。details に項目別のエラーが入る |
UNAUTHORIZED |
401 | 認証が必要/トークンが無効 |
FORBIDDEN |
403 | 権限不足 |
NOT_FOUND |
404 | リソースが存在しない |
CONFLICT |
409 | 重複 |
TOO_MANY_REQUESTS |
429 | レート超過 |
INTERNAL_ERROR |
500 | 内部エラー(詳細はクライアントに返さない) |
データベース
| コード | ステータス | 説明 |
|---|---|---|
DATABASE_ERROR |
500 | DB エラー全般 |
DATABASE_QUERY_ERROR |
500 | クエリ失敗 |
DATABASE_CONNECTION_ERROR |
503 | 接続失敗 |
DATABASE_CONSTRAINT_ERROR |
409 | 一意制約違反など |
外部 API
| コード | ステータス | 説明 |
|---|---|---|
EXTERNAL_API_ERROR |
502 | 外部 API エラー全般 |
API_TIMEOUT |
504 | タイムアウト |
API_UNAVAILABLE |
503 | 一時的に利用不可 |
GOOGLE_API_ERROR |
502 | Google API エラー |
GOOGLE_CALENDAR_ERROR |
502 | Google Calendar エラー |
GMAIL_API_ERROR |
502 | Gmail API エラー |
Databricks
| コード | ステータス | 説明 |
|---|---|---|
DATABRICKS_ERROR |
502 | Databricks エラー全般 |
DATABRICKS_QUERY_ERROR |
502 | ステートメントの失敗・キャンセル・タイムアウト |
DATABRICKS_AUTH_ERROR |
502 | OAuth M2M のトークン取得失敗 |
DATABRICKS_NOT_CONFIGURED |
503 | Databricks が未設定(DATABRICKS_HOST / 資格情報が空) |
業務固有
| コード | ステータス | 説明 |
|---|---|---|
TOKEN_EXPIRED |
401 | JWT の期限切れ |
TOKEN_INVALID |
401 | JWT が不正 |
SCHEDULING_TOKEN_ERROR |
400 | 調整トークンが無効または期限切れ |
SLOT_NOT_AVAILABLE |
409 | 選択した枠が既に埋まっている/確定済み |
EMAIL_SEND_ERROR |
500 | メール送信失敗 |
IMPORT_ERROR |
400 | インポートエラー。row / column を持つ |
SCORING_ERROR |
400 | スコアリングルールが不正 |
INVALID_STATUS_TRANSITION |
400 | 許可されていないステータス遷移 |
エラー処理の実装
ハンドラでは throw new NotFoundError('Project') のように投げるだけでよく、middlewares/error-handler.ts が上記の変換を行います。isOperational が false のエラーは詳細をクライアントに返しません(開発時のみ stack を含む)。
エンドポイント一覧
| リソース | 件数 | ページ |
|---|---|---|
| 認証 | 5 | 認証 API |
| ユーザー | 1 | ユーザー API |
| プロジェクト | 9 | プロジェクト API |
| 候補者 | 17 | 候補者 API |
| 面接官 | 6 | 面接官 API |
| メール | 10 | メール API |
| 予約 | 3 | 予約 API |
| 日程調整(公開) | 3 | 日程調整 API |
| アンケート | 7 | アンケート API |