prevent unnecessary duplicate database queries for generating metadata in the upcoming 13 or 14 instances

In the latest updates, version 13 and 14 of Next introduced the usage of the generateMetadata function as the preferred method to define all page metadata.

However, a drawback of this approach is that because of the separation of logic, fetching data based on backend fields now requires making two separate calls instead of just one.

Let's look at the example below in the file page.tsx:

export default async function ProfilePage(props){
    try{
        const profile = await prisma.users.findUnique({
            // ... fetch user data
        });

        return <div>Profile page</div>;
    } catch(err){
        notFound();
    }
}

export async function generateMetadata(params){
    const profile = await prisma.users.findUnique({
        // ... fetch user data
    });

    return {
        title: `${profile.name}'s Profile`,
    };
}

This leads to redundant data fetching, meaning I have to retrieve the same information twice.

Is there a way to achieve the same outcome without having to duplicate the fetch process? Instead of resorting to using document.title, which can result in a disruptive experience when applied to nested pages. How should I go about resolving this issue?

Answer â„–1

One challenge with this approach is that when the logic is separated, data obtained from backend fields necessitates two calls instead of one.

Resolution :

To address this issue, place your await prisma.users.findUnique within an asynchronous function enclosed in the cache() function provided by react on that specific page & return the data from there. Then simply invoke that function using const profile = await Function(). By caching the function and calling it twice on the same page, NextJS prevents duplicate calls.

Sample Code Structure :

projectName
├── src
│   └── app
│       ├── api
│       ├── favicon.ico
│       ├── globals.css
│       ├── layout.js
│       └── user
│           ├── page.js
│           └── [id]
│               └── page.js
└── tailwind.config.js 

Explanation :

  • There exists a page called /user which displays all users, and upon clicking on a user, it directs you to the details page /user/user_id where server-side metadata is generated and user data is set on the page through an API call.
  • Two different calls are made, one employing caching while the other is commented out. Uncomment each one separately to observe the outcomes.

Users Page projectName\src\app\user\page.js (this page solely retrieves user list) :

import Link from "next/link";

const AllUsers = async () => {
    let tmp = await fetch('https://dummyjson.com/users')
    let data = await tmp.json()
    return data.users
}

export default async function Page() {

    let AllUserData = await AllUsers();
    return (
        <div>
            <h1>Users Page</h1>
            <ol>
                {
                    AllUserData.map((u, i) => (
                        <li key={i}>
                            <Link href={'/user/' + u.id}> {u.firstName}</Link>
                        </li>
                    ))
                }
            </ol>
        </div>
    )
}

export async function generateMetadata() {

    let UserDetails = await AllUsers()
    return {
        title: UserDetails.length.toString() + " Users",
    };
}

User details page

projectName\src\app\user\[id]\page.js
:

import React from 'react'
import { cache } from 'react'

const GetUserByID = cache(async (ID) => {
    console.log(ID);
    // VISIBLE IN TERMINAL
    console.log("GetUserByID HIT (cached) : ", new Date().toLocaleTimeString());
    let tmp = await fetch('https://dummyjson.com/user/' + ID)
    let data = await tmp.json()
    return data

})


// TO RUN BELOW CODE & COMMENT THE ABOVE CODE
// WITHOUT ANY CACHING , THIS CODE RUNS TWICE


// const GetUserByID = async (ID) => {
//     console.log(ID);
//     console.log("GetUserByID HIT : ", new Date().toLocaleTimeString());
//   // VISIBLE IN TERMINAL

//     let tmp = await fetch('https://dummyjson.com/user/' + ID)
//     let data = await tmp.json()
//     return data
// }

export async function generateMetadata({ params }) {

    let UserDetails = await GetUserByID(params.id)
    return {
        title: UserDetails.firstName + " " + UserDetails.lastName,
    };
}

const UserDetailsPage = async ({ params }) => {
    let UserDetails = await GetUserByID(params.id)
    return (
        <div>
            <h1>UserDetailsPage </h1>
            <p>ID Received from params :{params.id} </p>
            <hr />
            <p>ID from data : {UserDetails.id}</p>
            <p>FirstName : {UserDetails.firstName}</p>
            <p>LastName : {UserDetails.lastName}</p>
            <p>Age : {UserDetails.age}</p>
            <p>Birth Day : {UserDetails.birthDate}</p>
        </div>
    )
}

export default UserDetailsPage

Output :

  • Access http://localhost:3000/user, where the metadata title reflects the number of users determined from API data. It provides user links – click to view individual user details, involving 2 API calls, one utilizing cache and the other not.

  • On the details page at

    http://localhost:3000/user/user_id
    , you will notice that the cached API call wrapped in cache executes only once in the terminal, while the uncommented code runs twice.

Please Refer To :

If you still have any doubts, feel free to comment (I will update the answer if required)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to display only li elements that possess a specific class utilizing javascript / jquery?

I have a bunch of li's: <ul> <li class="one"></li> <li class="one"></li> <li class="two"></li> </ul> and I want to display only the ones with the class "one" I attempted something like thi ...

