Building Scalable APIs with Next.js: REST vs GraphQL in App Router

Table of Contents

  1. Introduction
  2. API Architectures: REST vs GraphQL
  3. Where APIs Live in Next.js App Router
  4. Understanding Server vs Edge Runtime for APIs
  5. Structuring REST APIs in Next.js
  6. Creating Typed REST API Handlers with TypeScript
  7. Using Middleware in REST APIs
  8. Introduction to GraphQL in Next.js
  9. Setting Up Apollo Server in App Router
  10. Integrating URQL with React Clients
  11. Choosing Between REST and GraphQL
  12. Best Practices for Scalable APIs in Next.js
  13. 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

FeatureRESTGraphQL
Data fetchingMultiple endpointsSingle endpoint
FlexibilityRigid, defined by serverClient controls query shape
VersioningOften requiredAvoided due to flexible schema
Over/Under-fetchingCommonSolved via query specificity
ToolingNative browser tools, PostmanGraphiQL, 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 users
  • POST /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 CasePrefer RESTPrefer GraphQL
Simple CRUD APIs✅ Yes❌ Overkill
Highly nested or customizable UI❌ Verbose✅ Query tailoring
Browser caching✅ REST friendlyNeeds 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.