Dealing with errors in Next.js api: Best practices

When it comes to organizing code in Next.js api handlers, what is the most effective practice? In a video I watched, the presenter suggested placing all REST routes in two specific files:

  1. pages/api/users/index.ts would manage operations that do not require an id, such as

    GET /api/users - retrieve all users
    and
    POST pages/api/users - add a new user

  2. pages/api/users/[id].ts would handle operations that involve an id, for instance,

    GET api/users/1 - fetch user by id
    , PUT /api/users/1 - update user, and
    DELETE /api/users/1 - remove a user

This approach would consolidate a significant amount of code into just these 2 files, managed through a switch case statement. How should this code be structured effectively?

Each case statement necessitates its own try catch block for handling database queries, leading to repetition. While wrapping the entire switch with a single encompassing try catch could avoid duplicating code, each case might require distinct treatment. Another option is enclosing the cases within a higher-order function with a single try catch, though this may not be ideal either.

In addition, future implementation of route protection using withProtected and withRole middleware might pose a challenge due to the current setup where multiple APIs are handled within a single handler call.

What would be the best approach to organize this structure efficiently? Are there any comprehensive examples available for reference?


// pages/api/users/index.ts

import { NextApiRequest, NextApiResponse } from 'next';
import { hash } from 'bcryptjs';
import prisma from 'lib/prisma';

/**
 * POST /api/users
 * Required fields in body: name, username, email, password
 */

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<void> {
  const { method, body } = req;

  switch (method) {
    case 'GET':
      // get all
      // another try catch block?
      const users = await prisma.user.findMany();
      res.status(200).json({ users });

      break;
    case 'POST':
      // create
      try {
        const { name, username, email, password: _password } = body;

        // todo: validate...

        const _user = await prisma.user.findFirst({
          where: { email },
        });

        if (_user)
          throw new Error(`The user with email: ${email} already exists.`);

        const password = await hash(_password, 10);
        const user = await prisma.user.create({
          data: {
            name,
            username,
            email,
            password,
          },
        });

        res.status(201).json({ user });
      } catch (error) {
        res.status(500).json({ error });
      }

      break;
    default:
      res.setHeader('Allow', ['GET', 'POST']);
      res.status(405).end(`Method ${method} Not Allowed`);
  }
}

Answer №1

This is my approach to handling tasks with an explanation covering the "method not allowed" scenario.

See below for a snippet of code extracted from my ongoing project:

src/somewhere/globalAPICall.js

/**
 * 
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {{[key: string]: () => Promise<void>}} actions 
 */
export default async function globalAPICall(req, res, actions) {
    try {
      const method = req.method
      // check if there is an action matching the request.method; otherwise, throw a "method not allowed" error
      if (!Object.keys(actions).includes(method)) {
        console.log('method not allowed')
        throw new MyError('Method not allowed', 405)
      }
      // execute the action corresponding to the request.method
      await actions[method]()
    } catch(err) {
      if (err instanceof MyError) {
        res.status(err.code).send(err.message);
      } else {
        res.status(500).send("Internal server error");
      }
    }
}

src/pages/api/users.js


/**
 *
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 */
export default async function handler(req, res) {

    async function POST() {
      const { email, password, username } = req.body;
      if (!username) {
        throw new MyError('Username required', 400)
      }
      await CreateUser(email, password, username)
      res.status(201).send();
    }

    async function GET() {
      const result = await ListUsers()
      res.status(200).json(result);
    }

    await globalAPICall.js(req, res, {POST, GET})

}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with GMap Compatibility on Specific Web Browsers

I'm currently facing an issue with implementing gMap [1] on a website. The map is functioning properly in Chrome and Safari, but it fails to display in Firefox, IE, and Opera (latest versions of all). I have ensured that the Google Map API key loads a ...

Exploring JSON Object with ng-disabled in AngularJS

One unique feature of my web application is the ability to add new users (username + password) through a form. To prevent duplicate usernames, I have a JSON object called l_usernames defined in a controller named UsersController. This object contains all u ...

Tips for using jQuery to create a delete functionality with a select element

I'm relatively new to utilizing jquery. I decided to tackle this project for enjoyment: http://jsbin.com/pevateli/2/ My goal is to allow users to input items, add them to a list, and then have the option to select and delete them by clicking on the t ...

