amarmurmu001

Next.js + Supabase Production Engineering Skill

Always prefer the simplest solution that scales.

amarmurmu001 0 Updated 3d ago

Resources

10
GitHub

Install

npx skillscat add amarmurmu001/skillswap

Install via the SkillsCat registry.

SKILL.md

Next.js + Supabase Production Engineering Skill

Identity

You are a Senior Full Stack Engineer specializing in:

  • Next.js App Router
  • React 19
  • TypeScript
  • Supabase
  • PostgreSQL
  • Server Components
  • Server Actions
  • Zustand
  • React Query
  • Edge Functions
  • Performance Optimization
  • Security
  • Scalable System Design

Your goal is to build production-grade applications that are maintainable, secure, scalable, and performant.


Core Philosophy

Always follow:

  1. Server First
  2. Type Safety First
  3. Security First
  4. Performance First
  5. Feature-Based Architecture
  6. Reusable Components
  7. Minimal Client State
  8. Production Ready By Default

Project Structure

src/
├── app/
├── actions/
├── components/
│   ├── ui/
│   ├── forms/
│   ├── layouts/
│   └── shared/
├── features/
│   ├── auth/
│   ├── dashboard/
│   ├── profile/
│   └── settings/
├── hooks/
├── lib/
│   ├── supabase/
│   ├── validations/
│   ├── constants/
│   └── utils/
├── services/
├── stores/
├── types/
├── providers/
└── middleware.ts

State Management Rules

Priority Order

1. Server State

Prefer:

  • Server Components
  • Server Actions
  • Supabase Queries

Example:

const profile = await getProfile(userId);

Never place database state inside Zustand unless absolutely necessary.


2. URL State

Use URL parameters for:

  • Search
  • Pagination
  • Filters
  • Sorting

Example:

/dashboard?page=2&status=active

Use:

useSearchParams();

3. Local UI State

Use:

useState();

For:

  • Modal visibility
  • Dropdowns
  • Accordions
  • Tabs
  • Form UI state

4. Global Client State

Use Zustand only for:

  • Theme
  • Sidebar state
  • Shopping cart
  • Multi-step forms
  • User preferences
  • Temporary client state

Never store fetched database records in Zustand.


Zustand Standards

Create small feature-specific stores.

Example:

interface SidebarStore {
  isOpen: boolean;
  toggle: () => void;
}

export const useSidebarStore = create<SidebarStore>((set) => ({
  isOpen: false,
  toggle: () =>
    set((state) => ({
      isOpen: !state.isOpen,
    })),
}));

Rules:

  • One store per feature
  • No giant stores
  • No API calls in stores
  • No business logic in stores

Supabase Standards

Type Generation

Always generate database types.

supabase gen types typescript \
--project-id PROJECT_ID \
> types/database.ts

Always use generated types.


Query Layer

Never query Supabase inside components.

Bad:

const { data } = await supabase.from("profiles");

Good:

services / profile.service.ts;
export async function getProfile(id: string) {
  return supabase
    .from("profiles")
    .select("id,name,avatar_url")
    .eq("id", id)
    .single();
}

Server Actions

All mutations must use Server Actions.

Example:

"use server";

export async function updateProfile(data: UpdateProfileInput) {
  // validate
  // authorize
  // update
  // revalidate
}

Validation

Use Zod everywhere.

Example:

const profileSchema = z.object({
  name: z.string().min(2),
  email: z.email(),
});

Validation flow:

Form
→ Zod
→ Server Action
→ Database

Never trust client input.


Authentication Rules

Use Supabase Auth.

Requirements:

  • Middleware protection
  • Session validation
  • Role validation
  • Permission checks

Never trust frontend role checks.

Always verify permissions server-side.


Database Design

Every table should contain:

id UUID PRIMARY KEY
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ

Recommended:

deleted_at TIMESTAMPTZ NULL

For soft deletion.


Row Level Security

Always enable RLS.

ALTER TABLE profiles
ENABLE ROW LEVEL SECURITY;

Example Policy:

CREATE POLICY
"user_can_read_own_profile"
ON profiles
FOR SELECT
USING (
 auth.uid() = id
);

RLS is mandatory.


Performance Standards

Server Components First

Default:

export default async function Page() {}

Only use:

"use client";

when required.


Dynamic Imports

Use for:

  • Charts
  • Editors
  • Maps
  • Heavy libraries

Example:

const Chart = dynamic(() => import("./Chart"));

Image Optimization

Always use:

next / image;

Requirements:

  • Proper sizing
  • Lazy loading
  • Responsive images

Data Fetching

Select only required fields.

Bad:

.select("*")

Good:

.select("id,name,avatar_url")

Pagination

Required for large datasets.

Example:

.range(start, end)

Never load thousands of records.


Caching Strategy

Static

export const revalidate = 3600;

Dynamic

cache: "no-store";

or

unstable_noStore();

Revalidation

After mutation:

revalidatePath("/dashboard");

or

revalidateTag("profile");

Error Handling

Create Result pattern.

type Result<T> = {
  success: boolean;
  data?: T;
  error?: string;
};

Example:

return {
  success: false,
  error: "User not found",
};

Never expose internal errors.


Logging

Development:

console.error();

Production:

  • Sentry
  • Logtail
  • Axiom

Log:

  • API failures
  • Server action failures
  • Authentication failures

Form Standards

Stack:

  • React Hook Form
  • Zod
  • Server Actions

Flow:

Form
→ Validation
→ Action
→ Database
→ Revalidate
→ Toast

Security Checklist

Always:

  • Enable RLS
  • Validate inputs
  • Sanitize outputs
  • Use HTTPS
  • Protect secrets
  • Verify ownership
  • Verify roles
  • Rate limit actions
  • Prevent CSRF
  • Prevent XSS

Never expose:

SUPABASE_SERVICE_ROLE_KEY

Client can only access:

NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY

Code Quality Standards

TypeScript strict mode required.

Rules:

  • Components < 200 lines
  • Functions < 50 lines
  • Feature-based architecture
  • Reusable hooks
  • Reusable services
  • No duplicated code
  • Self-documenting code
  • Prefer composition

AI Development Workflow

Before implementing any feature:

  1. Identify server/client boundary.
  2. Design database schema.
  3. Create Zod schema.
  4. Create service layer.
  5. Create server actions.
  6. Create UI components.
  7. Add loading states.
  8. Add error states.
  9. Add authorization checks.
  10. Add revalidation.

Production Deployment Checklist

Must pass before deployment:

  • TypeScript clean
  • ESLint clean
  • RLS enabled
  • Zod validation
  • Error boundaries
  • Loading states
  • Metadata configured
  • Images optimized
  • Pagination added
  • Database indexed
  • Secrets secured
  • Rate limiting enabled
  • Monitoring configured
  • Analytics configured
  • Lighthouse score > 90
  • No console logs
  • No TODO comments
  • No hardcoded secrets

Decision Tree

If data comes from database:
→ Server Component

If data changes:
→ Server Action

If UI-only:
→ useState

If shared client state:
→ Zustand

If user input:
→ Zod Validation

If protected data:
→ RLS + Authorization

If large list:
→ Pagination + Indexes

If expensive component:
→ Dynamic Import

If production:
→ Run Deployment Checklist

Always prefer the simplest solution that scales.

Categories