Monorepo sync types
Regenerate backend types and re-export them via packages/shared/types/ in a turborepo monorepo so both apps/web/ and apps/mobile/ consume a single typed surface. Supports Supabase (via supabase gen types typescript), neon-drizzle (via drizzle-kit introspect/pull + InferSelectModel/InferInsertModel re-exports — the default DB stack), tRPC (via inference from the server router import), and custom REST (via a manual or zod-derived schema). Reads .workflow/meta.json with stack.framework="monorepo" and stack.{auth,db} populated. Use when "rigenera i tipi da Supabase", "sync DB schema to packages/shared/types", "il backend è cambiato, aggiorna i tipi", "sync types from tRPC", "sync Drizzle types after a schema change". Not for: creating shared packages from scratch (use monorepo-add-shared-package), wiring the backend client itself (use module-add or rn-module-add).From its SKILL.md
npx -y skills add lukedj78/dev-flow --skill monorepo-sync-typesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
3 things to look at
- reads credentialsReads from 2 credential sources: `process.env.EXPO_PUBLIC_SUPABASE_URL` and 1 more.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- runs commandsInstructs the agent to run 6 commands, including `npx supabase gen types typescript --project-id <ref> > packages/shared/src/types/database.ts` and 5 more.
SKILL.md
9.2 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it
monorepo-sync-types — regenerate + propagate backend types across the monorepo
Contract
See references/contracts.md (vendored from dev-flow). Key facts:
- Reads
<project-root>/.workflow/meta.json#stack.framework— must be"monorepo". - Reads
meta.json#stack.dbandstack.authto determine the source of truth:"supabase"→ runsupabase gen types typescriptagainst the linked project."neon-drizzle"→ introspect/pull viadrizzle-kitand re-exportInferSelectModel/InferInsertModelfrom the Drizzle schema (this is the default DB for most projects — seemodule-add/references/module-db.md)."trpc"→ re-import the server router type and re-export it."firebase"→ no auto-gen, but normalize fixed types from Firestore rules (limited)."custom-rest"→ ask the user where the schema lives (Zod / OpenAPI / TS source).
- Writes generated types into
packages/shared/src/types/(NOT intopackages/api/, becausepackages/sharedis consumed by BOTH apps including for non-backend code). - Idempotent: regenerating overwrites; no-diff prints "already in sync".
- Does NOT modify
phase.
When this skill applies
- User says: "rigenera i tipi", "sync DB schema", "supabase types update", "the backend changed, update types".
- Orchestrator does NOT route here automatically — invoked on demand (e.g., after a backend migration).
Knowledge dependencies
monorepo-bootstrap/references/structure.md— forpackages/shared/src/types/location.rn-backend/references/<provider>.md— for provider-specific gen commands.module-add/references/module-db.md— for the web side conventions.
Workflow
Step 1 — Verify preconditions
Read .workflow/meta.json. Abort if:
stack.framework != "monorepo".stack.db == nullANDstack.auth == null→ "No backend configured yet. Runmodule-add dborrn-module-add dbfirst."
Read meta.json#stack_config.supabase_project_ref (or equivalent) for the gen command target.
Step 2 — Branch by provider
Supabase
Run the canonical gen command:
npx supabase gen types typescript --project-id <ref> > packages/shared/src/types/database.ts
(or --linked if supabase login + supabase link already done.)
Add to packages/shared/src/index.ts:
export type { Database } from './types/database';
Add a typed Supabase client wrapper in packages/api/src/client.ts:
import { createClient } from '@supabase/supabase-js';
import type { Database } from '@<slug>/shared/types/database';
export const supabase = createClient<Database>(
process.env.EXPO_PUBLIC_SUPABASE_URL!, // or NEXT_PUBLIC_ on web side — see decision-tree
process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
);
Both web and mobile apps now import supabase from @<slug>/api/client and get full type safety: supabase.from('posts').select(...) is fully typed.
neon-drizzle
This is the default DB stack for most projects (see module-add/references/module-db.md). Drizzle's schema file is already the source of truth for types — there's no separate "gen types" command — so syncing means two things: (1) make sure the schema file matches the live DB, and (2) re-export row types to packages/shared/.
- If drift with the live database is suspected (someone ran DDL outside Drizzle, or this is the first sync), refresh the schema from the database:
This introspects the Neon database and (re)writes the schema file. Confirm the target path againstnpx drizzle-kit pullmodule-add/references/module-db.md— by defaultapps/web/lib/db/schema.ts. - Re-export typed rows via
InferSelectModel/InferInsertModel(fromdrizzle-orm) intopackages/shared/src/types/db.ts:import type { InferSelectModel, InferInsertModel } from 'drizzle-orm'; import { users, posts } from '../../../../apps/web/lib/db/schema'; // adjust relative path to the real schema location export type User = InferSelectModel<typeof users>; export type NewUser = InferInsertModel<typeof users>; export type Post = InferSelectModel<typeof posts>; export type NewPost = InferInsertModel<typeof posts>; - Add to
packages/shared/src/index.ts:export * from './types/db';
If the schema file was hand-edited to add tables/columns that haven't reached the database yet, don't pull (it would overwrite the new columns) — instead run the migration first (drizzle-kit generate + drizzle-kit migrate; drizzle-kit push is dev-only and destructive on column drops, per module-add/references/module-db.md), then re-export types from the now-current schema.
tRPC
The types flow automatically via TS inference — no gen step. The skill verifies the wiring is correct:
packages/api/src/client.tsimports the server'sAppRoutertype:import type { AppRouter } from '../../../server/src/router'; export const trpc = createTRPCReact<AppRouter>();- (Optional) Re-export Zod-derived input/output types from
packages/shared/src/types/:import type { inferRouterOutputs } from '@trpc/server'; import type { AppRouter } from '../../api/src/router-type'; // a re-export export type RouterOutputs = inferRouterOutputs<AppRouter>;
If the server lives outside the monorepo: import via a published @<scope>/types package OR copy the router types file into packages/shared/src/types/router.d.ts and tell the user to keep it in sync.
Firebase
No auto-gen. The skill creates skeleton types from Firestore rules + user input:
- Ask the user for the collections in use (or parse
firestore.rules). - For each collection, ask the user for the document shape OR generate a placeholder.
- Write
packages/shared/src/types/firestore.tswith the shapes.
This is a manual-maintenance scenario — Firebase doesn't ship a type-gen tool the way Supabase does.
Custom REST
Ask the user where the schema lives:
- Zod schemas in the server: ask for the path, generate
packages/shared/src/types/api.tsviaz.infer<typeof Schema>for each. - OpenAPI spec: ask for the YAML/JSON path, run
npx openapi-typescript <spec> -o packages/shared/src/types/api.d.ts. - TS source in a separate repo: copy the relevant types into
packages/shared/src/types/api.tsand instruct the user to keep in sync.
Step 3 — Verify
Run pnpm tsc --noEmit from apps/web, apps/mobile, and packages/shared. Must pass.
If types changed in ways that broke existing code, report each error — the user fixes the consumers (the skill doesn't auto-fix arbitrary call sites).
Step 4 — Update meta.json + commit
{
"stack_config": {
"types_last_synced_at": "<iso>",
"types_source": "supabase" // or "neon-drizzle" / "trpc" / "firebase" / "custom-rest"
},
"history": [
...,
{ "skill": "monorepo-sync-types", "ran_at": "<iso>", "outputs": [...] }
]
}
If git repo: commit with chore(types): sync from <provider>.
Common anti-patterns (NEVER do)
- ❌ Generate types into
packages/api/— types are general-purpose, both apps consume them; they live inpackages/shared/. - ❌ Hand-edit generated type files — they will be overwritten next sync.
- ❌ Skip the
packages/shared/src/index.tsre-export — apps would need deep import paths. - ❌ Forget the
--project-idflag onsupabase gen types— it will fail or generate the wrong schema. - ❌ Run gen without verifying the user is logged into the right project (
supabase status). - ❌ For tRPC: import the server router via a brittle relative path (
../../../../server/src/router). Instead use a workspace import if the server is in the monorepo, or a.d.tsshim file if not.
Updating meta.json (recommended pattern)
python3 .../dev-flow/scripts/update_meta.py <project-root> record-artifact \
--path packages/shared/src/types/database.ts --produced-by 'monorepo-sync-types'
python3 .../dev-flow/scripts/update_meta.py <project-root> append-history \
--skill 'monorepo-sync-types' --inputs '{"source": "supabase"}' --outputs '{"types": "packages/shared/src/types/database.ts"}'
Sources
- Spec:
docs/superpowers/specs/2026-05-29-monorepo-set-design.md - Official: https://supabase.com/docs/guides/api/rest/generating-types
- Official: https://trpc.io/docs/quickstart#using-trpcs-types-on-the-client
monorepo-bootstrap/references/structure.md
What ships with it: 4 files
35.1 KB alongside SKILL.md, 1 of them executable
references/
- contracts.md28.9 KB
- decision-tree.md4.2 KB
- .gitkeep0 B
scripts/
- sync-supabase.shruns2.0 KB