Use Cases (अपडेट: 1/6/2026)

Claude Code के साथ Supabase: Auth, RLS, Storage और Edge Functions की practical guide

Claude Code से Supabase सुरक्षित तरीके से बनाएं: Auth, RLS, Storage, Edge Functions, migrations, tests और review checklist।

Claude Code के साथ Supabase: Auth, RLS, Storage और Edge Functions की practical guide

Supabase prototype को बहुत तेज बना देता है, लेकिन यही speed risk भी बन सकती है। अगर आप Claude Code से सिर्फ “login और database जोड़ दो” कहते हैं, तो screen चल सकती है, पर Row Level Security, Storage policies, migrations और server-side session handling छूट सकते हैं।

Supabase एक BaaS है, यानी Backend as a Service। इसमें Postgres database, Auth, Storage, Edge Functions और generated APIs एक साथ मिलते हैं। Beginners के लिए इसका मतलब है कम backend setup; production teams के लिए असली फायदा है कि permissions को React UI में नहीं, बल्कि Postgres और RLS में enforce किया जा सकता है।

यह guide Next.js App Router और TypeScript पर आधारित है। इसमें Claude Code workflow शामिल है: requirements file, schema/RLS review, migration और type-generation commands, copy-paste code, testing steps, pitfalls और final review checklist। Authentication के लिए authentication implementation guide, database modeling के लिए database design और बदलाव संभालने के लिए database migration automation भी देखें।

Official docs को baseline बनाएं

Implementation से पहले ये official links देखें: Supabase Docs, Auth, Row Level Security, Edge Functions, और Storage

नए Next.js projects में cookie-based Auth के लिए @supabase/ssr और browser-safe access के लिए NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY बेहतर default है। पुराने projects में anon key मिल सकती है, लेकिन नए implementation में publishable key और explicit RLS policies को आधार बनाना चाहिए।

Architecture

Example feature project notes है: logged-in user अपनी notes बनाता है, private attachment upload करता है, और Edge Function से notification जैसी processing trigger करता है।

flowchart LR
  User["Browser"] --> Next["Next.js App Router"]
  Next --> SSR["@supabase/ssr client"]
  SSR --> Auth["Supabase Auth"]
  SSR --> DB["Postgres tables"]
  SSR --> Storage["Storage bucket"]
  Next --> Fn["Edge Function"]
  Fn --> DB
  DB --> RLS["RLS policies"]
  Storage --> StorageRLS["storage.objects policies"]

Security boundary Postgres RLS और Storage policy में होनी चाहिए। UI checks helpful हैं, लेकिन वे final authorization layer नहीं हैं।

Claude Code के लिए requirements file

# docs/supabase-notes-requirements.md

## Goal
Build a Supabase-backed project notes feature in Next.js App Router.

## Stack
- Next.js App Router
- TypeScript
- @supabase/supabase-js
- @supabase/ssr
- Supabase Auth, Postgres, Storage, Edge Functions

## Data model
- project_notes table
- Each note belongs to auth.users.id through owner_id
- Public notes are readable by anyone
- Private notes are readable only by the owner
- Owners can insert, update, and delete only their own notes

## Security rules
- Never expose a secret key or service role key in browser code
- Use NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY for browser and SSR clients
- Enable RLS on every public table
- Use explicit TO anon or TO authenticated in every policy
- Storage uploads must be restricted to a user-owned folder

## Claude Code workflow
1. Create SQL migration first.
2. Review RLS policies before writing UI.
3. Generate TypeScript database types.
4. Implement Supabase clients.
5. Implement server actions and upload helper.
6. Add test or manual verification commands.
7. Return a review checklist with file paths.

पहला prompt सिर्फ SQL migration के लिए रखें। Masa के notes में UI-first approach में owner_id को form input से लेने वाली गलती ज्यादा दिखी; migration-first approach में review छोटा और साफ रहा।

Setup और environment

