3 min read
In this tutorial, we'll learn how to build robust APIs using Next.js 14's App Router. We'll create a simple API for managing a book collection to demonstrate key concepts and best practices.
Prerequisites
- Node.js installed on your system
- Basic knowledge of JavaScript/TypeScript
- Familiarity with Next.js fundamentals
Setting Up the Project
First, create a new Next.js project:
npx create-next-app@latest my-api-project
cd my-api-project
Project Structure
Our API will follow the Next.js App Router convention. Create the following structure:
app/
api/
books/
route.ts
books/[id]/
route.ts
Creating the API Routes
1. GET and POST Endpoints (app/api/books/route.ts)
import { NextResponse } from 'next/server'
// In-memory storage for demonstration
let books = [
{ id: 1, title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' },
{ id: 2, title: '1984', author: 'George Orwell' }
]
export async function GET() {
return NextResponse.json(books)
}
export async function POST(request: Request) {
try {
const book = await request.json()
// Validate input
if (!book.title || !book.author) {
return NextResponse.json(
{ error: 'Title and author are required' },
{ status: 400 }
)
}
// Create new book
const newBook = {
id: books.length + 1,
title: book.title,
author: book.author
}
books.push(newBook)
return NextResponse.json(newBook, { status: 201 })
} catch (error) {
return NextResponse.json(
{ error: 'Invalid request body' },
{ status: 400 }
)
}
}
2. Single Book Operations (app/api/books/[id]/route.ts)
import { NextResponse } from 'next/server'
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const book = books.find(b => b.id === parseInt(params.id))
if (!book) {
return NextResponse.json(
{ error: 'Book not found' },
{ status: 404 }
)
}
return NextResponse.json(book)
}
export async function PUT(
request: Request,
{ params }: { params: { id: string } }
) {
try {
const bookIndex = books.findIndex(b => b.id === parseInt(params.id))
if (bookIndex === -1) {
return NextResponse.json(
{ error: 'Book not found' },
{ status: 404 }
)
}
const updatedBook = await request.json()
// Validate input
if (!updatedBook.title || !updatedBook.author) {
return NextResponse.json(
{ error: 'Title and author are required' },
{ status: 400 }
)
}
books[bookIndex] = {
...books[bookIndex],
...updatedBook
}
return NextResponse.json(books[bookIndex])
} catch (error) {
return NextResponse.json(
{ error: 'Invalid request body' },
{ status: 400 }
)
}
}
export async function DELETE(
request: Request,
{ params }: { params: { id: string } }
) {
const bookIndex = books.findIndex(b => b.id === parseInt(params.id))
if (bookIndex === -1) {
return NextResponse.json(
{ error: 'Book not found' },
{ status: 404 }
)
}
books = books.filter(b => b.id !== parseInt(params.id))
return NextResponse.json(
{ message: 'Book deleted successfully' },
{ status: 200 }
)
}
Testing the API
You can test your API using cURL or tools like Postman. Here are some example requests:
Get All Books
curl http://localhost:3000/api/books
Create a Book
curl -X POST http://localhost:3000/api/books \
-H "Content-Type: application/json" \
-d '{"title": "Dune", "author": "Frank Herbert"}'
Get a Single Book
curl http://localhost:3000/api/books/1
Update a Book
curl -X PUT http://localhost:3000/api/books/1 \
-H "Content-Type: application/json" \
-d '{"title": "Updated Title", "author": "Updated Author"}'
Delete a Book
curl -X DELETE http://localhost:3000/api/books/1
Error Handling and Best Practices
- Input Validation: Always validate incoming request data
- HTTP Status Codes: Use appropriate status codes for different scenarios
- Error Messages: Provide clear error messages for better debugging
- Type Safety: Use TypeScript for better type checking and IDE support
- Response Format: Maintain consistent response format across endpoints
Conclusion
Next.js's App Router provides a powerful and intuitive way to build APIs. The file-based routing system makes it easy to organize your endpoints, while built-in features like automatic response parsing make handling requests straightforward.
Remember to:
- Use appropriate HTTP methods for different operations
- Implement proper error handling
- Validate input data
- Follow REST conventions
- Use TypeScript for better type safety
For production applications, consider:
- Adding authentication/authorization
- Implementing rate limiting
- Setting up a proper database
- Adding request validation middleware
- Implementing logging and monitoring
Related Posts
• 5 min read
APIs (Application Programming Interfaces) are the backbone of modern digital applications. They allow different software systems to communicate, exchange data, and collaborate seamlessly. As businesse...
• 4 min read
In today’s interconnected digital world, APIs (Application Programming Interfaces) are the backbone of communication between different software applications. From mobile apps to cloud services, APIs e...
• 5 min read
In the modern digital ecosystem, APIs (Application Programming Interfaces) serve as the backbone of connectivity. Whether you're building microservices, enabling integrations, or crafting data pipelin...