A Comparative Guide to API Routes in Next.js and Express

Next.js Vs Express

In modern web development, creating robust APIs is crucial for building dynamic and interactive web applications. In this guide, we'll explore two popular approaches for building APIs: Next.js API routes and Express API routes. We'll provide code examples and compare their usage for common CRUD (Create, Read, Update, Delete) operations.

Next.js API Routes

Next.js is a powerful and production-ready React framework that allows you to seamlessly build both the frontend and backend of your applications. It's known for its simplicity and a routing system based on the file system. When you create a file in the pages folder of your Next.js project, it automatically maps to a route. The same applies to API routes.

Creating an API Route

To create an API route in Next.js, you follow a simple file-based approach. Here's an example:

// pages/api/hello.js

export default function handler(req, res) {
  res.status(200).json({ name: 'John Doe' });
}

Explanation: In this code, we've created a basic API route that responds with a JSON object. This file maps to the route http://localhost:3000/api/hello.

Making API Requests

Now, let's explore how to make GET, POST, DELETE, and PATCH requests to a Next.js API route using a simple CRUD application for managing comments.

GET Request

// pages/api/comments/index.js

import path from 'path';
import fs from 'fs';

export default async function handler(req, res) {
  let comments = [];

  try {
    comments = JSON.parse(
      fs.readFileSync(path.join(process.cwd(), 'data/comments.json'))
    );
  } catch (err) {
    console.log(err);
  }

  if (req.method === 'GET') {
    res.status(200).json(comments);
  }
}

Explanation: This code fetches comments from a JSON file and responds with them when a GET request is made to this API route.

POST Request

// pages/api/comments/index.js

if (req.method === 'POST') {
  const uniqueRandomID = uuid.v4(); // Generate a unique ID

  const newComment = {
    id: uniqueRandomID,
    text: req.body.comment,
  };

  comments.push(newComment);

  fs.writeFileSync(
    path.join(process.cwd(), 'data/comments.json'),
    JSON.stringify(comments)
  );

  res.status(201).json(comments);
}

Explanation: This code handles POST requests by generating a unique ID for a new comment, adding it to the data source, and responding with the updated list of comments.

PATCH Request

// pages/api/comments/index.js

if (req.method === 'PATCH') {
  const { id, comment } = req.body;

  try {
    const index = comments.findIndex((c) => c.id === id);
    comments[index].text = comment;

    fs.writeFileSync(
      path.join(process.cwd(), 'data/comments.json'),
      JSON.stringify(comments)
    );
  } catch (err) {
    console.log(err);
  }

  res.status(200).json(comments);
}

Explanation: This code handles PATCH requests by finding and updating a comment with a given ID in the data source.

DELETE Request

// pages/api/comments/[commentId].js

import path from 'path';
import fs from 'fs';

export default function handler(req, res) {
  const { commentId } = req.query;
  const comments = JSON.parse(
    fs.readFileSync(path.join(process.cwd(), 'data/comments.json'))
  );

  if (req.method === 'DELETE') {
    const index = comments.findIndex((c) => c.id === commentId);

    if (index !== -1) {
      comments.splice(index, 1);
      fs.writeFileSync(
        path.join(process.cwd(), 'data/comments.json'),
        JSON.stringify(comments)
      );
      res.status(200).json('Record deleted');
    } else {
      res.status(404).json('Record not found');
    }
  }
}

Explanation: This code handles DELETE requests by finding and deleting a comment with a given ID from the data source.

your-next-app-name/
├── data/
│   └── comments.json
├── node_modules/
├── pages/
│   ├── api/
│   │   └── comments.js
│   ├── comments/
│   │   └── index.js
│   ├── _app.js
│   ├── _document.js
│   ├── index.js
│   └── ...
├── public/
├── styles/
├── .gitignore
├── next.config.js
├── package.json
├── README.md
└── ...

Express API Routes

Express is a popular Node.js framework for building web applications and APIs. It provides more flexibility and is widely used for server-side development.

Creating an Express API Route

In Express, you set up API routes using manual route configuration. Here's a simplified example:

// server.js (or your entry point)

const express = require('express');
const app = express();
const port = 3000;

// Parse JSON requests
app.use(express.json());

// API route for fetching comments
app.get('/api/comments', (req, res) => {
  // Fetch comments from your data source
  res.json(comments);
});

// API route for adding a comment
app.post('/api/comments', (req, res) => {
  const newComment = {
    id: uuid.v4(),
    text: req.body.comment,
  };

  // Add newComment to your data source
  comments.push(newComment);

  res.status(201).json(newComment);
});

// API route for editing a comment
app.patch('/api/comments/:id', (req, res) => {
  const { id } = req.params;

  // Find and update the comment with the given ID
  const index = comments.findIndex((c) => c.id === id);

  if (index !== -1) {
    comments[index].text = req.body.comment;
    res.status(200).json(comments[index]);
  } else {
    res.status(404).json('Record not found');
  }
});

// API route for deleting a comment
app.delete('/api/comments/:id', (req, res) => {
  const { id } = req.params;

  // Find and delete the comment with the given ID
  const index = comments.findIndex((c) => c.id === id);

  if (index !== -1) {
    comments.splice(index, 1);
    res.status(200).json('Record deleted');
  } else {
    res.status(404).json('Record not found');
  }
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Explanation: In this Express code, we've manually set up routes for GET, POST, PATCH, and DELETE requests for managing comments.

express-server/
|-- .gitignore
|-- node_modules/
|-- package.json
|-- package-lock.json
|-- src/
|   |-- app.js
|   |-- routes/
|       |-- comments.js
|-- data/
|   |-- comments.json
|-- README.md
|-- yarn.lock

A Comparison

Now, let's compare Next.js API routes and Express API routes based on several aspects:

1. Simplicity and File-Based Routing:

  • Next.js: Offers a simple file-based routing system for both frontend and API routes.

  • Express: Requires manual route setup, offering more flexibility but with increased complexity.

2. Integration with React:

  • Next.js: Seamlessly integrates with React components, facilitating full-stack development.

  • Express: Requires additional configuration for React integration.

3. Server-Side Rendering (SSR):

  • Next.js: Provides built-in support for SSR, making it ideal for SEO-friendly applications.

  • Express: SSR requires additional setup and libraries.

4. Flexibility:

  • Next.js: Designed for simplicity and suitable for small to medium-sized applications.

  • Express: Offers more control and is suitable for a wide range of applications, including large-scale projects.

5. Learning Curve:

  • Next.js: Has a smaller learning curve, especially if you're already familiar with React.

  • Express: May have a steeper learning curve, especially for beginners.

6. Community and Ecosystem:

  • Next.js: Part of the Vercel ecosystem with a growing community and excellent deployment options.

  • Express: Has a mature and extensive ecosystem with a large community.

7. Data Validation and Middleware:

  • Next.js: Offers limited built-in support for middleware; you may need additional libraries.

  • Express: Provides a wide range of middleware options for data validation, authentication, and more.

In conclusion, the choice between Next.js API routes and Express API routes depends on your project's requirements and your familiarity with the frameworks. Next.js API routes are excellent for building React-based applications with server-side rendering and offer a simpler setup, making them ideal for smaller projects. On the other hand, Express provides greater flexibility and control, making it a solid choice for larger and more complex applications.

Ultimately, both Next.js and Express have their strengths, and the decision should be based on your specific use case and development preferences.

By understanding the differences and similarities between these two approaches, you can make an informed decision when building your next web application.