DEV Community

Marko Jakic
Marko Jakic

Posted on

How to Render JSON Items in Next.js and Server Components?

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 API.

For data fetching in Next.js we'll be using actions. This means we'll have to enable experimental features - in next.config.js first add serverActions experimental flag:

experimental: {
  serverActions: true,
}
Enter fullscreen mode Exit fullscreen mode

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 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, compared to paths. 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):

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

See nodb docs 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. 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:

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();
Enter fullscreen mode Exit fullscreen mode

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:

'use server'

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

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:

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

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

export default function DeleteAllItems() {
  return (
    <button onClick={deleteAllItems}>
      Delete all
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)