_private/qwestly-docs/Engineering/API-Routes-Guide.md
Table of Contents
API Routes Guide
NextJS provides a flexible way to create API endpoints using the App Router through route handlers. These are defined in your app/api directory and allow you to create serverless functions that can handle HTTP requests.
Basic API Route
The most basic API route is created by adding a route.ts file in your app/api directory:
// app/api/hello/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
return NextResponse.json({ message: 'Hello World' })
}
Dynamic Routes with Parameters
Single Dynamic Parameter [id]
You can capture dynamic parameters using square brackets [id] in the folder name:
// app/api/posts/[id]/route.ts
import { NextResponse } from 'next/server'
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const id = params.id
return NextResponse.json({ postId: id })
}
This will match routes like:
/api/posts/1/api/posts/2/api/posts/any-string
Catch-all Routes
Using double brackets [[...slug]] makes the catch-all parameter optional:
// app/api/posts/[[...slug]]/route.ts
import { NextResponse } from 'next/server'
export async function GET(
request: Request,
{ params }: { params: { slug?: string[] } }
) {
const slug = params.slug || []
return NextResponse.json({ path: slug })
}
This will match:
/api/posts→slug = []/api/posts/a→slug = ['a']/api/posts/a/b→slug = ['a', 'b']
Using ApiRouter for Advanced Route Management
The ApiRouter class provides a more sophisticated and FastAPI-inspired approach to handling API routes in NextJS. It offers features like type-safe route parameters, search parameter handling, and optional authentication checks.
Basic Setup
Create a catch-all route file (e.g., app/api/users/[[...slug]]/route.ts) and initialize the router:
import ApiRouter from "@/app/utils/api-router";
const router = new ApiRouter({
// Optional: Add authentication check for auth routes
authenticate: async (request) => {
return true; // Your auth logic here
},
// Optional: boolean to authenticate all routes
authAllRoutes: true,
});
// Define your routes; route is relative to /api/users
router.get(route, handler, { auth: boolean });
// Matches /api/users/:userId
router.get("{userId}", async (req, { userId, queryParam }) => {
// userId is a route parameter
// queryParam would come from search params
return NextResponse.json({ userId, queryParam });
});
// matches /api/users
// the slash is optional in case it's weird to route an empty string
router.post("/", async (req) => {
const body = await req.json();
return NextResponse.json(body);
});
// Export the route handlers
export const GET = router.getMethodHandler("GET");
export const POST = router.getMethodHandler("POST");
// Alternatively, export all methods at once:
const routes = router.exportRoutes();
export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } = routes;
Key Features
-
Type-Safe Route Parameters
- Use curly braces for route parameters:
users/{userId} - Parameters are automatically extracted and typed
- Supports both route parameters and search parameters
- Use curly braces for route parameters:
-
Method-Specific Routes
router.get("path", handler); // GET requests router.post("path", handler); // POST requests router.put("path", handler); // PUT requests router.delete("path", handler); // DELETE requests router.patch("path", handler); // PATCH requests -
Authentication Support
const router = new ApiRouter({ authenticate: async (request) => { // Return true if authenticated, false otherwise return verifyAuth(request); }, }); // This route will be authenticated router.get("{id}/secret", async (req, { id }) => { return NextResponse.json({ id }); }, { auth: true }); -
Automatic Parameter Handling
router.get("users/{userId}/posts/{postId}", async (req, params) => { const { userId, postId, page, limit } = params; // userId and postId are route parameters // page and limit would be from ?page=1&limit=10 return NextResponse.json({ userId, postId, page, limit }); });
Best Practices
-
Organize Routes by Resource
// app/api/users/[[...slug]]/route.ts router.get("", listUsers); // GET /api/users router.get("{userId}", getUser); // GET /api/users/123 router.post("", createUser); // POST /api/users router.put("{userId}", updateUser); // PUT /api/users/123 router.delete("{userId}", deleteUser); // DELETE /api/users/123 -
Handle Errors Consistently
router.get("{userId}", async (req, { userId }) => { try { const user = await getUser(userId); if (!user) { return NextResponse.json( { error: "User not found" }, { status: 404 } ); } return NextResponse.json(user); } catch (error) { return NextResponse.json( { error: "Internal server error" }, { status: 500 } ); } }); -
Leverage TypeScript for Better Type Safety
interface User { id: string; name: string; } router.post<User>("users", async (req) => { const user: User = await req.json(); return NextResponse.json(user); });
The ApiRouter provides a more structured and maintainable way to handle API routes in your NextJS application, especially for larger applications with many endpoints. It combines the flexibility of NextJS's routing system with the convenience of a FastAPI-style router.
HTTP Methods
Route handlers support all HTTP methods:
export async function GET(request: Request) { }
export async function POST(request: Request) { }
export async function PUT(request: Request) { }
export async function PATCH(request: Request) { }
export async function DELETE(request: Request) { }
export async function HEAD(request: Request) { }
export async function OPTIONS(request: Request) { }
Response Helpers
NextJS provides several response helpers:
// JSON Response
return NextResponse.json({ data: 'hello' })
// Redirect
return NextResponse.redirect(new URL('/new-page', request.url))
// Rewrite
return NextResponse.rewrite(new URL('/rewritten-page', request.url))
Error Handling
You can handle errors by throwing responses:
import { NextResponse } from 'next/server'
export async function GET() {
const error = new Error('Not Found')
error.status = 404
throw error
// Or use NextResponse
return NextResponse.json(
{ error: 'Not Found' },
{ status: 404 }
)
}
Best Practices
- Always validate input parameters
- Use appropriate HTTP status codes
- Handle errors gracefully
- Use TypeScript for better type safety
- Keep route handlers focused and modular
- Use middleware for common operations like authentication
- Consider rate limiting for public APIs