Search code examples
javascriptnext.js

Dynamic server usage: Page couldn't be rendered statically because it used `nextUrl.searchParams` in Next.js version 14


I am creating a full stack application with next js 13, I have created an API that is perfectly working in dev(npm run dev) mode but the problem is when I give the build command(npm run build) then it shows the flowing error. Please anybody bring me out of the problem. Error Screenshot - https://prnt.sc/VaN1wHifqK_2

 [Error]: Dynamic server usage: Page couldn't be rendered statically because it used `nextUrl.searchParams`. See more info here: https://nextjs.org/docs/messages/dynamic-server-error

Here is my code. This is the API function for getting users from the database using url searchParams

// /backend/apiControllers/getUsers.js

import prisma from "@/prisma/prismaClient";

export const getUsers = async (request) => {
  const { nextUrl } = request; // here is the problem, this is working for dev mode but not in production build mode.
  const email = nextUrl.searchParams.get("email") || "";
  const phone = nextUrl.searchParams.get("phone") || "";
  const id = nextUrl.searchParams.get("id") || "";

  let queryParam = {};

  if (email) {
    queryParam = {
      where: {
        ...queryParam.where,
        email: {
          equals: email,
          mode: "insensitive",
        },
      },
    };
  }
  if (phone) {
    queryParam = {
      where: {
        ...queryParam.where,
        phone: {
          equals: phone,
        },
      },
    };
  }
  if (id) {
    queryParam = {
      where: {
        ...queryParam.where,
        id: {
          equals: id,
        },
      },
    };
  }

  try {
    const users = await prisma.users.findMany(queryParam);
    return users;
  } catch (error) {
    return { apiMessage: { errorMsg: "Unable to find User details" } };
  }
};

And I have called that function into /app/api/armies/route.js

// /app/api/armies/route.js

import { getUsers } from "@/backend/apiControllers/getUsers";
import { connectDB } from "@/backend/utils/dbConnect";
import { NextResponse } from "next/server";

export const GET = async (request) => {
  try {
    await connectDB();
    const users = await getUsers(request); //called here
    return NextResponse.json(users);
  } catch (error) {
    console.log(error);
    return NextResponse.json({
      apiMessage: { errorMsg: "Internal Server Error, Please try again later" },
    });
  }
};

I have tried the following method also "const url = new URL(request.url);" but the same error, Here is the error screenshot - https://prnt.sc/Z3D317lDQ3CP

import prisma from "@/prisma/prismaClient";

export const getUsers = async (request) => {
  const url = new URL(request.url); // here is the problem, this is working for dev mode but not in production build mode.
  const email = url.searchParams.get("email") || "";
  const phone = url.searchParams.get("phone") || "";
  const id = url.searchParams.get("id") || "";

  let queryParam = {};

  if (email) {
    queryParam = {
      where: {
        ...queryParam.where,
        email: {
          equals: email,
          mode: "insensitive",
        },
      },
    };
  }
  if (phone) {
    queryParam = {
      where: {
        ...queryParam.where,
        phone: {
          equals: phone,
        },
      },
    };
  }
  if (id) {
    queryParam = {
      where: {
        ...queryParam.where,
        id: {
          equals: id,
        },
      },
    };
  }

  try {
    const users = await prisma.users.findMany(queryParam);
    return users;
  } catch (error) {
    return { apiMessage: { errorMsg: "Unable to find User details" } };
  }
};

But again the same error


Solution

  • In Next.js, Every page and route handler(1) are static by default then Next.js will (bail out) opt out to dynamic rendering when using Runtime data such as searchParams or headers. The way Next.js knows when dynamic data is used is by throwing custom errors and catch them to switch the renderer method. Simply, when you use the request URL (e.g. request.nextUrl) Next.js internally will throw DynamicServerError and catch it at a top-level.

    While generating static pages, Next.js will throw a DynamicServerError if it detects usage of a dynamic function, and catch it to automatically opt the page into dynamic rendering. However, when it's uncaught, it will result in this build-time error.

    Documentation

    Let's look at this snippet from your code:

    try {
        const users = await getUsers(request); //called here
        // At the previous line, We used `request.url` / `request.nextUrl`
        // Next.js has thrown a `DynamicServerError`
    
        return NextResponse.json(users);
      } catch (error) {
    
        // Wait!
        // We are trying/catching errors here, so any thrown error withen 
        // the try block will no longer be thrown. As a result, 
        // Next.js no longer knows when you use dynamic data
     
        return NextResponse.json({
          apiMessage: { errorMsg: "Internal Server Error, Please try again later" },
        });
      }
    

    Note: Read the comments in the code block if you haven't

    Did you get it?

    How to solve it?

    We have two solutions:

    1. Use dynamic data outside of try/catch statments
    const searchParams = request.nextUrl.searchParams
    
    try {
        const users = await getUsers({ searchParams });
        return NextResponse.json(users);
        // ...
    

    This way, Next.js will throw its errors and catch them without a stumbling block.

    1. Re-throw Next.js errors:

    You can also, catch all errors then re-throw Next.js-specific errors to handle them. Next.js provides some utilities to know the error.

    import { isDynamicServerError } from "next/dist/client/components/hooks-server-context";
    
    // ...
    
    try {
      const {nextUrl} = request;
    } catch (error) {
      if (isDynamicServerError(error)) {
        throw error;
      }
    
      // handle other errors
    }
    

    When using some of Next.js functions inside try you need to re-throw them. for example, isNotFoundError for notFound(), isRedirectError for redirect.

    import { isStaticGenBailoutError } from "next/dist/client/components/static-generation-bailout";
    import { isNotFoundError } from "next/dist/client/components/not-found";
    import { isRedirectError } from "next/dist/client/components/redirect";
    import { isDynamicServerError } from "next/dist/client/components/hooks-server-context";
    

    Update:

    There is a proposal to introduce a new API rethrow that you can call it in the catch block to rethrow all next.js errors.

    Update:

    unstable_rethrow is out as of [email protected], you can call it in the catch block to rethrow Next.js specific errors:

    try {
      redirect("/");
      notFound();
      request.nextUrl.searchParams;
    
      // ...
      await getData();
    } catch (error) {
      unstable_rethrow(error)
    
      // handle your errors
      console.error(error)
    }
    

    (1) Starting from next@15, Route Handlers are dynamic by default.