Version 3 of the kit: - Radix UI replaced with Base UI (using the Shadcn UI patterns) - next-intl replaces react-i18next - enhanceAction deprecated; usage moved to next-safe-action - main layout now wrapped with [locale] path segment - Teams only mode - Layout updates - Zod v4 - Next.js 16.2 - Typescript 6 - All other dependencies updated - Removed deprecated Edge CSRF - Dynamic Github Action runner
6.6 KiB
description
| description |
|---|
| End-to-end feature implementation following Makerkit patterns across database, API, and UI layers |
Feature Builder
You are an expert at implementing complete features in Makerkit following established patterns across all layers.
You MUST use the specialized skills for each phase while building the feature.
- Database Schema:
postgres-supabase-expert - Server Layer:
server-action-builder - Forms:
forms-builder
Implementation Phases
Phase 1: Database Schema
Use postgres-supabase-expert skill.
- Create schema file in
apps/web/supabase/schemas/ - Enable RLS and create policies using helper functions
- Generate migration:
pnpm --filter web supabase:db:diff -f feature_name - Apply:
pnpm --filter web supabase migrations up - Generate types:
pnpm supabase:web:typegen
-- Example: apps/web/supabase/schemas/20-projects.sql
create table if not exists public.projects (
id uuid unique not null default extensions.uuid_generate_v4(),
account_id uuid references public.accounts(id) on delete cascade not null,
name varchar(255) not null,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
primary key (id)
);
alter table "public"."projects" enable row level security;
revoke all on public.projects from authenticated, service_role;
grant select, insert, update, delete on table public.projects to authenticated;
create policy "projects_read" on public.projects for select
to authenticated using (
account_id = (select auth.uid()) or
public.has_role_on_account(account_id)
);
create policy "projects_write" on public.projects for all
to authenticated using (
public.has_permission(auth.uid(), account_id, 'projects.manage'::app_permissions)
);
Phase 2: Server Layer
Use server-action-builder skill for detailed patterns.
Rule: Services are decoupled from interfaces. The service is pure logic that receives dependencies (database client, etc.) as arguments — it never imports framework-specific modules. The server action is a thin adapter that resolves dependencies and calls the service. This means the same service can be called from a server action, an MCP tool, a CLI command, or a unit test with zero changes.
Create in route's _lib/server/ directory:
- Schema (
_lib/schemas/feature.schema.ts) - Service (
_lib/server/feature.service.ts) — pure logic, dependencies injected, testable in isolation - Actions (
_lib/server/server-actions.ts) — thin adapter, no business logic
Phase 3: UI Components
Use form-builder skill for form patterns.
Create in route's _components/ directory:
- List component - Display items with loading states
- Form component - Create/edit with validation
- Detail component - Single item view
Phase 4: Page Integration
Create page in appropriate route group:
- Personal:
apps/web/app/home/(user)/feature/ - Team:
apps/web/app/home/[account]/feature/
// apps/web/app/home/[account]/projects/page.tsx
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { PageBody, PageHeader } from '@kit/ui/page';
import { ProjectsList } from './_components/projects-list';
interface Props {
params: Promise<{ account: string }>;
}
export default async function ProjectsPage({ params }: Props) {
const { account } = await params;
const client = getSupabaseServerClient();
const { data: projects } = await client
.from('projects')
.select('*')
.order('created_at', { ascending: false });
return (
<>
<PageHeader title="Projects" />
<PageBody>
<ProjectsList projects={projects ?? []} accountSlug={account} />
</PageBody>
</>
);
}
Phase 5: Navigation
Add routes to sidebar navigation in apps/web/config/team-account-navigation.config.tsx or apps/web/config/personal-account-navigation.config.tsx.
File Structure
apps/web/app/home/[account]/projects/
├── page.tsx # List page
├── [projectId]/
│ └── page.tsx # Detail page
├── _components/
│ ├── projects-list.tsx
│ ├── project-form.tsx
│ └── project-card.tsx
└── _lib/
├── schemas/
│ └── project.schema.ts
└── server/
├── project.service.ts
└── server-actions.ts
Verification Checklist
Database Layer
- Schema file created in
apps/web/supabase/schemas/ - RLS enabled on table
- Default permissions revoked
- Specific permissions granted to
authenticated - RLS policies use helper functions (
has_role_on_account,has_permission) - Indexes added for foreign keys and common queries
- Timestamps triggers added if applicable
- Migration generated and applied
- TypeScript types regenerated
Server Layer
- Zod schema in
_lib/schemas/ - Service class in
_lib/server/with dependencies injected (not imported) - Service contains all business logic — testable with mock dependencies
- Server actions are thin adapters — resolve dependencies, call service, handle revalidation
- Server actions use
enhanceAction - Actions have
auth: trueandschemaoptions - Logging added for operations
revalidatePathcalled after mutations- Error handling with
isRedirectErrorcheck if applicable (i.e. when using redirect() in a server action)
UI Layer
- Components in
_components/directory - Forms use
react-hook-formwithzodResolver - Loading states with
useTransition - Error display with
Alertcomponent data-testattributes for E2E testingTranscomponent for all user-facing strings- Toast notifications for success/error if applicable
Page Layer
- Page in correct route group (user vs team)
- Async params handled with
await params - Server-side data fetching
PageHeaderandPageBodycomponents used- Proper error boundaries
Navigation
- Path added to
config/paths.config.ts - Menu item added to navigation config
- Translation key added to
public/locales/en/common.json
Testing
- Page Object created for E2E tests
- Basic CRUD operations tested
- Error states tested
data-testselectors used in tests
Final Verification
# Type check
pnpm typecheck
# Lint
pnpm lint:fix
# Format
pnpm format:fix
# Test (if tests exist)
pnpm --filter web-e2e exec playwright test feature-name --workers=1
When you are done, run the code quality reviewer agent to verify the code quality.