API Routes
Build backend APIs directly in Next.js
🚀 What are API Routes?
API Routes let you create backend endpoints inside your Next.js app. They run on the server and handle requests like GET, POST, PUT, and DELETE without needing a separate backend.
// app/api/hello/route.js
export async function GET(request) {
return Response.json({
message: 'Hello from API!'
})
}
// Access at: /api/hello
Key Concepts
File-based Routing
Create APIs with file structure
app/api/users/route.js
→ /api/users
HTTP Methods
Handle GET, POST, PUT, DELETE
export async function GET() {}
export async function POST() {}
Server-side Only
Runs on server, keeps secrets safe
const apiKey = process.env.API_KEY
JSON Responses
Easy data formatting
return Response.json({ data })
🔹 Basic GET Request
GET requests retrieve data from your API. This is the most common type of API route for fetching information.
// app/api/posts/route.js
export async function GET(request) {
const posts = [
{ id: 1, title: 'First Post' },
{ id: 2, title: 'Second Post' }
]
return Response.json(posts)
}
// Usage in component:
// fetch('/api/posts').then(res => res.json())
🔹 POST Request with Body
POST requests send data to your API to create new resources. You can access the request body using request.json().
// app/api/users/route.js
export async function POST(request) {
const body = await request.json()
const { name, email } = body
// Save to database (example)
const newUser = {
id: Date.now(),
name,
email
}
return Response.json(newUser, { status: 201 })
}
// Usage:
// fetch('/api/users', {
// method: 'POST',
// body: JSON.stringify({ name: 'John', email: '[email protected]' })
// })
🔹 Dynamic Route Parameters
Use dynamic segments in your API routes to handle specific resources by ID or other identifiers.
// app/api/posts/[id]/route.js
export async function GET(request, { params }) {
const { id } = params
// Fetch specific post
const post = {
id: id,
title: `Post ${id}`,
content: 'Post content here...'
}
return Response.json(post)
}
// Access at: /api/posts/123
// params.id will be "123"
🔹 Query Parameters
Read query parameters from the URL to filter or customize API responses based on user input.
// app/api/search/route.js
export async function GET(request) {
const { searchParams } = new URL(request.url)
const query = searchParams.get('q')
const limit = searchParams.get('limit') || 10
const results = [
{ id: 1, name: `Result for ${query}` }
]
return Response.json({
query,
limit,
results
})
}
// Access at: /api/search?q=nextjs&limit=5
🔹 Error Handling
Always handle errors properly in your API routes to provide meaningful feedback to clients.
// app/api/user/[id]/route.js
export async function GET(request, { params }) {
try {
const { id } = params
if (!id) {
return Response.json(
{ error: 'User ID is required' },
{ status: 400 }
)
}
// Simulate database fetch
const user = await fetchUserFromDB(id)
if (!user) {
return Response.json(
{ error: 'User not found' },
{ status: 404 }
)
}
return Response.json(user)
} catch (error) {
return Response.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
🔹 Multiple HTTP Methods
You can export multiple functions in one route file to handle different HTTP methods for the same endpoint.
// app/api/todos/route.js
// Get all todos
export async function GET() {
const todos = [
{ id: 1, task: 'Learn Next.js', done: false }
]
return Response.json(todos)
}
// Create new todo
export async function POST(request) {
const { task } = await request.json()
const newTodo = { id: Date.now(), task, done: false }
return Response.json(newTodo, { status: 201 })
}
// Delete all todos
export async function DELETE() {
return Response.json({ message: 'All todos deleted' })
}