A quick guide to Next.js API routes and requests

In this post, I will show you how to create Next.js API routes and how to make requests to those APIs from Frontend.

Next.js API Routes

  • Next.js is a production-ready React Framework You can write frontend code for user interaction and backend Rest API that handles requests and responses.
  • Routing in Nex.js is based on the file system, which means when you add a file to the pages folder in your Next.js project, that file is automatically mapped to a new route. API routing follows this same pattern.
  • Anything inside the pages/api folder is an API endpoint. You can add all your API handler functions and the associated logic inside this folder( the api folder ).
  • The code you write in api folder is bundled with the server-side code, and therefore it will not affect the bundled size of the frontend ( client-side ) code

Creating an API Route

run npx create-next-app demo-next-app on the terminal to create a Next.js app.

In the default settings, there is hello.js in the pages/api folder. This file matches the http://localhost:3000/api/hello

pages/api/hello.js

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

Now run the default app, make an API request with http://localhost:3000/api/hello in the browser

You should get json response of {"name":"John Doe"} with status code 200. ( Note: you can check the status code in the Network tab in the developer tools )

When this API request handler receives an API request, req , the parameter holds the IncomingMessage and pre-built middlewares that you can make use of.

The second argument res carries the ServerResponse object and helper function that you can utilize in sending the API response.

Making API requests

Now let us see how to make GET, POST, DELETE, and PATCH requests to API. 

We are going to build a simple CRUD application for comments management.

Setting up the dev environment

We are going to use a JSON file to record data ( comments ) for this application. we will UUID package to generate UUIDs to keep a unique ID for each record in the JSON file.

Now, delete the default file create( hello.js) from the pages/api folder, and, create another folder named comments in the api folder.

Inside the comments folder, create a file name index.js, in which we will be writing the API request handler function.

Making GET request

  1. First, we are going to add some test data and add the GET request
  1. Add the following data to comets.js in the data folder

[{ "id": 1 , "text": "this is the first comment"} , { "id": 2 , "text": "this is the second comment"}, { "id": 3 , "text": "this is the third comment"}]

  1. Now, add the following code index.js file at api/commnets

api/commnets/index.js

import path from 'path';
import fs from 'fs'; //or as const fs= require('fs');
 
export default async function handler( req, res ){
 
    
    let comments = [];
	
    //reading the records from the JSON file
    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 );//sending the response
   }
} 

As you can see in the above code, we need to check the incoming request type. We use the req.method property for this.

You might wonder why I did not place the code that read the file inside the if statement. Of course, you can place it. However, as we are going to add more code to other types of HTTP requests, and reading the file is required to show the updated data, I placed this code outside of the if statement.

  1. After we add the API handler, it is time to add the front-end code so that we can see the data we read. 
  2. Create a folder named comments in the page folder, and add an index.js file. Add the following code to it.
import { useEffect, useState } from "react";
 
export default function Comments(){    
     
     const [ comments, setComments ] = useState([]);
 
 
    useEffect(()=>{  
      getComments();     
    },[]);
 
   //Making GET request to the API
    const getComments = async () =>
    {
        const response = await fetch('/api/comments');
        const comments =  await response.json();
        setComments ( comments );
    };
 
    return(
    <div>            
      { comments.map( comment => <li keys={ comment.id } >{ comment.text } </li>) }
      </div>
    )             
  }

As Next.js uses React components to display the UI and its logic, if you know React, you should be able to understand the above code quite easily.

Making POST request

We are going to add the code to handle post request

First, go to the pages/api/index.js and add the following import statement

import uuid from 'uuid';//or as const uuid = require('uuid');

Add the following code to pages/api/index.js below the code for the GET request

if( req.method === 'POST'){
    const uniqueRandomID = uuid.v4();//creating unique id
 
    const newcomment = {
        id: uniqueRandomID,
        text: req.body.comment
    }
 
    comments.push(newcomment);
	
    //writing to the incoming data to the JSON file
    fs.writeFileSync( path.join( process.cwd(), 'data/comments.json'), JSON.stringify(comments));
   
    res.status( 201 ).json( comments);
  }

Add the code for UI in the return statement of the pages/comments/index.js file

return(
 <div>            
      { comments.map( comment => <li keys={ comment.id } >{ comment.text }  </li>) }
 
 <input ref={inputEl} type='text' onChange={ (e) => setState(e.target.value) }/>
            <button onClick={ addComments }> add comment</button>
      </div>
 )