A Nextjs application accessing configuration settings from an Azure App Service

We are currently working on a nextjs project that has been built using docker and deployed to Azure App Service (container). Despite setting up configuration values within App Service, we are experiencing issues while trying to access them. Here are some ...

Troubleshooting Vue.js and Laravel: Having trouble loading new image files, while the older ones load just fine

Currently, I am working on a project that involves Vue.js and Laravel. In this setup, Vue is located inside the resources/js directory of the Laravel project. My current issue revolves around loading an image from the resources/js/assets directory. While ...

Why does Vue 3 refuse to refresh an <img> element, while it successfully refreshes all other components within the same component?

In my current Vue project, I have set up multiple link cards (<a class='card'></a>) inside a "deck" container (<div class='deck'></div>). The implementation for these elements is relatively straightforward: <s ...

Extracting the token from a cookie for a server-side API request in Next JS: A guide

After following the guidelines provided in the Next.js repository to configure an Apollo client, the resulting code structure is as follows: import { ApolloClient } from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory&a ...

Failed to convert value to a string

I'm dealing with a frustrating issue and I just can't seem to figure it out. The error message I'm getting is Cast to string failed for value "{}" at path "post". { "confirmation": "fail", "message": { "message": "Cast to string fai ...

EJS unable to display template content

I am having an issue with rendering a template that contains the following code block: <% if(type === 'Not Within Specifications'){ %> <% if(Length !== undefined) { %><h5>Length: <%= Length %> </h5> <% ...

Encountering a blank response object when making a POST request with the fetch API

I'm new to working with express/Node.js. I recently attempted to send a request including the current user's location (longitude and latitude using the geolocation API). /public/index.html <!DOCTYPE > <html> <head> <me ...

Attempting to modify information in vue.js

I have been facing an issue with overriding data in the App.vue component. It seems that every time I try to update the data in my main.js file, the component still takes the default data. I am working with webpack as well. App.vue <template> & ...

Sending the selected object from a dropdown in React back to its parent component

I have encountered a few issues with my React dropdown component. My main goal is to pass the selected object from the dropdown back to the Parent component. Currently, the dropdown list is functional and I am able to pass {this.state.selectedUser} back to ...

The organizational chart function was functioning properly on the local environment, however, it failed to work after deployment, resulting in the error message "jQuery(

Currently, I am in the process of creating an organizational chart using the Angular library called orgchart. The code I have developed works fine on my local machine, but when we deploy it onto our nginx server, we encounter an error related to jQuery. T ...

Adding content to a text field and then moving to the next line

I am looking to add a string to a text area, followed by a new line. After conducting some research, here are the methods I have attempted so far but without success: function appendString(str){ document.getElementById('output').value += st ...

problem encountered with data not being received by Java servlet

I am having difficulty sending canned json data to a servlet using jquery ajax on the Google App Engine. Despite catching the call in the debugger and inspecting the request, I consistently find that the parameters map is null... Any assistance would be g ...

Step-by-step guide on how to showcase elements within v-for by clicking on them

In my data array, only the first element is visible by default. Clicking on either the YES or NO button will display the element with the corresponding id of yes_section or no_section (depending on which button was clicked). For instance, if we click on ...

By clicking the button, you can add an item to select2

Utilizing the select2 tag style in my Drupal YAML form allows users to easily locate and add various items. These items come with both images and titles on the same page, accompanied by a button next to each image. My goal is to enable users to click the b ...

Notify when a certain element's ID is visible on the screen using the jQuery appear method

Having trouble getting this plugin to cooperate: https://github.com/morr/jquery.appear I attempted to reference the plugin from a CDN using Ajax: http://cdnjs.cloudflare.com/ajax/libs/jquery.appear/0.3.3/jquery.appear.js Why isn't it working as expe ...

There seems to be a glitch in the AJAX code

I am currently working on a project that involves displaying categories and subcategories of products on a webpage. When users click on either a category or a subcategory, AJAX is used to send the selected item to a php script which then generates HTML cod ...

Authentication for REST API using JavaScript in the web browser

Currently, I am experimenting with creating a stateless, REST-based API that I intend to use from multiple sources, one of which will be a single-page Javascript web application. The goal is to have a unified API for different clients, even those developed ...