npm install @supabase/supabase-js @supabase/ssr zod
npm install --save-dev supabase vitest
npx supabase init
npx supabase start
NEXT_PUBLIC_SUPABASE_URL=https://your-project-ref.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxxxxxxxxxxxxxxxxxxx

Real keys को prompt में paste न करें। Secret key या service role key को NEXT_PUBLIC_ में रखना critical mistake है।

Migration और RLS

-- supabase/migrations/202606010001_create_project_notes.sql
create table if not exists public.project_notes (
  id uuid primary key default gen_random_uuid(),
  owner_id uuid not null references auth.users(id) on delete cascade,
  title text not null check (char_length(title) between 1 and 120),
  body text not null default '',
  visibility text not null default 'private'
    check (visibility in ('private', 'public')),
  attachment_path text,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create index if not exists project_notes_owner_created_idx
  on public.project_notes (owner_id, created_at desc);

alter table public.project_notes enable row level security;

create policy "Anyone can read public notes"
on public.project_notes
for select
to anon, authenticated
using (
  visibility = 'public'
  or (select auth.uid()) = owner_id
);

create policy "Owners can insert notes"
on public.project_notes
for insert
to authenticated
with check ((select auth.uid()) = owner_id);

create policy "Owners can update notes"
on public.project_notes
for update
to authenticated
using ((select auth.uid()) = owner_id)
with check ((select auth.uid()) = owner_id);

create policy "Owners can delete notes"
on public.project_notes
for delete
to authenticated
using ((select auth.uid()) = owner_id);

Storage bucket private रखें और path के पहले folder को user id मानें।

insert into storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
values (
  'note-attachments',
  'note-attachments',
  false,
  5242880,
  array['image/png', 'image/jpeg', 'application/pdf']
)
on conflict (id) do update
set public = excluded.public,
    file_size_limit = excluded.file_size_limit,
    allowed_mime_types = excluded.allowed_mime_types;

create policy "Users can upload own note attachments"
on storage.objects
for insert
to authenticated
with check (
  bucket_id = 'note-attachments'
  and (select auth.uid())::text = (storage.foldername(name))[1]
);

create policy "Users can read own note attachments"
on storage.objects
for select
to authenticated
using (
  bucket_id = 'note-attachments'
  and (select auth.uid())::text = (storage.foldername(name))[1]
);
npx supabase db reset
npx supabase gen types typescript --local > src/lib/database.types.ts
npm run typecheck

Supabase clients

// src/lib/supabase/client.ts
import { createBrowserClient } from "@supabase/ssr";
import type { Database } from "@/lib/database.types";

export function createClient() {
  return createBrowserClient<Database>(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
  );
}
// src/lib/supabase/server.ts
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
import type { Database } from "@/lib/database.types";

export async function createClient() {
  const cookieStore = await cookies();

  return createServerClient<Database>(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll();
        },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) => {
              cookieStore.set(name, value, options);
            });
          } catch {
            // Server Components cannot set cookies directly.
          }
        },
      },
    },
  );
}

Auth, CRUD और upload

// app/login/actions.ts
"use server";

import { redirect } from "next/navigation";
import { createClient } from "@/lib/supabase/server";

export async function signIn(formData: FormData) {
  const email = String(formData.get("email") ?? "");
  const password = String(formData.get("password") ?? "");
  const supabase = await createClient();

  const { error } = await supabase.auth.signInWithPassword({ email, password });

  if (error) return { error: error.message };
  redirect("/dashboard");
}
// src/features/notes/actions.ts
"use server";

import { revalidatePath } from "next/cache";
import { createClient } from "@/lib/supabase/server";

type CreateNoteInput = {
  title: string;
  body: string;
  visibility?: "private" | "public";
  attachmentPath?: string | null;
};

