Home  Nextjs   Folder stru ...

Folder structure to create nextjs App with prisma and tailwind

Here are main folders at top level

Here are main files at top level

app

e.g. admin will need different layout than landing page.
So you can have folder structure like \app(admin)\admin[model]\page.tsx - shows model records So you can have folder structure like \app(admin)\admin[model]\create\page.tsx - creates record in a model So you can have folder structure like \app(admin)\admin[model]\update[id]\UpdateForm.tsx - edits record in a model


e.g. Main page of the module/model (e.g. Employee) will have below code in server component - page.tsx that will fetch records from database/api and pass it to client component or show directly in same component.
  const supabase = await createClient();
  const {
    data: { session },
  } = await supabase.auth.getSession();

  if (!session) {
    redirect("/auth/login");
  }
  const mainRecords = await getModel(modelName, session.user.id);

When passing data to client components, you need to ensure that plain object is being passed. If there is a nested object or if object can not be serialized, it can not be passed to client component. e.g. in below code, we are converting Decimal object to primitive Number.

  const records = mainRecords.map((emp) => ({
    ...emp,
    salary: emp.salary.toNumber(), // or toString()
    superRate: emp.superRate.toNumber(),
  }));

You can create generic component show/create/edit record in a model.

        <GenericShowModelData model={modelName} records={records} />
        <GenericCreateModelData model={modelName} />
        <GenericUpdateModelData model={modelName} initialData={record}/>

These generic components will call server actions and update db models data accordingly. submitFormAction will accept 3 parameters - modelName, data, actionType. actionType will have these types - create, read, update, delete (CRUD) So server action will do CRUD operation on given model with given data


export async function submitFormAction(model: ModelKey, rawData: any, action : actionType) {
  const { schema, formMeta } = formConfig[model];

  const supabase = await createClient();

  const {
    data: { session },
  } = await supabase.auth.getSession();

  if (!session) {
    return null;
  }
  const result = schema.safeParse(rawData);

  if (!result.success) {
    throw new Error("Validation failed");
  }

  const filtered = Object.fromEntries(
    Object.entries(result.data).filter(([key]) => !formMeta[key]?.hidden)
  );

  // Example: assuming model is used directly as a Prisma model
  // You'd need dynamic routing to the right Prisma call, e.g.
  switch (model) {
    case "company":
      //todo
      break;
   
    case "employee":
      if (actionType == "CREATE"){
      try {
        filtered.createdById = session.user.id;
        filtered.companyId = "6a2b6c44-57dd-490e-811d-6a660d0a0cbc";

        const validated = EmployeeModel.parse(filtered); // this guarantees required fields
        const record = await prisma.employee.create({
          data: validated,
        });
        console.log("Record created on server ", record);
        const raw = convertToPrimitives(record);
        return raw;
      } catch (err) {
        console.log("Error occured", err);
      }
}
      break;

    default:
      throw new Error(`Model "${model}" not supported`);
  }
}

components

Components can have below folders/files

lib

prisma

This has schema.prisma (type generator, data source and models), seed.js (script to push test records) files

Published on: Jul 12, 2025, 07:04 AM  
 

Comments

Add your comment