# API Standardization Guide ## Overview All API routes must follow this standardized format for consistency, maintainability, and predictable client-side behavior. ## Response Format ### Success Response (GET, POST, PUT, PATCH) ```json { "success": true, "data": { /* actual data */ }, "timestamp": "2024-01-01T12:00:00Z", "path": "/api/v1/students" } ``` ### Error Response ```json { "success": false, "error": "Descriptive error message", "timestamp": "2024-01-01T12:00:00Z", "path": "/api/v1/students" } ``` ### Delete Response (204 No Content) No body, just status 204 ## Status Codes | Code | Meaning | Use Case | |------|---------|----------| | 200 | OK | GET, PUT, PATCH success | | 201 | Created | POST success | | 204 | No Content | DELETE success | | 400 | Bad Request | Invalid input data | | 401 | Unauthorized | Not authenticated | | 403 | Forbidden | Authenticated but no permission | | 404 | Not Found | Resource doesn't exist | | 422 | Unprocessable | Validation error | | 500 | Server Error | Unhandled exception | ## Route Structure ### Before (Inconsistent) ```typescript export async function POST(request: Request) { try { const session = await getSession(); if (!session) { return NextResponse.json({ error: 'Not authenticated' }, { status: 401 }); } // ... 100 lines of mixed logic if (!resource) { return NextResponse.json({ error: 'Not found' }, { status: 404 }); } // Multiple different response formats return NextResponse.json(resource, { status: 201 }); } catch (error) { return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); } } ``` ### After (Standardized) ```typescript import { NextRequest } from 'next/server'; import { withMiddleware, withAuth, withRoles, withAudit } from '@/lib/route-middleware'; import { successResponse, createdResponse, notFoundError, validationError } from '@/lib/api-response'; import { db } from '@/lib/db'; async function handler(request: NextRequest) { const body = await request.json(); const { resourceId } = body; if (!resourceId) { return validationError('resourceId is required'); } const resource = await db.resource.create({ data: body }); if (!resource) { return notFoundError('Resource creation failed'); } return createdResponse(resource); } export const POST = withMiddleware( withAuth, withRoles(['TEACHER', 'ADMIN']), withAudit('CREATE_RESOURCE') )(handler); ``` ## Using API Response Helpers All routes should use helpers from `@/lib/api-response`: ```typescript import { successResponse, createdResponse, noContentResponse, unauthorizedError, forbiddenError, notFoundError, validationError, serverError, } from '@/lib/api-response'; // GET - return data return successResponse(data); // POST - return created resource return createdResponse(resource); // DELETE - no content return noContentResponse(); // Errors return unauthorizedError(); return forbiddenError(); return notFoundError('Resource not found'); return validationError('Invalid email format'); return serverError(error); ``` ## Using Middleware ### Basic Authentication ```typescript import { withAuth } from '@/lib/route-middleware'; async function handler(request: NextRequest) { const user = (request as any).user; // injected by middleware // handler logic } export const GET = withAuth(handler); ``` ### Role-Based Access ```typescript import { withRoles } from '@/lib/route-middleware'; async function handler(request: NextRequest) { // only TEACHER and ADMIN can access } export const POST = withRoles(['TEACHER', 'ADMIN'])(handler); ``` ### Audit Logging ```typescript import { withAudit } from '@/lib/route-middleware'; export const DELETE = withAudit('DELETE_STUDENT')(handler); ``` ### Combining Middleware ```typescript import { withMiddleware, withAuth, withRoles, withAudit } from '@/lib/route-middleware'; export const PUT = withMiddleware( withAuth, withRoles(['TEACHER', 'ADMIN']), withAudit('UPDATE_GRADES') )(handler); ``` ## Client-Side Usage ### With useApiGet (for data fetching) ```typescript import { useApiGet } from '@/lib/hooks/useApi'; function MyComponent() { const { data, error, isLoading, mutate } = useApiGet('/api/v1/students'); if (isLoading) return