Advanced Tutorial
API Development
Build robust, scalable APIs with Next.js, authentication, and modern database integration
150 min
Total Duration
Advanced
Difficulty Level
5
Key Modules
Step-by-Step Implementation
Tutorial Sections
1
API Architecture & Design
Learn RESTful principles and design scalable API architecture
1
RESTful API Design Principles
8 minREST (Representational State Transfer) is an architectural style for designing web services.
Core Principles:
- Stateless: Each request contains all information needed
- Resource-based: URLs represent resources, not actions
- HTTP Methods: Use GET, POST, PUT, DELETE appropriately
- Status Codes: Return meaningful HTTP status codes
Resource Naming:
- Use nouns, not verbs: `/users` not `/getUsers`
- Use plural nouns: `/users` not `/user`
- Hierarchical relationships: `/users/123/posts`
Implementation
// Good API Design Examples
// Users resource
GET /api/users // Get all users
GET /api/users/123 // Get specific user
POST /api/users // Create new user
PUT /api/users/123 // Update user (full)
PATCH /api/users/123 // Update user (partial)
DELETE /api/users/123 // Delete user
// Nested resources
GET /api/users/123/posts // Get user's posts
POST /api/users/123/posts // Create post for user
GET /api/posts/456/comments // Get post comments
// Query parameters for filtering, sorting, pagination
GET /api/users?page=2&limit=10&sort=name&filter=active
// Response structure
{
"data": [...],
"meta": {
"total": 100,
"page": 2,
"limit": 10,
"totalPages": 10
},
"links": {
"first": "/api/users?page=1",
"prev": "/api/users?page=1",
"next": "/api/users?page=3",
"last": "/api/users?page=10"
}
}2
API Versioning Strategies
7 minWhy Version APIs?
- Maintain backward compatibility
- Allow gradual migration
- Support multiple client versions
Versioning Methods:
1. URL Versioning: `/api/v1/users`
2. Header Versioning: `Accept: application/vnd.api.v1+json`
3. Query Parameter: `/api/users?version=1`
Best Practice: URL versioning is most common and clear.
Implementation
// API Versioning Structure
/api/v1/users // Version 1 - Original
/api/v2/users // Version 2 - Enhanced with new fields
/api/v3/users // Version 3 - Breaking changes
// Version middleware
// middleware/version.ts
export function apiVersion(req: Request) {
const url = new URL(req.url);
const version = url.pathname.split('/')[2]; // Extract v1, v2, etc.
return {
version: version || 'v1',
isLatest: version === 'v3'
};
}
// Handling different versions
export async function GET(request: Request) {
const { version } = apiVersion(request);
switch (version) {
case 'v1':
return getUsersV1();
case 'v2':
return getUsersV2();
case 'v3':
return getUsersV3();
default:
return getUsersV1(); // Default to v1
}
}3
HTTP Status Codes & Error Handling
10 minCommon Status Codes:
- 200 OK: Successful GET, PATCH
- 201 Created: Successful POST
- 204 No Content: Successful DELETE
- 400 Bad Request: Invalid request data
- 401 Unauthorized: Authentication required
- 403 Forbidden: Access denied
- 404 Not Found: Resource doesn't exist
- 422 Unprocessable Entity: Validation errors
- 500 Internal Server Error: Server error
Error Response Format:
Consistent error structure helps clients handle errors properly.
Implementation
// Error response types
interface ApiError {
error: {
code: string;
message: string;
details?: any;
timestamp: string;
};
}
// Error handling utility
export class ApiError extends Error {
constructor(
public statusCode: number,
public code: string,
message: string,
public details?: any
) {
super(message);
this.name = 'ApiError';
}
}
// Error response helper
export function errorResponse(error: ApiError): Response {
return Response.json(
{
error: {
code: error.code,
message: error.message,
details: error.details,
timestamp: new Date().toISOString(),
}
},
{ status: error.statusCode }
);
}
// Usage in route handlers
export async function POST(request: Request) {
try {
const data = await request.json();
// Validation
if (!data.email) {
throw new ApiError(400, 'MISSING_EMAIL', 'Email is required');
}
if (!isValidEmail(data.email)) {
throw new ApiError(422, 'INVALID_EMAIL', 'Email format is invalid');
}
const user = await createUser(data);
return Response.json(user, { status: 201 });
} catch (error) {
if (error instanceof ApiError) {
return errorResponse(error);
}
// Unexpected errors
return errorResponse(
new ApiError(500, 'INTERNAL_ERROR', 'Something went wrong')
);
}
}Section 1 of 2
Ready to Build APIs?
Start creating robust, scalable APIs today