Hamburger Icon
  • Labs icon Lab
  • Core Tech
Labs

Guided: Data Fetching and API Routes in Next.js 14

This hands-on lab will guide you through the essentials of data fetching and API route creation in Next.js 14. Using the new App Router, you'll learn to implement Static Site Generation (SSG), Server-Side Rendering (SSR), and Incremental Static Regeneration (ISR) through a series of practical steps. Additionally, you'll build API routes for CRUD operations, enhancing your skills in creating scalable and efficient web applications. By the end of the lab, you'll be well-prepared to tackle advanced development challenges with a solid foundation in Next.js 14's data fetching and API routing capabilities.

Labs

Path Info

Level
Clock icon Beginner
Duration
Clock icon 45m
Published
Clock icon Oct 04, 2024

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    Introduction

    Welcome to this Code Lab, Guided: Data Fetching and API Routes in Next.js 14. This lab is designed for web developers or learners eager to deepen their knowledge and understanding of Next.js 14, particularly in data fetching and building robust API routes using the new App Router. Throughout this lab, you will engage in practical exercises that take you from the basics of data fetching to advanced API routing techniques. You'll learn to implement various data-fetching strategies like Static Site Generation(SSG), Server-Side Rendering (SSR), and Incremental Static Regeneration (ISR). Additionally, you'll gain hands-on experience creating and managing API routes to perform CRUD operations. By the end of this Code Lab, you will have a solid understanding of utilising the App Router in Next.js 14 to its fullest potential, enabling you to build scalable and efficient web applications. Whether you want to refine your skills or tackle real-world development challenges, this lab will equip you with the knowledge and practical experience necessary to succeed. info> Code Solutions : You can find the final code of each step inside the __solution/codes folder. For example, the __solution/codes/step1_final_code.ts file will have the final code of step 1.

    info> Image Solutions : You can also find the image depicting the lab's state inside the __solution/images folder. For example, __solution/images/step_1_final_output.png will have the image depicting the final state of step 1. Data store using lowdb : Lowdb, a lightweight JSON database, is used in this lab to efficiently store and manage data while you build and interact with the API routes. The database is already configured, and you can access the JSON data file at data/db.json. API Endpoint using json-server : In this lab, we'll use json-server to create an API endpoint at http://localhost:4000/api/tasks/, utilising data/db.json to demonstrate various data-fetching techniques in Next.js 14. You will also create an API endpoint using Next.js 14 that is similar to it.

  2. Challenge

    Step 1: GET Request

    Create an API GET Route in Next.js

    In this step, you'll create an API GET Route using Next.js' file-based routing system. This functionality will return all the task items in the data source. info> The code above sets up an API route that responds to the GET requests made to the route api/tasks.

    Async GET Function

    The asynchronous function above is triggered when an HTTP GET request is made to this API route. When a GET request is made to the route, it returns a JSON array of task objects. In this example, the tasks are hard-coded but might be fetched from a database or another data source in a real-world scenario.

    The function is named GET because Next.js follows this convention to handle GET requests at this endpoint. Other supported methods include POST, PUT, PATCH, and DELETE.

    NextRequest This utility class provided by Next.js encapsulates the HTTP request details. It includes methods to get query parameters, headers, body, etc.
    NextResponse This utility class provided by Next.js allows you to construct and return HTTP responses in API routes.
    Task This is a TypeScript interface that defines the structure of a Task object. It contains three specific fields: id, title, and completed.
    Now, navigate to the first terminal and execute the following command to set up a development server.
    npm run dev
    

    Then, navigate to the URL http://localhost:3000/api/tasks in your Web Browser tab.

    You should see the tasks displayed as shown in the image below.

    Step1 Final Output State

  3. Challenge

    Step 2: Dynamic Data

    Change Data For API Request to be Dynamic From Data Source

    For this step, you'll replace the hardcoded data from Step 1 with dynamic data fetched from a JSON database.

    Explanation of above code The imported `getAllTasks` function will be used to get all the tasks from the database. The import statement also includes the `addNewTask` functions, which will be used in the upcoming steps.
    info> The code above replaces hardcoded tasks with dynamic data fetched from a data source using the `getAllTasks` function.
    Real world scenario: Data sources In this lab, the data is fetched from a JSON database, but in a real-world scenario, the data could come from various sources such as an SQL database, an API endpoint, a GraphQL server, or other external services.
    Next, navigate to the URL `http://localhost:3000/api/tasks` in your Web Browser.

    You should see the task list displayed as shown in the image below. The database has been pre-populated with the task list on your screen.

    In the next step, you'll create a POST request to add new tasks.

    Step2 Final Output State

  4. Challenge

    Step 3: POST Request

    Create an API POST Route to Create New Tasks

    In this step, you will create an API Post route to create new tasks.

    Explanation of above code The above code retrieves the JSON data from the POST request and checks if the data is valid. If the data is invalid, it returns a 400 response with the message `Invalid data`.
    Practical validation You are using simple validation for this lab; however, in a real-life scenario, it's more common to use a third-party package, such as Zod, for more robust and scalable validation.
    Explanation of above code The above code retrieves the new task from the POST request body and adds it to the JSON database using the `addNewTask` function, which was imported in the earlier step. After that, it responds with the newly created task and a 201 status code, indicating that the request was successfully processed and a new resource was created.
    By running the above command, you have successfully created a new task using the POST API route created in this step. You should see a response in the command terminal displaying the new task along with its id , as shown in an example response below.
    workspace $ curl --location 'http://localhost:3000/api/tasks' 
    --header 'Content-Type: application/json' 
    --data '{
        "title": "Wash clothes",
        "completed": false
    }'
    {"id":3,"title":"Wash clothes","completed":false}workspace $ 
    ``` Navigate to the URL `http://localhost:3000/api/tasks` in your Web Browser tab.
    
    You should see the task list, including the newly created task, as shown in the image below. If the new task is not visible, scroll down in your browser to view it.
    
    
    ![Step3 Final Output State](https://ps-cdn.s3.us-west-2.amazonaws.com/code-labs/public/2ac94783-4504-43cb-ab1a-5c78677d21ae/4f273c6def231fb144b2-1730216745.png)
    
    
    
  5. Challenge

    Step 4: GET Single Task

    Create an API GET Route to Get a Single Task

    Now you'll create a dynamic API GET Route using Next.js' file-based dynamic routing system. This functionality will return a single task from the data source. info> The above code sets up an API route to handle GET requests sent to the api/tasks/<id> endpoint. The second argument of the function retrieves the dynamic ID from the URL. When a GET request is made, the function searches the database for the task with the provided id and returns it.

    The GET function will be completed in the next task. The getTask function has been imported to get the single task. Also, the functions updateTask and deleteTask will be used in the following steps.

    Close the app/api/tasks/route.ts file in your editor to avoid confusion. Navigate to the URL http://localhost:3000/api/tasks/1 in your Web Browser.

    You should be able to see the task with an id of 1, as demonstrated in the image below.

    Step4 Final Output State

  6. Challenge

    Step 5: PUT Request

    Create an API PUT Route to Update an Existing Task

    In this step, you will create an API PUT route to update an existing task.

    Explanation of above code The code above retrieves the JSON data from the PUT request and checks whether the data is valid. If the data is invalid, it returns a 400 response with the message `Invalid data`.
    Explanation of above code The above code updates an existing task in the JSON database using the data from the PUT request body. The `updateTask` function, imported in an earlier step, handles the update. After the update, the code checks if the task was successfully updated. If not, it returns a 404 status with an `Invalid data` message. If successful, it returns the updated task.
    By running the above curl command, you have successfully updated an existing task, as shown in an example response below.
    workspace $ curl --location --request PUT 'http://localhost:3000/api/tasks/1' 
    --header 'Content-Type: application/json' 
    --data '{
        "title": "Read a chapter of a book",
        "completed": true
    }'
    {"id":1,"title":"Read a chapter of a book","completed":true}workspace $
    

    Now, navigate to the URL http://localhost:3000/api/tasks/1 in your Web Browser.

    You should see the updated task with the completed flag set to true for the task with id 1, as shown in the image below.

    Step 5 Final Output

  7. Challenge

    Step 6: Delete Request

    Create an API DELETE Route to Delete an Existing Task

    For this step, you will create an API DELETE route to delete an existing task.

    Explanation of above code The above code deletes an existing task using the dynamic id provided in the params. It checks whether the task was successfully deleted. If not, it returns a message `Invalid data` with a 404 status code. Then, it returns a response with the information that the task has been successfully deleted.
    After executing the above command, you should see a response in your terminal indicating that the task has been deleted, as shown in an example response below.
    workspace $ curl --location --request DELETE 'http://localhost:3000/api/tasks/1'
    {"message":"Task with id 1 deleted"}workspace $ 
    ``` Now, navigate to the URL `http://localhost:3000/api/tasks` in your Web Browser.
    
    You should see the remaining tasks in the list, with the task that had ID 1 deleted, as shown in the image below.
    
    
    ![Step6 Final Output State](https://ps-cdn.s3.us-west-2.amazonaws.com/code-labs/public/2ac94783-4504-43cb-ab1a-5c78677d21ae/afcea594ca846d33ead1-1730216836.png)
    
    
    
  8. Challenge

    Data Fetching Techniques

    Understanding Data Fetching Techniques in Next.js

    Before diving into upcoming data fetching steps, let's get some ideas about data fetching techniques in Next.js. Next.js provides three data-fetching strategies to optimise performance and user experience in server-side rendering. Here's a brief overview of the essential methods:

    1. Static Site Generation (SSG)

    SSG pre-renders pages at build time, resulting in highly performant static pages.

    • Page rendered: On build time
    • Content freshness: Stale until rebuild if data is changed
    • Example use case: A blog where an article doesn’t change frequently
    • Advantages: Fast loading times, scalable
    • Limitations: Stale data after data is changed, requires rebuilding for the update, not suitable for frequently changing data

    2. Server Side Rendering (SSR)

    SSR generates pages on each request, ensuring the content is always up-to-date.

    • Page rendered: On request
    • Content freshness: Always fresh
    • Example use case: E-commerce sites displaying the latest price and stock levels
    • Advantages: No rebuild required, and always fresh data
    • Limitations: Slower compared to SSG, increased server load

    3. Incremental Static Regeneration (ISR)

    ISR offers a hybrid approach, combining SSG's speed with SSR's flexibility.

    • Page rendered: Build time + revalidation
    • Content freshness: Fresh after revalidation
    • Example use case: An event listing page that updates events and dates after a specific interval
    • Advantages: Balance between SSR and SSG, reduced server load compared to SSR, flexible and scalable
    • Limitations: Slight delay in content updates and the configuration of revalidation times
  9. Challenge

    Step 7: Static Site Generation

    Creating Static Page with Static Data Fetching

    You will now create a statically rendering page with static data fetching. This type of rendering is called Static Site Generation(SSG). The route of the page will be /tasks . The code above imports the Task interface and creates the getTaskData function, which uses the fetch method to get the list of tasks.

    info> Using fetch(url) or fetch(url, { cache: 'force-cache' }) will render the page as SSG (Static Site Generation) when the site is built. The { cache: 'force-cache' } option is the default for the second argument in the fetch function. This means Next.js will generate the page during the build process, and it won't update even if the data changes later, potentially serving outdated information. This method is best for content that doesn’t change often, like blog posts, making it an excellent example of static site generation.

    Explanation of above code The above code retrieves the task list and displays it with a timestamp indicating when the page was rendered. In an SSG (Static Site Generation) scenario, the timestamp will reflect when the page was generated during the build process.
    The command above will clear the pre-built cache and rebuild the application. Please wait a few minutes, as building a Next.js application may take some time.

    Once done, you should see the /tasks route created in the terminal, with a circle indicating that it is a static site, as shown in the image below.

    Step7 SSG Build Now, stop the current process by pressing Ctrl + C in the first terminal, and then run the following command in the same terminal to start the server at http://localhost:3000/

    npm run start
    ``` Now, navigate to the URL `http://localhost:3000/tasks` in your Web Browser. 
    
    You should see the task list as shown in the image below.
    
    info> Notice that the timestamp won’t change when you refresh the page, and the data won’t update if you add more tasks. This is because the page was generated during the build process using the Static Site Generation (SSG) rendering technique.
    
    
    ![Step7 Final Output State](https://ps-cdn.s3.us-west-2.amazonaws.com/code-labs/public/2ac94783-4504-43cb-ab1a-5c78677d21ae/8e69f722f49d3b4b4beb-1730216876.png)
    
    
    
  10. Challenge

    Step 8: Server Side Rendering

    In this step, you'll change the page /tasks from statically rendering to a server-side rendering page(SSR).

    SSR pages are dynamic and created on request rather than during build time. This is implemented when fresh data needs to be served on every request. For instance, when showing the latest prices on an e-commerce website. info> In the above task, adding {cache: 'no-store'} as the second argument in the fetch function ensures that the fetched data is not stored, making it dynamic for each request and resulting in dynamic page rendering. The command above will clear the pre-built cache and rebuild the application. Please wait a few minutes, as building a Next.js application may take some time.

    info> You should see that the tasks page has been rendered as a dynamic page with an ƒ sign in front of it, as shown in the image below.

    Step 8 Build Process Now, stop the current process by pressing Ctrl + C in the first terminal, and then run the following command in the same terminal to start the server at http://localhost:3000/.

    npm run start
    

    Next, Navigate to the URL http://localhost:3000/tasks in the browser and refresh the browser a couple of times. You should be able to view the task list with an updated timestamp on every request. After executing the above curl command, you should get a response in your terminal with message {"id":X,"title":"Plan weekend trip","completed":false} letting you know that the execution was successful. Now, navigate to the URL http://localhost:3000/tasks in the Web Browser.

    You should be able to view that the task list has been updated, as shown in the image below. If the new task is not visible, scroll down in your browser to view it.

    Step8 Final Output State

  11. Challenge

    Step 9: Incremental Static Regeneration

    Now you'll change the page /tasks from the server-side rendering page to an incremental static regeneration page(ISR).

    ISR is a mix of SSR and SSG where data fetch is invalidated at specified intervals. So, after the specific interval, the page is re-rendered. This approach is great for cases where data can be updated at certain intervals and do not need to be updated on every request. info> In the above updated code, { next: { revalidate: 60 }} configures the data to be invalidated and refreshed every 60 seconds, enabling Incremental Static Regeneration (ISR) for the page. This means the page's static content will be updated with fresh data at regular intervals, without needing to rebuild the entire application. The command above will clear the pre-built cache and rebuild the application. Please wait a few minutes, as building a Next.js application may take some time.

    info> You should see the /tasks route listed in the terminal, with a circle indicating that it is a static site, as shown in the image below. However, remember that its data will be revalidated at the specified interval.

    Step 9 App Build Output Now, stop the current process by pressing Ctrl + C in the first terminal, and then run the following command in the same terminal to start the server at http://localhost:3000/tasks.

    npm run start
    

    info> You should get a response in your terminal with message {"id":X,"title":"Walk the dog","completed":false} letting you know that the execution was successful. Now, navigate to the URL http://localhost:3000/tasks in the Web Browser after 60 seconds.

    You should be able to view that the task list has been updated, as shown in the image below. If the new task is not visible, scroll down in your browser to view it.

    Congratulations! You have successfully completed the lab.

    Step9 Final Output State

Asmin Bhandari is a full stack developer with years of experience in designing, developing and testing many applications and web based systems.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.