How to Render JSON Items in Next.js and Server Components?
Tags: nextjs, json, webdev, nodb
Published: 2023-06-08 10:27:49

Since Next.js released version 13.4 we have the option to render React components on the server. These are called React Server Components (RSC).

This tutorial will show you the way to load JSON content over HTTPS and render it in the server components, caching included. For storing JSON data and fetching it, we'll use [nodb](https://nodb.sh) API.

For data fetching in Next.js we'll be using [actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions). This means we'll have to enable experimental features - in `next.config.js` first add `serverActions` experimental flag:

```json
experimental: {
  serverActions: true,
}
```

This is major prerequisite before we proceed any further. Without this flag nothing will work, but in the next version of Next.js we'll probably already have stable version for actions to use in production.

In Next.js we can use [fetch](https://nextjs.org/docs/app/building-your-application/data-fetching) for all HTTP and HTTPS requests within our app. Next.js will cache any request used by `fetch`, meaning you don't make new request on each page load. This leads us to another important feature of revalidating cache. I tend to use revalidation of [tags](https://nextjs.org/docs/app/api-reference/functions/revalidateTag), compared to [paths](https://nextjs.org/docs/app/api-reference/functions/revalidatePath). This is because we can have multiple requests on the same page, but we only want SOME requests to be cached. That's why I tag the requests and later revalidate using `revalidateTag` function. If your case means you want to revalidate everything on the same page, feel free to use `revalidatePath` then.

Now let's see how can we send requests to `nodb` to store and fetch data. It's simple really. To store data in `nodb` you make a POST request and send array of objects as the payload (even if it's only one object):

```js
await fetch(
  `https://api.nodb.sh/your-app/dev/items`,
  {
    method: 'POST',
    body: JSON.stringify([item]),
    headers: { token: '...your access token' },
    next: { tags: ['items'] },
  }
);
```

See [nodb docs](https://docs.nodb.sh/docs/apps) to learn how to use apps and environments.

For `body` we must send "stringified" version of the payload. For details take a look at [Request API examples](https://developer.mozilla.org/en-US/docs/Web/API/Request#examples). It says:

> Note: The body can only be a Blob, an ArrayBuffer, a TypedArray, a DataView, a FormData, a URLSearchParams, a ReadableStream, or a String object, as well as a string literal, so for adding a JSON object to the payload you need to stringify that object.

To tag and later revalidate cached data, we add Next.js extended option `{ next: { tags: ['anystring'] } }`.

In `page.js` files we usually make GET requests. In my example project I've got the data from `nodb` like this:

```js
const req = await fetch(
  `https://api.nodb.sh/[app-name]/[env]/items?__sort_by=itemName`,
  {
    method: 'GET',
    headers: { token: `[token]`,
    next: { tags: ['items'] }
  }
);
const { items = [] } = await req.json();
```

This way we just map the items to React components.

### Actions

Let's say we want to delete all items. In `nodb` we can make a DELETE request to either `/items` path to delete them all, or `/items/:itemId` to delete a single entity.

For each request made by the user by clicking a button for example, we make actions.

So, if we want to delete all items, we make a file `actions.js` in `/src/app` (or `/app`) dir in our Next.js project, but you can put it anywhere really, e.g. in `/src/actions` dir. Inside this file we put the following:

```js
'use server'

export const deleteAllItems = async () => {
  await fetch(
    `${nodbUrl}/items`,
    { method: 'DELETE', headers, next: { tags: ['items'] } });
  revalidateTag('items');
}
```

Notice here two things. Actions must be called by the server. In Next.js we tell the framework to do exactly that by putting `'use server'` on top of the file, or inside a function. The other thing is that finally here we use `revalidateTag` function. After calling this function you'll see on your `page.js` file how they're all gone. 😊

### The frontend part

Server functions, pages or layouts can't call React hooks or DOM events like `onClick`. For that we need client components, the ones which will be loaded in the browser.

In our `page.js` let's import a button which will call action to delete all items. First we create a client component:

```js
// /app/components/delete-all-button.js
'use client'

import { deleteAllItems } from '@/app/actions';

export default function DeleteAllItems() {
  return (
    <button onClick={deleteAllItems}>
      Delete all
    </button>
  );
}
```

In Next.js we can import server functions inside client files. The result of `onClick` will delete all the items and your page will re-render automatically due to `revalidateTag` being called in `deleteAllItems` server function.

### Conclusion

This way we can work with Server Components and fetching data using `fetch` built-in mechanism. To store and fetch JSON data you can use any service, it's up to you really.

I hope you found this post helpful and meaningful.

You can find example project which I made to reflect on this post. You can find it at [https://github.com/nodb-sh/next-nodb-example](https://github.com/nodb-sh/next-nodb-example).