export async function createNote(input: CreateNoteInput) {
  const supabase = await createClient();
  const {
    data: { user },
    error: userError,
  } = await supabase.auth.getUser();

  if (userError || !user) throw new Error("Authentication required");

  const { data, error } = await supabase
    .from("project_notes")
    .insert({
      owner_id: user.id,
      title: input.title,
      body: input.body,
      visibility: input.visibility ?? "private",
      attachment_path: input.attachmentPath ?? null,
    })
    .select("id,title,visibility")
    .single();

  if (error) throw error;
  revalidatePath("/dashboard");
  return data;
}
// src/features/notes/upload-note-attachment.ts
"use client";

import { createClient } from "@/lib/supabase/client";

export async function uploadNoteAttachment(file: File, userId: string) {
  const supabase = createClient();
  const ext = file.name.split(".").pop()?.toLowerCase() ?? "bin";
  const path = `${userId}/${crypto.randomUUID()}.${ext}`;

  const { error } = await supabase.storage
    .from("note-attachments")
    .upload(path, file, {
      cacheControl: "3600",
      upsert: false,
      contentType: file.type,
    });

  if (error) throw error;
  return path;
}

Edge Function

npx supabase functions new notify-note-created
// supabase/functions/notify-note-created/index.ts
import { createClient } from "npm:@supabase/supabase-js@2";

Deno.serve(async (req) => {
  const authorization = req.headers.get("Authorization");
  if (!authorization) {
    return Response.json({ error: "Missing authorization" }, { status: 401 });
  }

  const supabase = createClient(
    Deno.env.get("SUPABASE_URL")!,
    Deno.env.get("SUPABASE_ANON_KEY")!,
    { global: { headers: { Authorization: authorization } } },
  );

  const {
    data: { user },
    error: userError,
  } = await supabase.auth.getUser();

  if (userError || !user) {
    return Response.json({ error: "Authentication required" }, { status: 401 });
  }

  return Response.json({ ok: true, userId: user.id });
});
npx supabase functions serve notify-note-created --env-file .env.local
npx supabase functions deploy notify-note-created

3 practical use cases

Use caseSupabase featuresClaude Code taskHuman review
SaaS team notesAuth, Postgres, RLSTables, Server Actions, list UITeam boundary vs owner boundary
Member-only resourcesAuth, Storage, signed URLsUpload UI, download API, audit logPrivate bucket and path rules
Event reservationsPostgres, Edge FunctionsReservation schema, notificationDouble booking and retries

Common pitfalls

RLS enable करने से ही security पूरी नहीं होती। Policy नहीं है तो data block होगा; बहुत broad using (true) है तो data leak होगा।

Client में secret key रखना सबसे गंभीर गलती है। Browser को publishable key ही दिखनी चाहिए।

Storage path UI से arbitrary न लें। Code और policy दोनों में userId/random-file जैसा एक ही pattern होना चाहिए।

Migration के बाद database.types.ts regenerate करें, वरना Claude Code पुराने schema पर code लिखता रहेगा।

Edge Function भी public HTTP endpoint है। Authorization validate करें और RLS-aware client use करें।

Review prompt

Review only the Supabase integration.
Check RLS, explicit TO roles, publishable key usage, no secret key in client code,
owner_id derived from authenticated user, Storage paths matching policies,
generated types in sync, and Edge Functions validating Authorization.
Return findings with file paths and line numbers.

Release से पहले anonymous public read, logged-in private read, दूसरे user की note update failure, दूसरे folder में upload failure, और unauthenticated Edge Function call failure जरूर test करें।

ClaudeCodeLab Supabase + Claude Code implementation review, RLS design, migrations, CLAUDE.md rules और team training में मदद कर सकता है। Team adoption के लिए Claude Code training and consultation देखें।

इस workflow को try करने पर सबसे बड़ा लाभ RLS migration को UI से पहले review कराने से मिला। Masa के notes में migration-first approach ने diff छोटा रखा और release checklist को ज्यादा clear बनाया।

#Claude Code #Supabase #BaaS #PostgreSQL #RLS #Auth
मुफ़्त

मुफ़्त PDF: Claude Code cheatsheet

Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.

हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.

Masa

लेखक के बारे में

Masa

Claude Code workflow और team adoption पर काम करने वाला engineer.