Next.js has evolved into one of the most powerful frameworks for building modern web applications. With its built-in API Routes, you can easily create secure backend endpoints for fetching data, managing users, or handling webhooks, all within the same codebase. This makes your app faster, simpler, and easier to maintain.
What Are API Routes in Next.js?
API routes allow you to build RESTful APIs directly within your Next.js app—no separate backend needed.
You can use them for:
- Fetching or posting data
- Connecting to databases (e.g., MongoDB, PostgreSQL)
- Authenticating users
- Handling webhooks or background tasks
In Next.js 15+ (App Router), you can create API routes using the app/api/ directory.
Example structure:
my-next-app/
+-- app/
¦ +-- api/
¦ ¦ +-- hello/
¦ ¦ +-- route.ts
¦ +-- page.tsx
¦ +-- layout.tsx
Step 1: Setting Up Your Next.js Project
If you don’t have a Next.js app yet, create one with:
npx create-next-app@latest my-next-api
cd my-next-api
Choose TypeScript if prompted.
Then start your dev server:
npm run dev
You can now visit your app at http://localhost:3000
Step 2: Creating Your First API Endpoint
Inside the app directory, create a new folder named api/hello, and inside it, a file called route.ts (or route.js if not using TypeScript):
mkdir -p app/api/hello
touch app/api/hello/route.ts
Now, add the following code:
// app/api/hello/route.ts
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({ message: "Hello from your first API endpoint!" });
}
Now, visit:
http://localhost:3000/api/hello
You’ll see:
{ "message": "Hello from your first API endpoint!" }Step 3: Handling Different HTTP Methods
Next.js API routes support multiple methods: GET, POST, PUT, DELETE, etc.
You can define one or more exports per route:
// app/api/user/route.ts
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({ user: "Alan", status: "active" });
}
export async function POST(request: Request) {
const body = await request.json();
return NextResponse.json({ received: body });
}
How it works:
- GET() runs when you call /api/user with a GET request
- POST() runs when you send data (like a form or JSON) with a POST request
Try sending a POST request using a tool like cURL or Postman:
curl -X POST http://localhost:3000/api/user \
-H "Content-Type: application/json" \
-d '{"name":"John"}'
Response:
{ "received": { "name": "John" } }Step 4: Handling Query Parameters
You can read query strings (like /api/greet?name=Alan) using the URL API:
// app/api/greet/route.ts
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const name = searchParams.get("name") || "Guest";
return NextResponse.json({ message: `Hello, ${name}!` });
}
Visiting /api/greet?name=Alan will return:
{ "message": "Hello, Alan!" }Step 5: Connecting to a Database (Optional)
You can use any Node.js database library, such as Prisma, Mongoose, or Drizzle ORM.
Example with Prisma:
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
export async function GET() {
const users = await prisma.user.findMany();
return NextResponse.json(users);
}Make sure your Prisma client is configured in lib/prisma.ts.
Returning Custom Status Codes
You can also set custom HTTP status codes:
export async function GET() {
return NextResponse.json(
{ error: "Not Found" },
{ status: 404 }
);
}Best Practices
- Use TypeScript for strong typing
- Validate input using Zod or Yup
- Keep endpoints small and focused
- Handle errors gracefully
- Use environment variables for secrets (process.env.API_KEY)
To read more about A Complete Beginner’s Guide to Getting Started with Next.js, refer to our blog, A Complete Beginner’s Guide to Getting Started with Next.js.