Next.js Example

API Routes

Build your API endpoints with Next.js API routes

Basic API Routes

Create simple API endpoints with Next.js

Basic API Route

// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'

type Data = {
  name: string
  message: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== 'GET') {
    return res.status(405).json({ 
      name: 'Method Not Allowed',
      message: 'Only GET requests are allowed'
    })
  }

  res.status(200).json({ 
    name: 'John Doe',
    message: 'Hello from Next.js!'
  })
}

CORS and Headers

// pages/api/cors-example.ts
import type { NextApiRequest, NextApiResponse } from 'next'
import Cors from 'cors'

// Initialize the cors middleware
const cors = Cors({
  methods: ['GET', 'POST', 'OPTIONS'],
})

// Helper method to wait for middleware
function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: Function) {
  return new Promise((resolve, reject) => {
    fn(req, res, (result: any) => {
      if (result instanceof Error) {
        return reject(result)
      }
      return resolve(result)
    })
  })
}

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  // Run the middleware
  await runMiddleware(req, res, cors)

  // Set custom headers
  res.setHeader('Cache-Control', 's-maxage=86400')

  // Rest of the API logic
  res.json({ message: 'This is a CORS-enabled endpoint' })
}

Key Points

  • File-based routing in the api directory
  • Built-in TypeScript support
  • HTTP method handling
  • Custom headers and CORS configuration

Dynamic API Routes

Create dynamic API endpoints with route parameters

Dynamic Route Handler

// pages/api/posts/[id].ts
import type { NextApiRequest, NextApiResponse } from 'next'
import { prisma } from '../../../lib/prisma'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { id } = req.query

  switch (req.method) {
    case 'GET':
      try {
        const post = await prisma.post.findUnique({
          where: { id: String(id) }
        })
        if (!post) {
          return res.status(404).json({ message: 'Post not found' })
        }
        return res.status(200).json(post)
      } catch (error) {
        return res.status(500).json({ message: 'Error fetching post' })
      }

    case 'PUT':
      try {
        const updatedPost = await prisma.post.update({
          where: { id: String(id) },
          data: req.body
        })
        return res.status(200).json(updatedPost)
      } catch (error) {
        return res.status(500).json({ message: 'Error updating post' })
      }

    case 'DELETE':
      try {
        await prisma.post.delete({
          where: { id: String(id) }
        })
        return res.status(204).end()
      } catch (error) {
        return res.status(500).json({ message: 'Error deleting post' })
      }

    default:
      res.setHeader('Allow', ['GET', 'PUT', 'DELETE'])
      return res.status(405).json({ message: `Method ${req.method} Not Allowed` })
  }
}

Catch All Routes

// pages/api/posts/[...slug].ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { slug } = req.query
  // slug will be an array of path segments
  // e.g. /api/posts/2023/01/hello -> ['2023', '01', 'hello']

  res.status(200).json({
    slug: slug,
    path: slug.join('/'),
    query: req.query
  })
}

Key Points

  • Dynamic route parameters with [param]
  • Catch-all routes with [...param]
  • RESTful API patterns
  • Database integration

API Middleware

Add middleware to your API routes

Authentication Middleware

// middleware/withAuth.ts
import { NextApiRequest, NextApiResponse } from 'next'
import { getToken } from 'next-auth/jwt'

export function withAuth(handler: any) {
  return async (req: NextApiRequest, res: NextApiResponse) => {
    try {
      const token = await getToken({ req })

      if (!token) {
        return res.status(401).json({ 
          error: 'Unauthorized' 
        })
      }

      // Add user to request object
      req.user = token

      // Call the original handler
      return handler(req, res)
    } catch (error) {
      return res.status(500).json({ 
        error: 'Authentication error' 
      })
    }
  }
}

// Usage in API route
import { withAuth } from '../../../middleware/withAuth'

export default withAuth(async function handler(req, res) {
  // This route is now protected
  const user = req.user
  res.json({ message: `Hello ${user.name}!` })
})

Rate Limiting

// middleware/rateLimit.ts
import rateLimit from 'express-rate-limit'
import { NextApiRequest, NextApiResponse } from 'next'

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
})

export function withRateLimit(handler: any) {
  return async (req: NextApiRequest, res: NextApiResponse) => {
    try {
      await new Promise((resolve, reject) => {
        limiter(req, res, (result: any) => {
          if (result instanceof Error) {
            return reject(result)
          }
          resolve(result)
        })
      })
      
      return handler(req, res)
    } catch (error) {
      return res.status(429).json({
        error: 'Too Many Requests'
      })
    }
  }
}

Key Points

  • Authentication and authorization
  • Rate limiting and throttling
  • Error handling
  • Request validation