next.jsreact-admindrizzle-orm

TypeError: can't access property "name", record.section is undefined


Hey guys thanks in advance! I’m using react-admin@5.3.0 in nextjs@14.2.14. My src/app/admin/App.tsx looks like this:

// src/app/admin/App.tsx

const dataProvider = simpleRestProvider("/api");

const App = () => {
  return (
    <Admin dataProvider={dataProvider}>
      {/* ... */}
      <Resource
        name="units"
        list={UnitList}
        create={UnitCreate}
        edit={UnitEdit}
        recordRepresentation={(record) => `${record.section.name} - ${record.name}`}
      />
      {/* ... */}
    </Admin>
  );
};

It will go to src/app/api/(admin)/units/route.ts

// src/app/api/(admin)/units/route.ts

import { auth } from "@/auth";
import { db } from "@/db";
import { units } from "@/db/schema";
import { asc } from "drizzle-orm";
import { NextResponse } from "next/server";

export const GET = async () => {
  const session = await auth();

  if (!session) {
    return new NextResponse("Unauthorized", { status: 401 }); // User not signed in
  }
  if (!session.user || session.user.role !== "admin") {
    return new NextResponse("Forbidden", { status: 403 }); // User signed in but not admin
  }

  const data = await db.query.units.findMany({
    orderBy: [asc(units.order)],
    with: { section: true },
  });

  const totalCount = data.length;

  const headers = new Headers();
  headers.set("Access-Control-Allow-Origin", "*");
  headers.set(
    "Access-Control-Allow-Methods",
    "GET, POST, PUT, DELETE, OPTIONS",
  );
  headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
  headers.set("Access-Control-Expose-Headers", "Content-Range");
  headers.set("Content-Range", `units 0-${totalCount}/${totalCount}`);

  return NextResponse.json(data, { headers });
};

export const POST = async (req: Request) => {
  const session = await auth();

  if (!session) {
    return new NextResponse("Unauthorized", { status: 401 }); // User not signed in
  }
  if (!session.user || session.user.role !== "admin") {
    return new NextResponse("Forbidden", { status: 403 }); // User signed in but not admin
  }

  const body = await req.json();
  console.log(body);

  const data = await db
    .insert(units)
    .values({
      ...body,
    })
    .returning();

  return NextResponse.json(data[0]);
};

This is the type of the object being returned from this route:

const data: {
    name: string;
    order: number;
    id: number;
    description: string;
    sectionId: number;
    section: {
        name: string;
        id: number;
        description: string;
        courseId: number;
    };
}[]

I get the error when I create a record using react-admin create functionality and if I refresh the page the error goes away. What’s weird is if i set the record representation to recordRepresentation="name" there is no error. Which could mean that the error only appears when i set the recordrepresentation to a nested object.

Am I doing something wrong?

I tried reading the react-admin docs I found nothing.

I also tried adding a hidden field for section.name the UnitList.tsx i thought it will reference it but it didnt do anything:

import {
  Datagrid,
  List,
  NumberField,
  ReferenceField,
  TextField,
} from "react-admin";

export const UnitList = () => (
  <List filter={{ order: true }}>
    <Datagrid>
      <TextField source="id" />
      <TextField source="name" />
      <TextField source="description" />
      <NumberField source="order" />
      <ReferenceField source="sectionId" reference="sections" />
      <TextField source="section.name" label={false} hidden /> // <- this line
    </Datagrid>
  </List>
);

Solution

  • I figured it out. The issue was when react-admin creates a resource, it sends a POST request to api/<resource> in my case api/(admin)/units and it will expect you to return the object you just created which I did in my API route:

    // ...
    
    export const POST = async (req: Request) => {
      // ...
    
      const body = await req.json();
      console.log(body);
    
      const data = await db
        .insert(units)
        .values({
          ...body,
        })
        .returning();
    
      return NextResponse.json(data[0]);
    };
    
    

    The problem is the data[0] has no sections property which my App.tsx uses as a recordRepresentation for my units resource

    // src/app/admin/App.tsx
    
    const dataProvider = simpleRestProvider("/api");
    
    const App = () => {
      return (
        <Admin dataProvider={dataProvider}>
          {/* ... */}
          <Resource
            name="units"
            list={UnitList}
            create={UnitCreate}
            edit={UnitEdit}
            recordRepresentation={(record) => `${record.section.name} - ${record.name}`}
          />
          {/* ... */}
        </Admin>
      );
    };
    

    To fix this I went and queried the data with the id returned from the data[0] object.

    // ...
    
    export const POST = async (req: Request) => {
      // ...
    
      const body = await req.json();
      console.log(body);
    
      const data = await db
        .insert(units)
        .values({
          ...body,
        })
        .returning();
    
      const dataWithSection = await db.query.units.findFirst({
        where: eq(units.id, data[0].id),
        with: { section: true },
      });
    
      return NextResponse.json(dataWithSection);
    };
    
    

    Note that i'm doing another db call so its inefficient but it works. Maybe someone knows how to optimize this with drizzle or postgres please teach me thanks.