When building full-stack applications, one of the biggest challenges is managing separate frontend and backend codebases. This is where Next.js API routes come in. With the App Router, Next.js allows you to build backend endpoints directly inside your project, no need for a separate Express server.
Today, for day 56, the goal was to understand the API routing in Next.js
React.js + Node.js vs Next.js
In MERN:
- Frontend → React
- Backend → Express (separate server)
In Next.js:
Same project = Frontend + Backend
You don’t need a separate backend anymore.
API routes live inside your app.
📁 Folder Structure (App Router Way)
We create APIs like this:
app/api/users/route.js
This automatically maps to:
/api/users
No extra routing setup needed.
HTTP Methods in Next.js API Routes
Inside route.js, you export functions like:
export async function GET() {}
export async function POST() {}
export async function PUT() {}
export async function DELETE() {}
Each function handles a specific HTTP method.
💡 Important correction:
- These are Web standard Request/Response handlers, not Express handlers.
- They run in server environments (Node.js or Edge Runtime).
Request & Response Handling
Basic Example:
export async function GET() {
return Response.json({ message: "Hello World" });
}
Access Request Data:
export async function POST(request) {
const body = await request.json();
return Response.json({ received: body });
}
This replaces:
- Express
req.body - Express
res.json()
Better understanding:
-
request= Web APIRequest -
Response.json()= built-in helper (Next.js sugar over standard Response)
Connecting Frontend to API
From your component:
const res = await fetch("/api/users");
const data = await res.json();
No need for:
- Express server
- Axios (optional)
- CORS setup (in most cases)
Dynamic API Routes
Create dynamic endpoints like this:
app/api/users/[id]/route.js
Access params:
export async function GET(request, { params }) {
return Response.json({ id: params.id });
}
Same concept as dynamic pages.
💡 Correction:
You don’t access params.id directly, you must destructure it from the second argument.
🛡️ Basic Validation & Error Handling
export async function POST(request) {
const body = await request.json();
if (!body.name) {
return Response.json(
{ error: "Name required" },
{ status: 400 }
);
}
return Response.json({ success: true });
}
You control:
- Status codes
- Errors
- Responses
Database Integration
You can connect databases directly inside API routes:
- MongoDB (e.g., with Mongoose)
- PostgreSQL (e.g., with Prisma)
API routes act like your backend layer.
Best practice:
- Reuse DB connections (avoid reconnecting on every request in dev)
🔥 Why This Is Powerful
- No separate backend setup
- Full-stack in one project
- Faster development
- Cleaner architecture
Key Takeaways
- Next.js API routes replace Express in many cases
- File-based routing makes APIs super simple
- Uses Web standard
Request&Response - Works seamlessly with frontend via
fetch() - Supports dynamic routes and full backend logic
Final Thoughts
Coming from React + Node.js, this felt weird at first…
But once it clicks, it’s actually simpler and more powerful.
We're basically writing backend logic inside the frontend project.
Thanks for reading. Feel free to share your thoughts!
Top comments (0)