Tutorials

Understanding Protected Routes in Next.js 14

August 20th, 2024 | By Jay Raj | 10 min read

An application has many sections to it. Certain parts of the application will be restricted only to authorized users or to users with special privileges and some will be public. Publicly available parts need not be validated but requests to authorized sections need to be validated.


In this tutorial, we'll be learning about `middlewares` and how it helps to control access to different parts of the application.


Getting Started with Understanding Protected Routes in Next.js 14


We'll be using Next.js 14 for this tutorial so please make sure you have Node.js 18 or later. From your terminal prompt type in the following command,


npx create-next-app@latest


As seen in the above command, we are using `create-next-app` to create our Next.js project. The above command prompts you to install the `create-next-app` package. Type in yes,


[email protected]
Ok to proceed? (y)


Once you type in `y` it installs the package. Next, it will ask a couple of questions related to your project. Type in the following responses:


√ What is your project named? ... next-protected
√ Would you like to use TypeScript? ... Yes
√ Would you like to use ESLint? ... No
√ Would you like to use Tailwind CSS? ... No
√ Would you like to use `src/` directory? ... Yes
√ Would you like to use App Router? (recommended) ... Yes
√ Would you like to customize the default import alias (@/*)? ... No

Once done you will have your Next.js 14 project created with some boilerplate code.


Navigate to the project directory and try to run the project,


cd next-protected
npm run dev


Navigate your browser to http://localhost:3000/ and you will have the application running.


Creating an API Route


Let's start by creating an API route that returns some JSON data as a response. For creating an API route, navigate to your `src/app` folder and create an `api` folder which will contain the code for your API endpoints. Inside `api` create a folder called `photos` and inside `photos` create a file called `route.ts`.


Now this `route.ts` file serves as the root file for the API endpoints of `photos`. Inside the `route.ts` file let's add a `GET` method which will handle the GET request to the `api/books/` endpoint.


const GET = async () => {
    const data = await fetch('https://jsonplaceholder.typicode.com/photos').then(res => res.json());
    return Response.json({"status":200, "data": data});
}
export {GET}


As seen in the above `GET` method, we are querying another external API using fetch and returning the response as result for `/api/books` GET request.


Save the above changes and restart the application.


npm run dev


Try hitting the endpoint `http://localhost:3000/api/books` either using POSTMAN client or simply in your web browser and it will return an API response.


The above endpoint is public and no matter whoever tries to access the above endpoint will get the request response.


Now let's try to make the above API route protected.


Creating a Protected API Route


For protecting an API route or request, Next.js 14 provides `Middleware`. A Middleware is something that comes in between your request and before it hits the route.

 

From the official documentation,


"Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly."


For creating a middleware go to your `src` folder and create a file called `middleware.ts`. Inside the `middleware.ts` file create a method called `middleware` and export it.


import { NextRequest } from "next/server";

const middleware = (request : NextRequest) => {
    console.log('request is ', request);
}

export default middleware;


Save the above changes and try to hit the API endpoint `http://localhost:3000/api/books`. Once the API returns the response, check the project terminal for the console log from the middleware function.


As seen from the logs, before hitting the endpoint the request passed through the middleware function. So let's say we are only allowing requests to the endpoint with a particular API key in headers. You can validate it in the middleware and reject invalid requests.


typescript
import { NextRequest, NextResponse } from "next/server";

const middleware = (request : NextRequest) => {
    const {headers} = request;
    if(headers.get('x-api-key') !== "zoomba"){
        return Response.json(null,{status:400, statusText:'Unauthorized request'});
    }
}

export default middleware;


In the above code, we are checking for `headers` from the request. Inside the headers we are checking for `x-api-key` which must be passed with a value `zoomba` for it to be a valid request. In case the request doesn't have a valid `x-api-key` it will return 400 responses. This way the API route is protected against invalid requests.


Save the above changes and try to make an API request to the endpoint `http://localhost:3000/api/books`. 


If you try to make a GET request from the browser to `http://localhost:3000/api/books` you will get `null` response since the required header `x-api-key` is not being passed.

status-400-bad-request-example

Next, let's try to make a GET request to the endpoint with the required header `x-api-key`. 


status-200-ok-example


I'm using a POSTMAN client for testing the API endpoint, you can use the same or any other.


Note: The `x-api-key` being used is a hard-coded one. It's just for this tutorial. You can use a dynamic encrypted string that can be encrypted on the client side and decrypted on the server side to make sure it's authentic.


Next up, let's try adding Middleware logic to Next.js pages.


Adding Middleware to Page


Some pages might not be available to all users. And we can handle page-level restrictions too in the `middleware.ts` file.


Try to navigate to http://localhost:3000/ and the application will return `null`. It's because we have added a generic restriction to the middleware logic.


if(headers.get('x-api-key') !== "zoomba")


We are checking for `x-api-key` in all requests in our middleware. Let's modify it a bit to check for only `/api/`.


typescript

const middleware = (request : NextRequest) => {
    const {headers, nextUrl : {pathname}} = request;
    if(pathname.startsWith('/api/') && headers.get('x-api-key') !== "zoomba"){
        return Response.json(null,{status:400, statusText:'Unauthorized request'});
    }
}

By checking if the request pathname starts with `/api/` we can distinguish between API and page requests.


Save the changes and now you will be able to render the application page. 


This way, there is no validation for pages. Let's add one that checks for a session cookie if the user tries to visit the page.


typescript
const middleware = (request : NextRequest) => {
    const {headers, nextUrl : {pathname}, cookies} = request;
    console.log('cookies ', pathname);
    if(pathname.startsWith('/api/') && headers.get('x-api-key') !== "zoomba"){
        return Response.json(null,{status:400, statusText:'Unauthorized request'});
    }

    if(!cookies.get('session')?.value){
        return Response.json('Access denied',{status:400, statusText:'Access denied!!'});
    }
}


Save the changes and try reloading the application and you'll get `Access denied`, since the `session` cookie is not found.


Let's add a cookie to the application. For that go to your application tab in the developer console. From the left side go to storage, cookie, and click on the site URL. There you can add cookies to the application.


select-a-cookie-to-preview-its-value


Now try refreshing the application and the page will appear since the `session` cookie is present. So this way you can add the middleware logic to control access to pages and API endpoints.


Wrapping it up


In this tutorial, you learned about protected routes in Next.js. You learned how to protect API routes and pages using Middleware. This helps in protecting your application data from unwanted access and data breaches.


Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Tutorials Web Development

Building API Using Next.js

In this development tutorial, we'll explore how to build APIs using Next.js 14. The newer version of Next.js provides the platform to create APIs using the App router.

July 30, 2024 | By Jay Raj | 10 min read

Web Development

Understanding Routing in Next.js

In this tutorial, you'll learn the basics of how to route pages in your Next.js application.

February 28, 2023 | By Jay Raj | 9 min read

Section Divider