Struggling to understand the javascript snippet "requiring the passport file and passing in passport as a parameter."

I am still learning the ropes of javascript and currently working on a basic login restful api using the passport middleware. I understand that when I use require('xxxxx'); I am importing a module for use. While researching online, I came across ...

Can icons with an external path be utilized as a src in the manifest.json file within an Angular application?

Let's visualize the setup with two projects - project1 and project2. Each project has its own manifest.json file and the same apple-touch-icon-144x144.png file located in assets/icons directory. -project2 |_src | |_assets | | |_icons | | ...

Navigating directories and file locations in a Node/Express application

An example of my Node/Express app's file structure is shown below: /lib coolStuff.js /routes funRoute.js /views sickView.hbs app.js In the past, I have been using relative paths like var coolStuff = require('../lib/coolStuff'); to re ...

Issues with ontimeupdate event not triggering in Chrome for HTML5 audio files

After creating an HTML5 audio element and setting a listener for when its time updates, I have run into an issue where the ontimeupdate function does not fire in Chrome, including Chrome on Android. The audio plays without any issues in other browsers. va ...

The file type stored in Firebase storage is identified as 'octet-stream' rather than the expected 'png/jpg' format

I am experiencing an issue with uploading images to Firebase storage. After uploading them, I notice that they are labeled as application/octet-stream instead of the expected types like image/jpeg or image/png. Below is the code snippet I am using: <in ...

Blank YouTube Popup Modal

I am in the process of creating a pop-up modal that contains an embedded YouTube video, but my modal is not displaying properly. I am utilizing Bootstrap and have two separate sections along with JavaScript code. When the modal appears, it remains empty. I ...

encountering a loading issue with angular2-modal in rc1 version

Currently, I am implementing a library called angular2-modal. This library offers various modal options including bootstrap and vex for angular 2. While vex is functioning properly, I encounter an error when switching to bootstrap: responsive-applicati ...

Execute a Vue.js script once all Axios calls have been resolved

In this scenario, it is necessary to execute the carResult function only after all axios requests have been completed. Placing it inside the success method of method2 won't work because the component ends up executing the code twice. It would be great ...

Creating dynamic grid data based on various filter criteria using JavaScript (AngularJS)

In my approach, I have created two JSON data files. If the 'List' value equals A-1, I will retrieve the JSON data specific to that value. Otherwise, I will retrieve all the main data from data.json. My goal is to have 3 other checkbox options un ...

Unable to access the website's source code

It's frustrating when I can't view the source code of certain websites, such as this one: . When I try to right-click and select "View Source", only a portion of the code is displayed, which isn't the proper source code for the page. Alth ...

What steps should be taken to activate eslint caching?

I'm attempting to activate eslint caching by following the instructions in this section of the user guide The command I am using is npm run lint -- --cache=true, and the lint script simply executes a script that spawns esw (which itself runs eslint â ...

Ensure that every route is prefixed with /api

Is there a way to set all routes accepted by Express to start with /api without explicitly defining it? Current: this.app.get('/api/endpoint-A', (req, res) => { return res.send('A'); }); this.app.get('/api/endpoint-B', ...

When the user hits the enter key, automatically submit a form without the need for

Is it possible to submit a form on enter using type="button"? Here are my input fields: <input type="text" id = "login-user" class="form-control log-input" placeholder="Username" required="required"> <input type="password" id="login-password" clas ...

How can JavaScript be utilized for connecting to CSS files?

I'm unsure if this is feasible and after a brief search, I couldn't find any information. I am curious to know if it's possible to connect a CSS file using JavaScript? In essence, I would like to invoke a style sheet when I execute a specif ...

Troubleshooting: TailwindCSS issues in Next.js 13 (with app directory)

I have recently updated to the latest version of tailwindcss. However, when I execute the command "npm run dev", tailwind only affects the fonts and nothing else. My current tailwind.config.js setup: /** @type {import('tailwindcss').Config} */ ...

Strange interaction observed when working with Record<string, unknown> compared to Record<string, any>

Recently, I came across this interesting function: function fn(param: Record<string, unknown>) { //... } x({ hello: "world" }); // Everything runs smoothly x(["hi"]); // Error -> Index signature for type 'string' i ...

How to send parameters to Bootstrap modal using a hyperlink

I need help confirming the deletion of a datatable row using a Bootstrap modal dialog. When a user clicks on the delete link, I want a modal to appear asking for confirmation. Here is my code: <c:forEach items="${equipes}" var="equ"> ...

I am encountering an undefined error with crypto.subtle while working with http://localhost:3000

As I attempt to incorporate crypto.subtle.digest() into a checksum function for a file upload project, I keep encountering an error that reads as follows: upload-form.tsx:129 Uncaught TypeError: Cannot read properties of undefined (reading 'digest&apo ...

What is the best way to include an attribute to an object in a knockout observable array and ensure a notification is triggered?

Implementing Knockout.js in my project, I have an observable array included in the view model. function MyViewModel() { var self = this; this.getMoreInfo = function(thing){ var updatedThing = jQuery.extend(true, {}, thing); ...