Folder structure to create nextjs App with prisma and tailwind
Here are main folders at top level
- app
- components - React components and Shadcn components will go here
- lib - This can have client (client code), server (server code) and common folders(common between client and server).
- prisma
Here are main files at top level
- Environemt file - .env
- git related file - .gitignore
- Nextjs Related - middleware.ts, next.config.ts
- tailwind related - tailwind.config.ts and postcss.config.ts
- typescript related - tsconfig.json
app
- globals.css
- layout.tsx
- (main) - main app can stay here. This contains page.tsx and layout.tsx
- (custom) - If you need different layout, you can put that files under this route group. This contains page.tsx and layout.tsx
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
- hooks - You can store hooks here
- ui - stores shadcn components
- all react components can be grouped in folders based on modules like Employee, Company etc
lib
- common - zod types can go here. Because we need them in client as well as server. Supabase folder can go here as it has client and server codebase. Prisma client can also go here. Zod schema can be exported from here
- server - server actions can go here
- client - db schema and form meta can be joined here.
prisma
This has schema.prisma (type generator, data source and models), seed.js (script to push test records) files