avatarLorenzo Zarantonello

Summary

The provided content discusses techniques for reading and writing local JSON data in a Next.js application using utility functions, API routes for server-side operations, and client-side functions to interact with these APIs.

Abstract

In a detailed guide, the content outlines how to extend a Next.js application's capabilities by managing local JSON data. Initially, it revisits a previously discussed utility function, getLocalData, which reads from a local JSON file. The focus then shifts to writing data to the same JSON file using Next.js API routes. An example is provided by creating a store.js file within the pages/api directory to handle HTTP requests. The guide demonstrates setting up an API endpoint that responds to both GET and POST requests, allowing for the retrieval and updating of data in data.json. The article emphasizes that API routes should be tested and that fetching from these routes should occur on the client side, not within getStaticProps or getStaticPaths. It concludes by offering a complete example of a Next.js application that can read and write data to a local JSON file, with the code available on Stackblitz for further exploration.

Opinions

  • The author values the DRY (Don't Repeat Yourself) principle, suggesting that code should be refactored to avoid repetition.
  • Client-side fetching is preferred over server-side fetching in certain scenarios, as indicated by the advice against using getStaticProps or getStaticPaths for API route fetching.
  • The article conveys the importance of error handling when dealing with file operations, demonstrated by the use of try-catch blocks in the store.js example.
  • By providing the full code and examples on Stackblitz, the author shows a commitment to practical learning and developer empowerment.
  • The author encourages readers to subscribe for future updates, indicating a desire to build a community of developers interested in frontend development and related topics.

NEXT.JS

Read And Write Local JSON Data With Next.js

Read And Write Local JSON Data With Next.js, getStaticProps, And Node.js fs/promises

In the previous post, I showed one way to read data from a local JSON file located in json/data.json.

I created a utility function called getLocalData in lib/localData.js that reads data from data.json whenever it is invoked.

// lib/localdata.js

import fsPromises from 'fs/promises';
import path from 'path'

export async function getLocalData() {
  // Get the path of the json file
  const filePath = path.join(process.cwd(), 'json/data.json');
  // Read the json file
  const jsonData = await fsPromises.readFile(filePath);
  // Parse data as json
  const objectData = JSON.parse(jsonData);

  return objectData
}

In this post, we write data in the same local JSON file.

Write JSON Data

In the previous post, we invoked getLocalData in getStaticProps.

However, since getStaticProps is executed during the build process, any changes made to the data.json file will only be reflected when you rebuild your app, which is not what you want in production.

One way to write data on a local JSON file is by using API Routes.

Setting Up An API

According to the docs, Any file inside the folder pages/api is mapped to /api/* and will be treated as an API endpoint instead of a page. They are server-side only bundles and won’t increase your client-side bundle size.

So we create a file in pages/api and we call it store.js. In this file, we will create a little backend.

This file will be treated as an endpoint, meaning that it will handle HTTP requests. In our case, we will see those requests at the URL: http://localhost:3000/api/store.

Testing The API

To make sure everything works fine, we test the newly created API by adding the following code in store.js.

The handler function handles GET requests to the url: /api/store and returns status 200 and some data in a JSON format.

// pages/api/store.js

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

Then, we can create the following function inside the Home component. The fetchJoe function is an asynchronous function that sends a GET request to the url: /api/store.

Then, it receives a response that is a Promise. When the Promise completes we parse it as JSON and finally we log the data we receive.

// pages/index.js

...
export default function Home({ localData }) {
    const fetchJoe = async () => {
      const response = await fetch('/api/store')
      const data = await response.json();
      console.log(data);
    }
    ...
}

The function can be triggered in several ways. For a quick test, use a button in the template of the Home component and click it.

<button onClick={fetchJoe}>Fetch</button>

You should see the following log in the console:

{name: 'John Doe'}

As you can see from the code above, fetchJoe is a function in the component and it is not inside a getStaticProps. This is something to remember: You should not fetch an API Route from getStaticProps or getStaticPaths because they run on the server-side and not on the client-side.

GET And POST Requests

The previous example makes sure everything works.

Now we write a GET request that reads data from the JSON file.

HTTP GET

In store.js we write the logic to read data.json. If the request method of the HTTP request is GET,

  1. read data in data.json, and
  2. when the Promise completes send a response with status 200 and data.
// pages/api/store.js

import fsPromises from 'fs/promises';
import path from 'path';

const dataFilePath = path.join(process.cwd(), 'json/userData.json');

export default async function handler(req, res) {

  if (req.method === 'GET') {
    const jsonData = await fsPromises.readFile(dataFilePath);
    const objectData = JSON.parse(jsonData);
    res.status(200).json(objectData);
  }

}

Now, we can update the fetchJoe function and call it fetchData to send an HTTP GET request to api/store and log the response.

// pages/index.js

...
export default function Home({ localData }) {
  const fetchData = async () => {
      const response = await fetch('/api/store')
      const data = await response.json();
      console.log("data", data);
    }
    ...
}

Since we are just logging the data in the console, you should simply see the content of data.json in your console.

GET request response in the console

We can create the logic to write data in store.json through HTTP POST requests.

HTTP POST

Let’s expand the code in store.js to take into consideration POST requests as follow:

if (req.method === 'GET') {
    const jsonData = await fsPromises.readFile(dataFilePath);
    const objectData = JSON.parse(jsonData);
    res.status(200).json(objectData);
  } else if (req.method === 'POST') {
    // Code for POST requests goes here
  }

Inside the condition, we can write the following logic and add some error handling:

try {
      const jsonData = await fsPromises.readFile(dataFilePath);
      const data = JSON.parse(jsonData);
      
      // Get the data from the request body
      const { bicycle } = req.body;
      // Create new object
      const newData = {
        store: {
          book: data.store.book,
          bicycle: bicycle,
        },
      };

      // Convert the object back to a JSON string
      const updatedData = JSON.stringify(newData);
      // Save data in data.json
      await fsPromises.writeFile(dataFilePath, updatedData);
      // Send a success response
      res.status(200).json({ message: 'Bike updated successfully' });
    } catch (error) {
      console.error(error);
      // Send an error response
      res.status(500).json({ message: 'Error storing data' });
    }

You might notice some repetitions and it could be good to refactor the code to follow DRY principles.

However, that is up to you.

In index.js we can add a function called writeData. Every time this function is called, it will send a bicycle object with a random price. Then, it will log the response e.g. the whole data.json with updated data.

// pages/index.js

...
export default function Home({ localData }) {
  const fetchData = async () => { ... }

  const writeData = async () => {
      const response = await fetch('/api/store', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          bicycle: {
            color: 'green',
            id: 1,
            price: Math.floor(Math.random() * 1000),
          },
        }),
      });
      const data = await response.json();
      console.log(data);
    };
}

With this piece of code, every time you click on the Write button, you will overwrite the data on the bicycle in data.json.

// pages/index.js

...
export default function Home({ localData }) {
  ...

  return (
    ...
    <button onClick={fetchData}>Fetch</button>
    <button onClick={writeData}>Write</button>
    ...
  )

 

You can find the code on Stackblitz.

Read and write data on JSON in Next.js

In Plain English

Thank you for being a part of our community! Before you go:

React
Nextjs
Web Development
Front End Development
JavaScript
Recommended from ReadMedium