# Makerkit Guidelines ## Project Stack - Framework: Next.js 15 App Router, TypeScript, React, Node.js - Backend: Supabase with Postgres - UI: Shadcn UI, Tailwind CSS - Key libraries: React Hook Form, React Query, Zod, Lucide React - Focus: Code clarity, Readability, Best practices, Maintainability ## Project Structure ``` /apps/web/ /app /home # protected routes /(user) # user workspace /[account] # team workspace /(marketing) # marketing pages /auth # auth pages /components # global components /config # global config /lib # global utils /content # markdoc content /supabase # supabase root ``` ## Core Principles ### Data Flow 1. Server Components - Use Supabase Client directly via `getSupabaseServerClient` - Handle errors with proper boundaries - Example: ```tsx async function ServerComponent() { const client = getSupabaseServerClient(); const { data, error } = await client.from('notes').select('*'); if (error) return ; return ; } ``` 2. Client Components - Use React Query for data fetching - Implement proper loading states - Example: ```tsx function useNotes() { const { data, isLoading } = useQuery({ queryKey: ['notes'], queryFn: async () => { const { data } = await fetch('/api/notes'); return data; } }); return { data, isLoading }; } ``` ### Server Actions - Name files as "server-actions.ts" in `_lib/server` folder - Export with "Action" suffix - Use `enhanceAction` with proper typing - Example: ```tsx export const createNoteAction = enhanceAction( async function (data, user) { const client = getSupabaseServerClient(); const { error } = await client .from('notes') .insert({ ...data, user_id: user.id }); if (error) throw error; return { success: true }; }, { auth: true, schema: NoteSchema, } ); ``` ### Route Handlers - Use `enhanceRouteHandler` to wrap route handlers - Use Route Handlers when data fetching from Client Components ## Database & Security ### RLS Policies - Strive to create a safe, robust, secure and consistent database schema - Always consider the compromises you need to make and explain them so I can make an educated decision. Follow up with the considerations make and explain them. - Enable RLS by default and propose the required RLS policies - `public.accounts` are the root tables for the application - Implement cascading deletes when appropriate - Ensure strong consistency considering triggers and constraints - Always use Postgres schemas explicitly (e.g., `public.accounts`) ## Forms Pattern ### 1. Schema Definition ```tsx // schema/note.schema.ts import { z } from 'zod'; export const NoteSchema = z.object({ title: z.string().min(1).max(100), content: z.string().min(1), category: z.enum(['work', 'personal']), }); ``` ### 2. Form Component ```tsx 'use client'; export function NoteForm() { const [pending, startTransition] = useTransition(); const form = useForm({ resolver: zodResolver(NoteSchema), defaultValues: { title: '', content: '', category: 'personal' } }); const onSubmit = (data: z.infer) => { startTransition(async () => { try { await createNoteAction(data); form.reset(); } catch (error) { // Handle error } }); }; return (
( Title )} /> {/* Other fields */} ); } ``` ## Error Handling - Consider logging asynchronous requests in server code using the `@kit/shared/logger` - Handle promises and async/await gracefully - Consider the unhappy path and handle errors appropriately ### Structured Logging ```tsx const ctx = { name: 'create-note', userId: user.id, noteId: note.id }; logger.info(ctx, 'Creating new note...'); try { await createNote(); logger.info(ctx, 'Note created successfully'); } catch (error) { logger.error(ctx, 'Failed to create note', { error }); throw error; } ``` ## Context Management In client components, we can use the `useUserWorkspace` hook to access the user's workspace data. ### Personal Account ```tsx 'use client'; function PersonalDashboard() { const { workspace, user } = useUserWorkspace(); if (!workspace) return null; return (

Welcome, {user.email}

); } ``` ### Team Account In client components, we can use the `useTeamAccountWorkspace` hook to access the team account's workspace data. It only works under the `/home/[account]` route. ```tsx 'use client'; function TeamDashboard() { const { account, user } = useTeamAccountWorkspace(); return (

{account.name}

); } ``` ## UI Components - Reusable UI components are defined in the "packages/ui" package named "@kit/ui". - By exporting the component from the "exports" field, we can import it using the "@kit/ui/{component-name}" format. ## Creating Pages When creating new pages ensure: - The page is exported using `withI18n(Page)` to enable i18n. - The page has the required and correct metadata using the `metadata` or `generateMetadata` function. - Don't worry about authentication, it's handled in the middleware.