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

Tips on configuring HTTP headers for numerous messages within Node-RED?

I've been working on a flow that involves breaking down a large message into smaller messages. I've successfully implemented the logic for splitting the message, but when attempting to send these small messages to an http response node, I encount ...

Incorporating a feature that displays one show at a time and includes a sliding animation into an

Hey there! I have this show/hide script on my website that's been working well. However, there are a couple of things I need help with. I want to modify the script so that only one div can be shown at a time. When a div appears, I'd love for it ...

Having trouble retrieving the ID of the clicked element in AngularJS?

I have successfully implemented a function in angularjs to retrieve the id of the clicked element. Below is my code snippet html <a href="#faqinner/contactform" class="clearfix" ng-click="getCateId($event)" id="{{option.id}}"> <span class= ...

Tips for saving and accessing the value of an md-select ng-model in an AngularJS dialog?

Currently, I am utilizing a template to populate an md-dialog box: The procedure for displaying the dialog: $scope.showDialog = function(element) { var parentEl = angular.element(document.body); $mdDialog.show({ template: element, scope: $scope, pr ...

Sort an array using another array as the reference in JavaScript

I have encountered similar questions before, but my current challenge involves partially sorting an array based on values from another array. This is what I aim to achieve: let array = [ { name: "Foo", values: [a, b, c, d] }, { name: " ...

By setting up a keydown event listener, I am unable to inspect HTML elements by using the F-12 key shortcut in Chrome

Recently, I encountered an issue where adding a keydown event listener in my JavaScript code prevented F-12 from working properly. window.addEventListener("keydown", function (event) { if (event.defaultPrevented){ retu ...

Steps to resolve the Angular observable error

I am trying to remove the currently logged-in user using a filter method, but I encountered an error: Type 'Subscription' is missing the following properties from type 'Observable[]>': _isScalar, source, operator, lift, and 6 more. ...

Can you define the "tab location" in an HTML document using React?

Consider this component I have: https://i.stack.imgur.com/rAeHZ.png React.createClass({ getInitialState: function() { return {pg: 0}; }, nextPage: function(){ this.setState({ pg: this.state.pg+1} ) }, rend ...

Retrieve data from a URL using Angular 6's HTTP POST method

UPDATE: Replaced res.json(data) with res.send(data) I am currently working on a web application using Angular 6 and NodeJS. My goal is to download a file through an HTTP POST request. The process involves sending a request to the server by calling a func ...

HTML - Transforming JSON data into a table

I'm having trouble converting JSON data into a table format. Although everything seems to be in order, I am unable to view the values on my table. Here is the code I am using to convert JSON to a table: $(function() { var my_data = &ap ...

Exploring nested arrays of objects and applying value constraints

Is there a way to iterate through an array and display only 5 items at once, with the option to call a function on click that will add another 20 items? I have an object structured like this: let someObject = [ { data: [ { id: 123, ...

`Loading CSS files in Node.js with Express``

My CSS isn't loading properly when I run my HTML file. Even though the HTML is correctly linked to the CSS, it seems like express and node.js are not recognizing it. I find it confusing to understand the articles, tutorials, and stack overflow questio ...

Button with a Jquery icon design

Hello, I am currently working on implementing an icon button that can collapse and expand text. Although I have successfully implemented the logic for collapsing/expanding, I am facing difficulties in creating the actual icon button. The theme I am require ...

Navigating views with ReactJS using ES5

I have been searching for ReactJs guides, but most of them are based in ES5. As a result, I have begun using ReactJS in this manner. Now that I understand how to create all my components, I am ready to build a small single-page-application. However, I am s ...

Occasionally, the function XMLHttpRequest() performs as expected while other times it may

I've encountered an issue with my XMLHttpRequest() function where it randomly works in both Chrome and IE. The function is triggered by On-click, but I'm having trouble catching the error. The only information I could gather is that readystate = ...

What could be the reason for the handleOpen and handleClose functions not functioning as expected?

I am facing an issue with my React component, FlightAuto, which contains a dropdown menu. The functionality I'm trying to achieve is for the dropdown menu to open when the user focuses on an input field and close when they click outside the menu. Howe ...

Steps for implementing a reset button in a JavaScript slot machine game

I need assistance with implementing a reset button for my slot machine game prototype written in JS. I attempted to create a playAgain function to restart the game by calling the main game function, but it's not working as expected. Do I need to pass ...

Creating Visuals from Text with ReactJS/NextJS

Looking to transform text into an image with a shareable link in ReactJS, similar to Spotify's lyrics sharing feature. I haven't attempted any solutions so far. I've explored various libraries without success. ...

AngularJS directive facing a callback problem

Having trouble with callbacks while calling a service function This is the function defined in registrationService function checkUserAccess(incentiveLevel, callback, displayRegistrationView) { var url = "..."; httpWrapperService. ...

Challenges Associated with Promises in JavaScript

I'm having trouble with the last line of code in my program and I need some help figuring out how to fix it. Specifically, I know that the second "then" statement needs to return resolve() but I'm not sure how to go about implementing this. Any t ...