As you can see I have added the input tag and a button with the relevant function. We need to add the setState() and addComments() functions.

Now in the same file add the following code

const [ state, setState ] = useState([]);
 const inputEl = useRef(null);
 
//post request to the API
const addComments = async () => {
    await fetch('/api/comments',
                {
                    method:'POST',
                    body: JSON.stringify( { comment: state }),
                    headers: {
                        'Content-Type': 'application/json'
                    }
                }
               
                );
               
                getComments();//called Get request to refresh the display
                inputEl.current.value = '';
             
  }

I use the state to hold the input entered into the text box. So, this state container the new comment entered. 

Then, I used that state in the post request. The new comment will be added to the comments.json file. After that, because I need to display all the comments again on the browser I called the getComments() method, which makes the GET request.

Making PATCH request

For the patch request, we need to update the UI again. We are going to add an Edit button in the return statement of the React component of  pages/comments/index.js 

 

return(
     <div>            
      { comments.map( comment =>
      <li keys={ comment.id } >{ comment.text }
           <button onClick={ () => editComment( comment.id , comment.text) }>edit</button>
      </li>)
      }
 
            <input ref={inputEl} type='text' onChange={ (e) => setState(e.target.value) }/>
            <button onClick={ addComments }> add comment</button>
      </div>
    )  

When you click on the edit button, the editComments() function that takes the comment ID and the comment text executes.

we have not yet implemented the setUIForUpdates function, which prepares the UI for updating a selected comment

const setUIForUpdates =  async ( id , text ) => {
    setCurentId( id ); //set the id of the selected commnet       
    setState( text );//set the state with the comment text
    inputEl.current.value = text;//change the value of the input element           
}

The above code has a new function named setCurentId. This function sets the new State currentId work as a container to hold the selected comment. Add this new state along with other states we used before.

    const [ comments, setComments ] = useState([]);
    const [ state, setState ] = useState([]);
    const inputEl = useRef(null);
    const [ currentId , setCurentId ] = useState(null);

Now add the code that makes the PATCH request

const UpdateComment = async () =>{
            const response = await fetch('/api/comments',
            {
                method:'PATCH',
                body: JSON.stringify( { id: currentId, comment: state }),
                headers: {
                    'Content-Type': 'application/json'
                }
            }
           
            );
            setState(response.json())
            getComments();
            inputEl.current.value = '';
}

First, add update handler code for the patch request in the api/comments.js.

if( req.method === 'PATCH'){      
           
        const { id , comment } =  req.body;
       
        try{
         const index = comments.findIndex( comment => comment.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 );
}

As you can see in the above code, we receive an id and comment from the req.body. We have not added the code responsible for this in the frontend code. 

Let’s add the code that makes the PATCH request. 

const UpdateComment = async () =>{
            const response = await fetch('/api/comments',
            {
                method:'PATCH',
                body: JSON.stringify( { id: currentId, comment: state }),
                headers: {
                    'Content-Type': 'application/json'
                }
            }
           
            );
 
            setState(response.json())            
            getComments();// to get the updated record from comments.js
            clear(); //set currentID to null & clear the input text box
}

Making DELETE request

We are going to handle the DELETE request differently. We are going to send the Id of the common as a query string. 

We can do this by creating a dynamic API route in Next.js. You can read more on Next.js dynamic routes in this post.

  1. In the pages/api/comment , create a file named [commentId].js
  2. Add the following handler function
import path from 'path';
import fs from 'fs' //or' const fs= require('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( comment => ( comment.id ===  commentId) );      
        comments.splice(  index , 1);//deleting the record
        fs.writeFileSync( path.join( process.cwd(), 'data/comments.json'), JSON.stringify(comments));
        res.status( 200 ).json( "Record deleted");
    }   
}

 comments.findIndex() method is to find the index of the comments array that satisfies the ( comment.id ===  commentId ) condition. The commentId is the ID of the comment that you choose to delete. This ID passes from the Frontend to the Backend via the query object.

Summary

We discussed what API routes are in Next.js, how to create API routes, and how to make API requests such as GET, POST, PATCH, and DELETE important in CRUD applications.

Related Post

Leave a Reply

Your email address will not be published. Required fields are marked *