Skip to content

feat: generate Supabase TypeScript types for typed database client#4658

Open
GMetaxakis wants to merge 5 commits intoONEARMY:masterfrom
GMetaxakis:feat/supabase-typescript-types
Open

feat: generate Supabase TypeScript types for typed database client#4658
GMetaxakis wants to merge 5 commits intoONEARMY:masterfrom
GMetaxakis:feat/supabase-typescript-types

Conversation

@GMetaxakis
Copy link
Copy Markdown
Contributor

PR Checklist

  • - Unit and/or e2e tests for the changes that have been added (for bug fixes / features)

PR Type

  • Feature

What is the new behavior?

  • Added src/database.types.ts with generated TypeScript types for all 26 tables, 14 RPC functions, and 6 enums
  • The Supabase client in supabase.server.ts is now typed with createServerClient<Database>(...), giving compile-time checks on table names, column names, insert/update payloads, and RPC function signatures
  • Added db:types script to package.json for regenerating types from a local Supabase instance (yarn db:types)
  • Fixed all resulting type errors across 27 route files (Date↔string conversions, Json↔DBMedia casts, route param narrowing, enum type assertions)

Does this PR introduce a DB Schema Change or Migration?

  • No

Git Issues

Closes #4344

@cypress
Copy link
Copy Markdown

cypress bot commented Mar 6, 2026

onearmy-community-platform    Run #8808

Run Properties:  status check passed Passed #8808  •  git commit 5a63edd545: Merge remote-tracking branch 'origin/master' into feat/supabase-typescript-types
Project onearmy-community-platform
Branch Review pull/4658
Run status status check passed Passed #8808
Run duration 06m 57s
Commit git commit 5a63edd545: Merge remote-tracking branch 'origin/master' into feat/supabase-typescript-types
Committer Georgios Metaxakis
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 1
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 90
View all changes introduced in this branch ↗︎

.from('projects')
.update({
modified_at: new Date(),
modified_at: new Date().toISOString(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this still work, or fixes a bug?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still works the same — the Supabase client serializes Date objects to ISO strings internally, so the runtime behavior is unchanged. The explicit .toISOString() is needed because the typed client expects string for timestamp columns, not Date.

@mariojsnunes
Copy link
Copy Markdown
Contributor

I've tried this before, adding all those casts didn't feel great.

  • Are there any cases where it actually improves type checking?
  • Can we remove any of our DB types like DBProfile, DBComment ?

@GMetaxakis
Copy link
Copy Markdown
Contributor Author

Good points. The main wins right now are on inserts/updates and RPC calls — those get real type checking (correct column names, enum values, etc.). The read-side casts are ugly and don't add much.

Let me give it a try to remove the DB types like DBProfile, DBComment, etc. and replace them with the generated types (Tables<'profiles'>, Tables<'comments'>). That would eliminate both the manual types and the as unknown as casts.

- Align DB model date types with Supabase returns (Date → string)
- Create supabase.types.ts with toJson, fromJson, fromJsonArray,
  dbResult, dbResultArray helpers
- Replace all 56 as unknown as casts in route files with helpers
- Update IDBDocSB base interface and all implementing types
- Fix mock data and test factories for string date types
- Move database.types.ts from src/ to shared/ (enables DB models to
  derive from generated types in the future)
- Export Database, Tables, Json types from oa-shared package
- Convert all DB model classes to interfaces (DBProfile, DBNews,
  DBComment, DBQuestion, DBResearchItem, DBResearchUpdate, DBProject,
  DBProjectStep, DBCategory, DBTag, DBProfileTag, DBProfileBadge,
  DBProfileType, DBMedia, DBNotification, DBMapPin, DBBanner)
- Remove fake Object.assign constructors from DB types
- Replace new DB*() instantiation patterns with plain object literals
- Update all imports to use oa-shared instead of src/database.types
- Update db:types script target path
@GMetaxakis
Copy link
Copy Markdown
Contributor Author

Updated the PR based on feedback. Here's what changed:

Eliminated all as unknown as casts from route files:

  • Created semantic helper functions (toJson, fromJson, fromJsonArray, dbResult, dbResultArray) in src/utils/supabase.types.ts
  • These centralize the type bridge between Supabase's generated types and our DB model types — the casts still exist internally but route files are clean

Converted all DB model classes to interfaces:

  • DBProfile, DBNews, DBComment, DBQuestion, DBResearchItem, DBResearchUpdate, DBProject, DBProjectStep, DBCategory, DBTag, DBProfileTag, DBProfileBadge, DBProfileType, DBMedia, DBNotification, DBMapPin, DBBanner — all converted from class → interface
  • Removed the fake Object.assign constructors (net -91 lines)
  • Replaced new DB*() instantiation patterns with plain object literals

Moved database.types.ts to shared/:

  • Generated types now live alongside DB models in the oa-shared package
  • Database, Tables, Json exported from oa-shared
  • This enables DB models to derive from generated Row types in the future (incremental follow-up)

Aligned DB date types with Supabase returns:

  • Changed Datestring for created_at/modified_at fields across all DB types (matching what Supabase actually returns as ISO strings)

The as unknown as casts can't be fully removed without rewriting DB types to derive from Tables<'tablename'> — the structural mismatch between Supabase's Json type for jsonb columns and our structured types (DBMedia, etc.) requires a type bridge somewhere. The helper functions make this explicit and centralized rather than scattered across every route file.

@mariojsnunes
Copy link
Copy Markdown
Contributor

great work! I need to look into this carefully

@benfurber benfurber moved this to In progress in Core Team - BAU list Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status
Status: In progress

Development

Successfully merging this pull request may close these issues.

[technical] Generate supabase typescript types

3 participants