Menu

11. Backend Development

Next.js Master Roadmap - IT Technology

This chapter covers backend development in Next.js, focusing on creating API routes with GET, POST, PUT, PATCH, and DELETE handlers, applying REST principles, implementing validation and error handling, and using middleware for authentication, redirects, and logging.

No MCQ questions available for this chapter.

11. Backend Development

Route Handlers

In Next.js, API routes are defined under the app/api directory. Each route file exports an async function corresponding to an HTTP method. The request object is of type NextRequest, providing access to URL, headers, cookies, and body. Below we explore each method with detailed examples and best practices.

GET Requests

The GET handler retrieves resources, often with query parameters for filtering, pagination, or sorting. It should be idempotent and safe.

export async function GET(request: NextRequest) { const searchParams = request.nextUrl.searchParams; const limit = searchParams.get('limit') ?? '10'; const category = searchParams.get('category'); let query = 'SELECT * FROM products'; const params: any[] = []; if (category) { query += ' WHERE category = $' + (params.length + 1); params.push(category); } query += ' LIMIT $' + (params.length + 1); params.push(limit); const data = await db.query(query, params); return NextResponse.json(data); }

Example: GET /api/products?limit=10&category=electronics returns the first ten electronics products. To enable caching for static data, export const dynamic = 'force-static' or set revalidate = 60 for incremental static regeneration.

POST Requests

POST creates a new resource. The handler must validate the incoming payload, persist it, and return the created entity with a 201 Created status and a Location header pointing to the new resource.

export async function POST(request: NextRequest) { const body = await request.json(); const validated = schema.parse(body); // Zod validation const result = await db.insert(validated); const url = request.nextUrl.origin + `/api/products/${result.id}`; return NextResponse.json(result, { status: 201, headers: { Location: url } }); }

Example: POST /api/orders with body { userId: '123', items: [{ productId: '456', qty: 2 }] } creates an order and returns 201 with Location: /api/orders/789.

PUT Requests

PUT replaces an existing resource entirely. It is idempotent; repeating the same request yields the same state. The handler receives the resource ID via route parameters.

export async function PUT(request: NextRequest, { params }: { params: { id: string } }) { const body = await request.json(); const updated = await db.update(params.id, body); // full replacement return NextResponse.json(updated); }

Example: PUT /api/users/123 with a complete user object replaces the user record. If the resource does not exist, return 404 Not Found.

PATCH Requests

PATCH applies partial modifications. Unlike PUT, it is not required to be idempotent, especially when the update depends on current values (e.g., incrementing a counter).

export async function PATCH(request: NextRequest, { params }: { params: { id: string } }) { const body = await request.json(); const updated = await db.patch(params.id, body); // merges fields return NextResponse.json(updated); }

Example: PATCH /api/users/123 with { name: 'New Name' } updates only the name field.

DELETE Requests

DELETE removes a resource. Successful deletion returns 204 No Content with an empty body. For soft deletes, update a deletedAt timestamp instead of removing the row.

export async function DELETE(request: NextRequest, { params }: { params: { id: string } }) { await db.delete(params.id); return new NextResponse(null, { status: 204 }); }

Example: DELETE /api/posts/456 removes the post. Soft‑delete alternative:

await db.update(params.id, { deletedAt: new Date() });

API Design

Following RESTful conventions ensures predictable, scalable APIs. This section outlines URL design, HTTP method mapping, status codes, versioning, and pagination.

REST Principles

  • Resource‑based URLs: Use nouns, not verbs. Collections: /api/users, individual items: /api/users/:id, sub‑resources: /api/users/:id/posts.
  • HTTP methods map to CRUD:
    • GET → Read
    • POST → Create
    • PUT → Replace (full update)
    • PATCH → Modify (partial update)
    • DELETE → Remove
  • Status codes: Use the appropriate code for each outcome.
CodeMeaningTypical Use
200OKSuccessful GET, PUT, PATCH
201CreatedSuccessful POST
204No ContentSuccessful DELETE
400Bad RequestValidation error
401UnauthorizedMissing or invalid auth
403ForbiddenAuthenticated but insufficient rights
404Not FoundResource does not exist
500Internal Server ErrorUnexpected server failure

Example: GET /api/users/123/posts?page=2&limit=20 returns the second page of a user's posts, twenty per page.

Versioning

API evolution should not break existing clients. Two common strategies:

  1. URL versioning: /api/v1/users, /api/v2/users.
  2. Header versioning: Accept: application/vnd.api.v1+json.

Validation

Input validation protects against malformed data and injection attacks. Zod is a popular schema‑based validator that integrates seamlessly with Next.js API routes.

const schema = z.object({ email: z.string().email(), password: z.string().min(8), confirmPassword: z.string().min(8) }).refine((data) => data.password