How Do We Create Custom APIs with Next.js?

Introduction

Next.js supports creating API routes, allowing you to build backend functionality within your application. Here’s a quick guide to setting up and using custom APIs.

1. Setting Up Basic API Routes

API routes are defined in the pages/api directory. Each file in this directory maps to a unique endpoint.

Example. Basic API Route

Create File

mkdir -p pages/api
touch pages/api/hello.js

Define Handler

// pages/api/hello.js
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello, World!' });
}

Testing: Access the endpoint at http://localhost:3000/api/hello.

2. Handling Different HTTP Methods

You can handle various HTTP methods like GET, POST, PUT, and DELETE.

Example. Handling Methods

// pages/api/posts.js
export default function handler(req, res) {
  if (req.method === 'GET') {
    res.status(200).json({ message: 'GET request received' });
  } else if (req.method === 'POST') {
    res.status(201).json({ message: 'POST request received', data: req.body });
  } else {
    res.setHeader('Allow', ['GET', 'POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

3. Working with Query Parameters

Access query parameters from req. query.

Example. Query Parameters

// pages/api/posts/[id].js
export default function handler(req, res) {
  const { id } = req.query;
  res.status(200).json({ message: `Post ID: ${id}` });
}

4. Connecting to a Database

Connect to a database and perform CRUD operations.

Example SQLite Integration

Install SQLite

npm install sqlite3

Set Up Database Connection

// pages/api/posts.js
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
async function openDb() {
  return open({
    filename: './mydatabase.db',
    driver: sqlite3.Database,
  });
}
export default async function handler(req, res) {
  const db = await openDb();
  if (req.method === 'GET') {
    const posts = await db.all('SELECT * FROM posts');
    res.status(200).json(posts);
  } else if (req.method === 'POST') {
    const { title, content } = req.body;
    await db.run('INSERT INTO posts (title, content) VALUES (?, ?)', [title, content]);
    res.status(201).json({ message: 'Post created' });
  } else {
    res.setHeader('Allow', ['GET', 'POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

5. Adding Middleware

Implement custom middleware for request processing.

Example. Logging Middleware

// pages/api/posts.js
function logRequest(req, res, next) {
  console.log(`Request received: ${req.method} ${req.url}`);
  next();
}
export default async function handler(req, res) {
  logRequest(req, res, () => {
    // Actual API handling code
  });
  // Handle requests here
}

6. Authentication

Protect your API routes with authentication.

Example. Simple Auth Check

// pages/api/posts.js
export default async function handler(req, res) {
  const token = req.headers.authorization;
  if (token !== 'Bearer my-secret-token') {
    return res.status(403).json({ message: 'Forbidden' });
  }
  // Handle requests if authenticated
}

7. Testing API Routes

Test your API routes using frameworks like Jest.

Example. Jest Test

Install Jest

npm install --save-dev jest

Write Test

// __tests__/api/posts.test.js
import { createMocks } from 'node-mocks-http';
import handler from '../../pages/api/posts';
test('returns a 200 status code for GET requests', async () => {
  const { req, res } = createMocks({
    method: 'GET',
  });
  await handler(req, res);
  expect(res._

Run Tests

npm test

Summary

Next.js makes it easy to create custom APIs within your application. By defining API routes, handling different HTTP methods, working with query parameters, and integrating with databases, you can build robust backend functionalities. Adding middleware, implementing authentication, and testing further enhance your APIs, making Next.js a powerful tool for full-stack development.