Set up database tables with Row-Level Security policies, configure authenticated connections, and implement secure user-scoped data access patterns (Do not apply this skill unless specifically asked by user) (project)
From schema0-devnpx claudepluginhub schema0/ai-agent-plugins --plugin schema0-devThis skill is limited to using the following tools:
EXAMPLES.mdREFERENCE.mdSCHEMA.mdPrerequisite: This skill requires a schema0 template project. Before using, ensure
CLAUDE.mdexists in the project root and read it for project rules and conventions.
Use this skill when you need to:
⚠️ ALWAYS start with scaffolding before writing code:
bun run scaffold-scripts/generate.ts rls-service <name>
Example:
bun run scaffold-scripts/generate.ts rls-service products
| Output File | Location | Template | Responsibility |
|---|---|---|---|
| RLS Router | packages/api/src/routers/[name].ts | scaffold-templates/rls-service.hbs | Generates ORPC router with protectedProcedure, createRLSTransaction for authenticated database access, and CRUD operations with RLS enforcement |
What the template generates:
getAll - Fetches all items (RLS automatically filters by user)getById - Fetches single item by ID (RLS ensures user owns it)create - Creates new item with automatic userId assignmentupdate - Updates item (RLS ensures user owns it)delete - Deletes item (RLS ensures user owns it)protectedProcedure for authenticationcreateRLSTransaction for secure database accessLocation: packages/db/src/schema/<table>.ts
import { pgTable, text, timestamp, bigint, boolean } from "drizzle-orm/pg-core";
import { crudPolicy, authUid } from "drizzle-orm/neon";
import { authenticatedUserRole } from "./index";
// Example table with RLS
export const tasks = pgTable(
"tasks",
{
id: bigint("id", { mode: "number" })
.primaryKey()
.generatedAlwaysAsIdentity(),
userId: text("user_id").notNull(),
title: text("title").notNull(),
completed: boolean("completed").default(false).notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
},
(table) => [
crudPolicy({
role: authenticatedUserRole,
read: authUid(table.userId), // Users can only read their own tasks
modify: authUid(table.userId), // Users can only modify their own tasks
}),
],
);
Key Points:
authenticatedUserRole is defined and exported in packages/db/src/schema/index.tsauthUid() and crudPolicy() are imported from drizzle-orm/neonuserId column must be set when inserting recordspackages/db/src/schema/index.ts:export * from "./tasks";
packages/api/src/routers/index.ts:import { tasksRouter } from "./tasks";
export const appRouter = {
// ... other routers
tasks: tasksRouter,
};
Use React Query for data fetching with proper optimistic updates (see route.hbs template for reference).
protectedProcedure - RLS requires authenticated requestsuserId from context - Never trust user-provided userIdcreateRLSTransaction - Ensures RLS policies are enforcedany type in generated code — use proper types, generics, or unknown with type narrowing// @ts-ignore, // @ts-expect-error, // @ts-nocheck, or // eslint-disable — fix the type error insteadFor detailed schema patterns, see SCHEMA.md. For service examples, see EXAMPLES.md. For common mistakes to avoid, see REFERENCE.md.