Repository navigation
Roadmap signal: effect-orm as the ORM of the CAFE stack #52
Description
What this is
This issue groups a set of signal issues from the MyRemotely team. Each one says where we want effect-orm to go. They are not demands, and they are not PRs yet. We open them to show direction and ask for your view. We can send PRs for the items you accept. #48 and PRs #49–#51 (SQLite / D1) are our first steps.
Our context
MyRemotely is a multi-tenant SaaS for rental property managers: locks, tours and resident messages. We build on the CAFE stack: Cloudflare (Workers, D1, Durable Objects), Alchemy, Foldkit, Effect.
We plan to move our database from MariaDB to Cloudflare: D1 for shared and per-org data, and Durable Object SQLite for stateful entities. We want to do the move with effect-orm.
Today's schema, from schema.prisma, and our production code:
| Fact | Count |
|---|---|
| Tables / enums | 101 / 30 |
Relations / onDelete: Cascade |
130 / 106 |
| Indexes / composite uniques | 215 / 39 |
Json / DateTime columns |
67 / 274 |
cuid() ids / @updatedAt |
77 / 48 |
findUnique + findFirst call sites |
652 |
include: / _count |
61 / 104 |
upsert / createMany |
70 / 20 |
FOR UPDATE raw sites |
29 |
Unique-violation (P2002) handlers |
27 |
Why effect-orm and not Drizzle
Drizzle 1.0 has Effect drivers for MySQL, D1 and DO. But in Drizzle, Effect is a wrapper: a query is yieldable, all errors share one tag, and rows are not decoded by a Schema. effect-orm already has what Drizzle cannot add on top: row Schemas derived from each query, a tenant-scope proof, and transactions as a requirement in R. We want to make it the best Drizzle alternative in the CAFE stack.
The issues come from three sources:
- Cloudflare needs. What the MariaDB → D1 / DO move requires.
- Effect-native. Ideas from effect-qb and from Effect core (
SqlModel,SqlResolver,BeginTransaction). - Drizzle / Prisma parity. The features we use every day.
Issues
Cloudflare: the MariaDB → D1 / DO move
- MySQL / MariaDB dialect #53 MySQL / MariaDB dialect
- Durable Object SQLite dialect profile #54 Durable Object SQLite dialect profile
- D1
batch()as a typed atomic unit #55 D1batch()as a typed atomic unit - D1 read replication: Sessions API and bookmarks #56 D1 read replication: Sessions API and bookmarks
- Bound-parameter limits: chunk
INlists and multi-row inserts #57 Bound-parameter limits: chunkINlists and multi-row inserts - Database-per-tenant routing from the tenant scope #58 Database-per-tenant routing from the tenant scope
- Migrations that Alchemy can apply to D1 and Durable Objects #59 Migrations that Alchemy can apply to D1 and Durable Objects
- A
Databaselayer from an Alchemy binding #60 ADatabaselayer from an Alchemy binding - Move data between dialects: MariaDB → D1 copy and verify #61 Move data between dialects: MariaDB → D1 copy and verify
-
effect-orm pull --from-prisma: import a Prisma schema #62effect-orm pull --from-prisma: import a Prisma schema - One table definition for several dialects #63 One table definition for several dialects
Effect-native
- Install the dialect through Effect context, not module state #64 Install the dialect through Effect context, not module state
- A pipeable, data-last query API next to the fluent builder #65 A pipeable, data-last query API next to the fluent builder
- Dialect mismatch and missing sources as type errors #66 Dialect mismatch and missing sources as type errors
- Predicate facts: narrow row types from joins and WHERE #67 Predicate facts: narrow row types from joins and WHERE
- Table Schemas: select, insert, update and json variants #68 Table Schemas: select, insert, update and json variants
- Cardinality helpers:
runOne,runOption,runNonEmpty#69 Cardinality helpers:runOne,runOption,runNonEmpty -
Db.stream: stream rows with decoding #70Db.stream: stream rows with decoding - Typed constraint errors across dialects #71 Typed constraint errors across dialects
- Use
SqlClient.BeginTransactionfor transaction settings #72 UseSqlClient.BeginTransactionfor transaction settings -
afterCommit: post-commit work bound to the transaction #73afterCommit: post-commit work bound to the transaction - Request batching with
SqlResolver(N+1 safety) #74 Request batching withSqlResolver(N+1 safety) -
makeRepository(table): typed, tenant-scoped CRUD #75makeRepository(table): typed, tenant-scoped CRUD - Test layers: in-memory and SQLite-backed
Database#76 Test layers: in-memory and SQLite-backedDatabase - Observability:
observehook, span attributes, parameter redaction #77 Observability:observehook, span attributes, parameter redaction - Decode errors that are safe to log #78 Decode errors that are safe to log
Drizzle / Prisma parity
- Relational queries: nested
withloading, decoded by Schema #79 Relational queries: nestedwithloading, decoded by Schema - Relation counts and filters:
_count,some,every,none#80 Relation counts and filters:_count,some,every,none - Portable upsert on SQLite and MySQL, plus an
excludedhelper #81 Portable upsert on SQLite and MySQL, plus anexcludedhelper - Client-side defaults as Effects:
defaultFnandonUpdate#82 Client-side defaults as Effects:defaultFnandonUpdate - Portable text search: case-insensitive
containsand LIKE escaping #83 Portable text search: case-insensitivecontainsand LIKE escaping - Typed JSON paths and JSON updates on SQLite and MySQL #84 Typed JSON paths and JSON updates on SQLite and MySQL
- Row locks on SQLite / D1: refuse in the type, document the replacement #85 Row locks on SQLite / D1: refuse in the type, document the replacement
- Keyset (cursor) pagination helper #86 Keyset (cursor) pagination helper
- Name mapping: TS keys and DB names (
@map/@@map, casing) #87 Name mapping: TS keys and DB names (@map/@@map, casing) - Seeding from Schema arbitraries #88 Seeding from Schema arbitraries
- Drift check: table definitions against a live database #89 Drift check: table definitions against a live database
- Temporal and decimal codecs for SQLite and MySQL #90 Temporal and decimal codecs for SQLite and MySQL
- Docs: "Coming from Drizzle / Prisma" and a CAFE example #91 Docs: "Coming from Drizzle / Prisma" and a CAFE example
Activity