javascriptreactjsnext.jsfetch

Why fetch data is empty nextjs


Im using nextjs app router and im trying to pull data from database and access it in app via fetch

export default async function Home() {
    try {
        const userData = await fetch("http://localhost:3000/api/userData")
            .then((response) => response.json())
            .then((data) => console.log("DATA: " + JSON.stringify(data)));
    } catch (error) {
        console.error("ERROR: " + error);
    }

    return <div></div>;
}

Console.log with data is always empty and there is no errors

import { firestore } from "@/firebase/firebase";
import { collection, getDocs, onSnapshot, query, where } from "firebase/firestore";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
    let sessionId = cookies().get("sessionId")?.value;

    if (!sessionId) {
        sessionId = "";
    }

    let userData = undefined;

    const userDataQuery = query(collection(firestore, "users"), where("sessionId", "==", sessionId));

    const userFirstDoc = await getDocs(userDataQuery);
    userFirstDoc.forEach((doc) => {
        userData = doc.data();
    });

    const unsubscribe = onSnapshot(userDataQuery, (userSnapshot) => {
        userSnapshot.forEach((doc) => {
            userData = doc.data();
        });
    });

    console.log(userData); // Returns proper user data
    return NextResponse.json({ userData });
}

console.log(userData) displays expected value but this value doesn't seems to be available via fetch


Solution

  • First things first, I would highly recommend to change your code inside the Home page. This is not the indended way of doing API request inside React. Your typical request inside the home page should look like this for example:

    import React, { useState, useEffect } from 'react';
    
    export default function Home() {
        const [userData, setUserData] = useState(null);
        const [error, setError] = useState(null);
    
        useEffect(() => {
            const fetchData = async () => {
                try {
                    const response = await fetch("http://localhost:3000/api/userData");
                    const data = await response.json();
                    setUserData(data.userData);
                } catch (error) {
                    setError(error);
                    console.error("ERROR: " + error);
                }
            };
    
            fetchData();
        }, []);
    
        if (!userData) {
            return <div>Loading...</div>;
        }
    
        return (
            <div>
                <h1>User Data</h1>
                <p>Name: {userData.name}</p>
                <p>Email: {userData.email}</p>
            </div>
        );
    }
    

    What is happening inside the code?

    1. First we define two states, the useData and error states. This will be filled after the fetch call, with the appropiate response.
    2. Then we use the useEffect hook, which in this case will be only called once inside the Home component (then its mounted).
    3. Inside we define the async fetchData function, which does the fetch call. But furthermore it sets the states.
    4. The first if condition is a fallback, incase the fetch call takes some time to accomplish.
    5. The div inside the last return renders the userData.

    I think this should solve your problem. Remember this is an example pseudo code and not just copy paste, there might be some mistakes, especially if you are using next.js.