Core · Library

End-to-end Type Safety

Define your schema once. The types follow your data everywhere it goes.

The types your editor shows you should match the shape of the data your app actually stores. Not the shape you typed into an interface six months ago. Not the shape a teammate documented in a wiki. The real shape, as the database sees it, right now.

With sp00ky there is one place where your schema lives, and everything else is generated from it. Your queries, your hooks, your components all agree on what a row looks like, because they are all reading from the same source of truth.

The old way, and why it gets tiring

Every app starts the same way. You write the schema, you type the row, and the two match for a few days. Then a field gets renamed and nobody updates the interface. Then a column gets added and the code keeps compiling anyway. Three months later, half the types in your codebase describe a reality that stopped existing a while ago, and nobody remembers which.

Hand-written types drift. Generated types don't.

How sp00ky does it

You write your schema. sp00ky does the rest.

  1. You define your tables once, versioned alongside your code.
  2. One command regenerates the types for every language the schema touches.
  3. Your query builder already knows every table, every field, every relationship.
  4. Your editor autocompletes the right names and flags the wrong ones as errors.
  5. Rename a field, and every place that used it lights up before you commit.

Write the schema once. The compiler remembers every detail, so you don't have to.

Catches bugs in your editor, not in production

When you filter a query, the only field names it will accept are the ones that actually exist on the table. When you ask for a related record, the related object shows up in your types with its own fields ready to autocomplete. If you rename a column, every component that read it stops compiling until you update it.

You do not find out about a broken call at 3am from a bug report. You find out instantly, in the file you just edited.

Types travel as far as your data does

Queries are typed. Reactive updates carry the exact shape your query asked for. Joining related rows keeps its shape through the whole pipeline, from database to hook to component. Backend calls are checked against your schema before the code ever runs. The type system follows the data into every corner of your app.

How sp00ky compares

Plenty of tools do one or two parts of this well. sp00ky's goal is that every step, from schema to rendered component, agrees on the same types without you having to wire it up.

Tool Source of truth Typed query results Typed reactive updates Typed related data
Drizzle TypeScript schema Yes Not built in Yes
Prisma Prisma schema file Yes Separate product (Pulse) Yes
Supabase Postgres introspection Yes You type the payloads Loosely typed
tRPC Procedure definitions Yes Yes Not a database layer
TanStack DB Collection schemas Yes Yes No first-class relationships
sp00ky Your schema file Yes, generated and inferred Yes, end to end Yes, out of the box

Legend: green check means it is there and typed, amber bar means partial or sold separately, red cross means it is not part of the tool.

What you don't have to write anymore

  • Hand-written row interfaces that drift away from the schema.
  • Generic gymnastics to carry a query's shape into a component.
  • Manual casts when you fetch a related record.
  • Documentation comments that promise a shape the code no longer matches.
  • That one bug that only shows up when a field gets renamed in production.

Where we are today, honestly

Reads, updates, and backend calls are fully typed today. Create payloads still accept a slightly looser shape than the rest of the API, and tightening that is on the roadmap. We will update this page as it lands.

Ready to build something amazing?

Full docs, install guide, and API reference — all in one place.

Read the docs