Table of Contents
- Introduction
- API Architectures: REST vs GraphQL
- Where APIs Live in Next.js App Router
- Understanding Server vs Edge Runtime for APIs
- Structuring REST APIs in Next.js
- Creating Typed REST API Handlers with TypeScript
- Using Middleware in REST APIs
- Introduction to GraphQL in Next.js
- Setting Up Apollo Server in App Router
- Integrating URQL with React Clients
- Choosing Between REST and GraphQL
- Best Practices for Scalable APIs in Next.js
- Conclusion
1. Introduction
Next.js is not just a frontend framework. With App Router and React Server Components, it can power robust, scalable backends — including both RESTful APIs and GraphQL.
In this module, we explore:
- How to structure APIs in the App Router.
- The differences between REST and GraphQL in the context of modern frontend-heavy apps.
- The tradeoffs and integrations using Apollo, URQL, and native handlers.
2. API Architectures: REST vs GraphQL
Feature | REST | GraphQL |
---|---|---|
Data fetching | Multiple endpoints | Single endpoint |
Flexibility | Rigid, defined by server | Client controls query shape |
Versioning | Often required | Avoided due to flexible schema |
Over/Under-fetching | Common | Solved via query specificity |
Tooling | Native browser tools, Postman | GraphiQL, Apollo DevTools |
You can use either — or both — in your Next.js app depending on requirements.
3. Where APIs Live in Next.js App Router
With the App Router, APIs are usually created in the special app/api
directory.
Example structure:
bashCopyEditapp/
└── api/
└── users/
├── route.ts
└── auth/
├── login/
├── route.ts
Each route.ts
file exports a handler for the HTTP method (GET, POST, etc).
4. Understanding Server vs Edge Runtime for APIs
In App Router, routes can be configured to run on:
- Default (Node.js) – More powerful, supports native libraries.
- Edge Runtime – Faster cold starts, minimal latency, ideal for auth or analytics APIs.
You can set it with a directive:
tsCopyEditexport const runtime = 'edge'; // or 'nodejs'
Use Edge for lightweight operations and Node.js for DB-heavy ones.
5. Structuring REST APIs in Next.js
Create a RESTful endpoint in app/api/users/route.ts
:
tsCopyEdit// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(req: NextRequest) {
const users = await fetchUsersFromDB();
return NextResponse.json(users);
}
export async function POST(req: NextRequest) {
const body = await req.json();
const user = await createUser(body);
return NextResponse.json(user, { status: 201 });
}
This mimics classic RESTful semantics:
GET /api/users
– fetch usersPOST /api/users
– create user
6. Creating Typed REST API Handlers with TypeScript
Typing request/response formats improves API maintainability.
tsCopyEdittype CreateUserInput = {
name: string;
email: string;
};
export async function POST(req: NextRequest) {
const body: CreateUserInput = await req.json();
const user = await createUser(body);
return NextResponse.json(user);
}
Use Zod or Yup for runtime validation.
7. Using Middleware in REST APIs
Middleware is useful for:
- Authentication
- Logging
- CORS handling
Example of a basic auth middleware:
tsCopyEdit// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export function middleware(req: NextRequest) {
const token = req.cookies.get('token');
if (!token) {
return NextResponse.redirect(new URL('/login', req.url));
}
return NextResponse.next();
}
Apply middleware selectively with a matcher.
8. Introduction to GraphQL in Next.js
GraphQL is an alternative API paradigm where clients ask for exactly the data they need.
To use GraphQL in Next.js, install a server like Apollo:
bashCopyEditnpm install @apollo/server graphql
9. Setting Up Apollo Server in App Router
Create an Apollo server handler:
tsCopyEdit// app/api/graphql/route.ts
import { startServerAndCreateNextHandler } from '@as-integrations/next';
import { ApolloServer } from '@apollo/server';
import { NextRequest } from 'next/server';
const typeDefs = `
type User {
id: ID!
name: String!
}
type Query {
users: [User!]!
}
`;
const resolvers = {
Query: {
users: () => [{ id: 1, name: 'Alice' }],
},
};
const server = new ApolloServer({ typeDefs, resolvers });
const handler = startServerAndCreateNextHandler<NextRequest>(server);
export { handler as GET, handler as POST };
This sets up /api/graphql
as a single GraphQL endpoint.
10. Integrating URQL with React Clients
URQL is a lightweight GraphQL client alternative to Apollo Client.
bashCopyEditnpm install urql graphql
Then create a client and wrap your app:
tsCopyEdit// providers/UrqlProvider.tsx
import { createClient, Provider } from 'urql';
const client = createClient({
url: '/api/graphql',
});
export const UrqlProvider = ({ children }) => (
<Provider value={client}>{children}</Provider>
);
Now, you can use the useQuery
hook to fetch data.
11. Choosing Between REST and GraphQL
Use Case | Prefer REST | Prefer GraphQL |
---|---|---|
Simple CRUD APIs | ✅ Yes | ❌ Overkill |
Highly nested or customizable UI | ❌ Verbose | ✅ Query tailoring |
Browser caching | ✅ REST friendly | Needs manual setup |
Real-time updates | ❌ Extra config | ✅ Subscriptions supported |
Hybrid architectures using REST for auth and GraphQL for complex queries are common.
12. Best Practices for Scalable APIs in Next.js
- Use App Router with
app/api
for serverless APIs. - Choose Node.js runtime for DB-heavy ops, Edge for lightweight logic.
- Use typed inputs and outputs (Zod, TypeScript interfaces).
- Authenticate with middleware or session tokens.
- Use GraphQL only when needed; it introduces schema and tooling overhead.
- For GraphQL, prefer lightweight clients (URQL) unless caching and dev tools are critical.
13. Conclusion
Next.js offers a flexible, scalable platform for building full-stack applications — including modern APIs. Whether you prefer traditional REST endpoints or opt for GraphQL flexibility, you can implement them efficiently using App Router and server capabilities.
By understanding runtime environments, API structure, and tooling, you’ll be equipped to build scalable, secure, and performant backends directly within your React-powered Next.js app.