giaggi
giaggi

Reputation: 592

Next Js - `params` should be awaited before using its properties

Kind of losing my mind on this one. In my Next Js app I keep hitting this error of Error: Route "/api/materials/[materialId]" used params.materialId. params should be awaited before using its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis

I have a page.tsx

"use client";

import { type DataTableColumn } from "@/components/data-table";
import { BackButton } from "@/components/ui/back-button";
import { DetailsView } from "@/components/ui/details-view";
import { Inventory, Material } from "@/types/types";
import { notFound, useRouter } from "next/navigation";
import { use, useEffect, useState } from "react";

// Example form fields for a Material
// Adapt these to match your Material schema
const materialFields = [
  { key: "type", label: "Type", type: "text", required: true },
  { key: "color", label: "Color", type: "text" },
  { key: "colorCode", label: "Color Code", type: "text" },
  { key: "brand", label: "Brand", type: "text" },
  { key: "quantity", label: "Quantity", type: "number" },
  { key: "unit", label: "Unit", type: "text" },
  { key: "costPerUnit", label: "Cost Per Unit", type: "number" },
  { key: "currency", label: "Currency", type: "text" },
  { key: "location", label: "Location", type: "text" },
  { key: "Properties", label: "Properties", type: "textarea" },
];

// Fetch the material from the API:
async function fetchMaterial(materialId: string) {
  try {
    const response = await fetch(`/api/materials/${materialId}`);
    if (response.status === 404) {
      notFound();
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Error fetching material:", error);
    throw error;
  }
}

export default function MaterialPage({
  params,
}: {
  params: Promise<{ materialId: string }>;
}) {
  const router = useRouter();
  const resolvedParams = use(params); // Resolve the promise for the 'materialId'
  const [material, setMaterial] = useState<Material | null>(null);
  const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);

  // Load material when the page mounts or materialId changes
  useEffect(() => {
    fetchMaterial(resolvedParams.materialId).then(setMaterial);
  }, [resolvedParams.materialId]);

  if (!material) {
    return <div>Loading...</div>;
  }

  // Prepare detail items for <DetailsView>
  console.log(material);
  const detailItems = [
    {
      label: "Brand",
      value: material.brand, // Make sure this is a string
      key: "brand",
      editable: true,
    },
    {
      label: "Type",
      value: material.type, // Make sure this is a string
      key: "type",
      editable: true,
    },
    {
      label: "Cost Per Unit",
      value: material.defaultCostPerUnit, // This should be a number
      key: "costPerUnit",
      type: "number",
      editable: true,
    },
  ];

  // -- Column definitions --

  const inventoryColumns: DataTableColumn<Inventory>[] = [
    {
      header: "Quantity",
      accessorKey: "quantity",
      cell: (inventory) => `${inventory.quantity} ${inventory.unit}`,
    },
    { header: "Location", accessorKey: "location" },
  ];

  const productsColumns: DataTableColumn<Inventory>[] = [
    {
      header: "Model",
      accessorKey: "name",
      cell: (product) => `${product.product.name}`,
    },
    {
      header: "Season",
      accessorKey: "season",
      cell: (product) => `${product.product.season}`,
    },
  ];

  // Called after a successful PATCH (edit):
  const handleSaveSuccess = (updatedMaterial: Material) => {
    setMaterial(updatedMaterial);
    setIsEditDialogOpen(false);
  };

  // Called when the user clicks Delete
  const handleDelete = async (id: string) => {
    try {
      const response = await fetch(`/api/materials/${id}`, {
        method: "DELETE",
      });
      if (!response.ok) {
        throw new Error("Failed to delete material");
      }
      // Go back to the materials list
      router.push("/inventory/materials");
      router.refresh();
    } catch (error) {
      console.error("Error deleting material:", error);
    }
  };

  return (
    <>
      {/* Page Header */}
      <div className="space-y-6">
        <div className="flex items-center justify-between">
          <h1 className="text-2xl font-bold">
            {material.colorCode} - {material.color}
          </h1>
          <div className="flex gap-2">
            {/* <button
              onClick={() => setIsEditDialogOpen(true)}
              className="px-4 py-2 bg-black text-white rounded-md hover:bg-gray-800"
            >
              Edit
            </button> */}
            <BackButton />
          </div>
        </div>

        <DetailsView
          title=""
          items={detailItems}
          apiEndpoint="/api/materials"
          itemId={material.id}
        />
      </div>

    </>
  );
}

and my route.ts

// app/api/materials/[materialId]/route.ts
import { prisma } from "@/lib/prisma";
import { NextResponse } from "next/server";

//
// GET /api/materials/[materialId]
//
export async function GET(
  request: Request,
  context: { params: { materialId: string } }
) {
  try {
    // Destructure materialId from context.params
    const { materialId } = context.params;

    // No request.json() for GET — remove that part entirely
    console.log("GET request for material:", materialId);

    const material = await prisma.material.findUnique({
      where: {
        id: materialId,
      },
      include: {
        inventory: {
          include: {
            movements: true,
          },
        },
        products: {
          include: {
            product: true,
          },
        },
      },
    });

    if (!material) {
      return NextResponse.json(
        { error: "Material not found" },
        { status: 404 }
      );
    }

    return NextResponse.json(material);
  } catch (error) {
    console.error("Error fetching material:", error);
    return NextResponse.json(
      { error: "Error fetching material" },
      { status: 500 }
    );
  }
}

and I can't understand why I get this error consistently every time I load the page.

Upvotes: 0

Views: 503

Answers (2)

Manula Pasan
Manula Pasan

Reputation: 1

Step 1: npx @next/codemod@latest next-async-request-api --force

Step 2: 'On which files or directory should the codemods be applied?': '.'

Also we need to make sure that the function is declared as async, otherwise the changes won't take effect.

Upvotes: 0

Surya
Surya

Reputation: 51

From nextjs 15 params are asynchronous. Changelog and the doc.

Try:

export async function GET(
  request: Request,
  context: { params: Promise<{ materialId: string }> }
) {
  const { materialId } =  await context.params;
  // Rest of your code
}

Upvotes: 1

Related Questions