Goal
Build no-orm as a tiny, database-independent persistence core for TypeScript libraries.
The immediate benchmark is the hebo-gateway storage layer: the same core should be able to describe its persistence model once and support multiple backends without choosing the database up front.
What This Must Cover
hebo-gateway does not need a full ORM. It needs:
- a canonical schema model
- TypeScript types derived from that schema for authoring code
- an adapter / dialect contract for backend-specific behavior
- a thin execution contract for reads, writes, and transactions
- minimal schema bootstrap from that schema
- a minimal query / operation language
- extensibility hooks for schema, writes, and queries
Concretely, the current storage layer needs support for:
Schema requirements
- string fields with logical length constraints
- number fields
- boolean fields
- timestamp fields
- JSON fields
- nullable fields
- single-field and composite primary keys
- ordered indexes
Type inference requirements
- TypeScript types derived from the canonical schema
- stable type-level mapping for field types and nullability
- enough typing support to write host-library code against schema-defined models
Query and write requirements
- insert/get/list/delete/upsert-style operations
- filtering
- ordering
- cursor pagination
- transactions
Backend adaptation requirements
- backend-specific type mapping
- backend-specific JSON extraction/select behavior
- backend-specific quoting, placeholders, upsert behavior, and limit handling
Extensibility requirements
- schema extension by consumers of the host library
- write-time injection or enforcement of field values
- query-time enforcement of required filters or scoped values
Core Capabilities
1. Canonical schema
One portable schema representation:
2. Logical types, not physical types
The schema should describe intent, not storage implementation.
Initial field types:
string
number
boolean
timestamp
json
Adapters decide how those map to Postgres, SQLite, MySQL, MongoDB, memory, or other targets.
3. TypeScript inference layer
The canonical schema should also be usable as a source of static TypeScript types for application and library code.
At minimum, the product should support:
- deriving model types from schema
- preserving field names
- mapping field types into useful TypeScript types
- mapping nullability into TypeScript nullability
This should remain separate from backend runtime mapping. The schema spec defines the canonical schema shape; type inference defines how developers write strongly typed code against that shape.
4. Adapter / dialect contract
Adapters must be able to consume the canonical schema and provide backend-specific behavior for:
- physical type mapping
- DDL emission
- quoting and placeholders
- JSON select/extract semantics
- upsert semantics
- limit/pagination quirks
5. Execution contract
The core also needs a minimal, generic execution boundary that adapters can target when running compiled persistence operations.
- execute read operations
- execute write operations
- support transactions where available
The exact method surface is still open. The important requirement is that the execution boundary stays small, generic, and usable across multiple backend adapters without introducing a full query builder or ORM runtime.
6. Minimal schema bootstrap
To match the current hebo-gateway storage layer, v1 should support minimal migration behavior:
- create model if not exists
- create index if needed
- backend-specific idempotent DDL where supported
This is not a full migration framework. It is just enough to bootstrap storage from the canonical schema.
7. Minimal query / operation layer
To match the hebo-gateway storage layer without becoming storage-specific, the core needs a minimal, generic, database-independent operation model for common persistence actions.
At minimum:
- create / insert
- read one
- read many
- delete
- upsert-style write
- portable filtering
- ordering
- cursor pagination
This layer should be generic enough to support multiple backends and host-library use cases, but intentionally too small to become a general-purpose query builder.
8. Extensibility hooks
Because no-orm is intended to be embedded inside other libraries, it must support controlled extension by consumers of the host library.
At minimum, the product needs hooks for:
- extending the canonical schema before it is finalized
- adding or enforcing field values during write operations
- adding or enforcing constraints during query operations
This is required for use cases such as:
- tenant scoping
- audit fields
- plugin-owned fields
- policy-enforced query filtering
Non-Goals for v1
Do not build these into the core yet:
- relations
- foreign keys
- defaults
- full migration framework
- codegen
- full query builder
- generated client
- reactive client cache/state
- dedicated schema validation layer
- broad arbitrary lifecycle hooks
- mixing backend runtime mapping rules into the canonical schema spec
Differentiation
Versus Drizzle
Drizzle is SQL-first and dialect-aware from the start. no-orm should be database-independent at the schema layer and lower-level: a portable schema + adapter core, not a SQL-first ORM toolkit.
Versus Prisma
Prisma is an integrated ORM workflow with schema, client generation, and migrations. no-orm should stay much smaller and embeddable: one portable schema model plus adapter/runtime contracts.
Versus Kysely
Kysely is a type-safe SQL query builder. no-orm should be schema-first, not query-builder-first, and must remain portable beyond SQL-shaped tooling.
Versus unadapter
unadapter standardizes CRUD across multiple backends and ORMs. no-orm should be narrower and lower-level: the canonical schema model and backend projection layer underneath that kind of abstraction.
Versus Better Auth database adapters
Better Auth provides a database adapter factory for implementing database persistence behind Better Auth, including methods such as create, update, delete, find, count, schema generation, and field/model name transformation. Its default adapters are built around tools such as Kysely and Prisma.
That is useful inspiration for adapter ergonomics, but the scope is different.
no-orm should differ by:
- being a reusable persistence core for third-party libraries, not an adapter layer owned by one host framework
- centering the canonical schema model first
- keeping adapter contracts generic across host-library use cases, rather than being shaped around Better Auth's adapter factory and model pipeline
Versus TanStack DB
TanStack DB is a reactive client-side data layer. no-orm should stay focused on backend persistence definition and execution, not client reactivity or caching.
Success Criteria
This ticket is complete when:
- the
hebo-gateway conversation storage schema can be represented cleanly in the canonical schema
- useful TypeScript types can be derived from that schema for host-library code
- at least two materially different backends can consume the same schema
- the adapter layer can cover the backend differences
hebo-gateway currently handles manually
- minimal schema bootstrap can create the required storage structures for at least one SQL backend
- the minimal query/operation layer can express the current
hebo-gateway storage operations without hand-written SQL in the storage implementation
- consumers of a host library can extend schema, write behavior, and query enforcement without backend-specific code
- the public API remains clearly smaller than Drizzle, Prisma, Kysely, unadapter, or TanStack DB
References
Goal
Build
no-ormas a tiny, database-independent persistence core for TypeScript libraries.The immediate benchmark is the
hebo-gatewaystorage layer: the same core should be able to describe its persistence model once and support multiple backends without choosing the database up front.What This Must Cover
hebo-gatewaydoes not need a full ORM. It needs:Concretely, the current storage layer needs support for:
Schema requirements
Type inference requirements
Query and write requirements
Backend adaptation requirements
Extensibility requirements
Core Capabilities
1. Canonical schema
One portable schema representation:
SchemaModelFieldIndex2. Logical types, not physical types
The schema should describe intent, not storage implementation.
Initial field types:
stringnumberbooleantimestampjsonAdapters decide how those map to Postgres, SQLite, MySQL, MongoDB, memory, or other targets.
3. TypeScript inference layer
The canonical schema should also be usable as a source of static TypeScript types for application and library code.
At minimum, the product should support:
This should remain separate from backend runtime mapping. The schema spec defines the canonical schema shape; type inference defines how developers write strongly typed code against that shape.
4. Adapter / dialect contract
Adapters must be able to consume the canonical schema and provide backend-specific behavior for:
5. Execution contract
The core also needs a minimal, generic execution boundary that adapters can target when running compiled persistence operations.
The exact method surface is still open. The important requirement is that the execution boundary stays small, generic, and usable across multiple backend adapters without introducing a full query builder or ORM runtime.
6. Minimal schema bootstrap
To match the current
hebo-gatewaystorage layer, v1 should support minimal migration behavior:This is not a full migration framework. It is just enough to bootstrap storage from the canonical schema.
7. Minimal query / operation layer
To match the
hebo-gatewaystorage layer without becoming storage-specific, the core needs a minimal, generic, database-independent operation model for common persistence actions.At minimum:
This layer should be generic enough to support multiple backends and host-library use cases, but intentionally too small to become a general-purpose query builder.
8. Extensibility hooks
Because
no-ormis intended to be embedded inside other libraries, it must support controlled extension by consumers of the host library.At minimum, the product needs hooks for:
This is required for use cases such as:
Non-Goals for v1
Do not build these into the core yet:
Differentiation
Versus Drizzle
Drizzle is SQL-first and dialect-aware from the start.
no-ormshould be database-independent at the schema layer and lower-level: a portable schema + adapter core, not a SQL-first ORM toolkit.Versus Prisma
Prisma is an integrated ORM workflow with schema, client generation, and migrations.
no-ormshould stay much smaller and embeddable: one portable schema model plus adapter/runtime contracts.Versus Kysely
Kysely is a type-safe SQL query builder.
no-ormshould be schema-first, not query-builder-first, and must remain portable beyond SQL-shaped tooling.Versus unadapter
unadapter standardizes CRUD across multiple backends and ORMs.
no-ormshould be narrower and lower-level: the canonical schema model and backend projection layer underneath that kind of abstraction.Versus Better Auth database adapters
Better Auth provides a database adapter factory for implementing database persistence behind Better Auth, including methods such as create, update, delete, find, count, schema generation, and field/model name transformation. Its default adapters are built around tools such as Kysely and Prisma.
That is useful inspiration for adapter ergonomics, but the scope is different.
no-ormshould differ by:Versus TanStack DB
TanStack DB is a reactive client-side data layer.
no-ormshould stay focused on backend persistence definition and execution, not client reactivity or caching.Success Criteria
This ticket is complete when:
hebo-gatewayconversation storage schema can be represented cleanly in the canonical schemahebo-gatewaycurrently handles manuallyhebo-gatewaystorage operations without hand-written SQL in the storage implementationReferences