Next.js API routes — build your first endpoint step by step
Learn how to create API routes in Next.js 15. Handle GET and POST requests, validate input, return JSON, and connect to a database. Beginner-friendly with full code examples.
What are API routes?
API routes let you build a backend inside your Next.js project. Instead of setting up a separate server, you create files in app/api/ and Next.js turns them into endpoints. Your frontend can call them, and so can external services (like Stripe webhooks).
Think of it this way: app/api/hello/route.ts becomes https://yourapp.com/api/hello. That's it.
Step 1: Create a GET endpoint
Create the file app/api/hello/route.ts. Add this code:
// app/api/hello/route.ts
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({
message: "Hello from the API!",
time: new Date().toISOString(),
});
}Open http://localhost:3000/api/hello in your browser. You'll see the JSON response. That's your first API endpoint.
Step 2: Handle POST requests
Most forms send POST requests. Here's how to read the body and validate the input:
// app/api/contact/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const body = await req.json();
// Validate input
if (!body.email || !body.message) {
return NextResponse.json(
{ error: "Email and message are required" },
{ status: 400 }
);
}
// Do something with the data
// e.g. save to database, send email, etc.
console.log("New contact:", body.email, body.message);
return NextResponse.json({ success: true });
}Key takeaways: use await req.json() to parse the body, always validate before doing anything, and return proper status codes (400 for bad input, 200 for success).
Step 3: Call it from the frontend
In your React component, use fetch to call the endpoint:
// In any client component
const handleSubmit = async () => {
const res = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: "user@example.com",
message: "Hello!",
}),
});
const data = await res.json();
if (res.ok) {
alert("Sent!");
} else {
alert(data.error);
}
};Notice: no absolute URL needed. /api/contact works because your frontend and API live in the same project.
Step 4: Add authentication
To protect an endpoint (only logged-in users), check the session:
// app/api/profile/route.ts
import { createClient } from "@/libs/supabase/server";
import { NextResponse } from "next/server";
export async function GET() {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return NextResponse.json(
{ error: "Not logged in" },
{ status: 401 }
);
}
return NextResponse.json({ email: user.email });
}Important in Next.js 15: createClient() is async on the server — always use await. Learn more in our Supabase Auth guide.
Common patterns
- GET — read data (list items, get profile)
- POST — create data (submit form, start checkout)
- PUT / PATCH — update data (edit profile)
- DELETE — remove data (delete account)
Export the function name that matches the HTTP method: export async function GET(), export async function POST(), etc. You can have multiple methods in the same route.ts file.
20+ API routes ready to use
Delfy comes with API routes for Stripe checkout, webhooks, user profiles, community invites, and more — all with proper validation and error handling.
See what's included