Skip to content

feat(database): makeRepository, tenant-pinned CRUD derived from a table - #104

Merged
Makisuo merged 7 commits into
MapleTechLabs:mainfrom
artile:feat/make-repository
Nov 2, 2026
Merged

Makisuo merged 7 commits into
MapleTechLabs:mainfrom
artile:feat/make-repository

Conversation

@artile

@artile artile commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

A team that uses effect-orm writes the same insert, update, findById and delete for each table, and must remember the tenant filter every time. Effect core has SqlModel.makeRepository for a Model.Class, but nothing builds it from an effect-orm table.

Closes #75.
Part of #52.

Depends on #99; review only the commits after it. This branch builds on tableSchemas(table) from #99. After #99 merges, I will rebase on main.

Changes

  • New makeRepository(table, { idColumn, tenantColumn }) in /database. It returns an Effect that needs Database, like SqlModel.makeRepository needs SqlClient.
  • Methods: insert, insertVoid, update, updateVoid, findById, delete. findById and update return an Option (Option.fromNullishOr on the first row). insert returns the stored row with its defaults.
  • Input types come from tableSchemas(table): insert takes the insert row, a patch takes the update row. A patch cannot set the id.
  • A table with a tenant column gets a TenantRepository. Every method takes the tenant first. Inserts write the tenant column from that argument, and the row type has no tenant column. Reads and writes filter on the tenant column and the id. A patch cannot set the tenant column. A tenant column put into a row or patch at runtime (through a cast) is removed. Each statement compiles as "single-tenant", so a Database with requireTenantScope runs it.
  • A call without the tenant does not type-check. The Table type does not carry its tenant column (it is widened to string on purpose), so makeRepository checks tenantColumn against table.tenantColumn when it is built. A mismatch in either direction is a QueryBuilderDefect.
  • ClickHouse: insertVoid, updateVoid, findById and delete work. updateVoid is a mutation with mutations_sync = 2, and delete a lightweight delete with lightweight_deletes_sync = 2, so a findById after them reads the write. insert and update need RETURNING, which ClickHouse does not have, so they die with the existing QueryBuilderDefect and write nothing. The choice uses the dialect's clauses (writeSettings, alterTableUpdate), not its name.
  • Tests: PGlite behaviour tests (src/database/repository.test.ts, run with requireTenantScope: true), type tests (src/database/repository.test-d.ts), and a live ClickHouse suite (tests/repository.clickhouse.test.ts).
  • Docs: a "Repositories" section in docs/database.md, a row in docs/reference.md, the docs index, and a minor changeset.

Left out

Validation

Run on b4f5c51 against a live ClickHouse 26.8.2.7 (the CI matrix version), with EFFECT_ORM_CLICKHOUSE_URL set, so no suite is skipped:

  • bun run build: pass.
  • bun run typecheck: pass.
  • vitest run --testTimeout=60000 --hookTimeout=60000: pass. Test files: 63 passed (63). Tests: 1051 passed (1051).
  • Doc citations, export catalog and doc examples: pass.
  • bun scripts/check-package.ts (tarball, types, CommonJS consumer): pass.
  • The longer timeouts are only for the local machine, which was under load.
  • Upstream CI for fork PRs waits for a maintainer to approve the workflow run.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 43 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d5ff666b-7a38-4e27-9025-6c257295903f
Commits

Reviewing files that changed from the base of the PR and between b57faf1 and 74a123b.

Files selected for processing: 17
  • .changeset/make-repository.md
  • .changeset/table-row-schemas.md
  • docs/README.md
  • docs/database.md
  • docs/reference.md
  • docs/tables-and-types.md
  • src/ch/table-schemas.test-d.ts
  • src/ch/table-schemas.test.ts
  • src/ch/table-schemas.ts
  • src/core.ts
  • src/database.ts
  • src/database/repository.sqlite.test.ts
  • src/database/repository.test-d.ts
  • src/database/repository.test.ts
  • src/database/repository.ts
  • src/docs-examples.test.ts
  • tests/repository.clickhouse.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Makisuo and others added 3 commits November 2, 2026 16:32
Resolve the reference.md conflict (tableSchemas row next to main's SQLite
insertInto row) and cover SQLite tables: wire-form and JSON variants, a
node:sqlite round trip of a jsonCreate body, and the insert row type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dedup across tenants

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Makisuo

Makisuo commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Merge-readiness pass (74a123b):

Verified locally: bun run typecheck passes. bun run test against live ClickHouse 26.8 passes (77 files, 1451 tests), including tests/repository.clickhouse.test.ts. FUZZ_SCALE=0.3 vitest run tests/fuzz passes (9 files, 269 tests). CI run 41473712111.

@Makisuo
Makisuo merged commit 2da4377 into MapleTechLabs:main Nov 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

makeRepository(table): typed, tenant-scoped CRUD

2 participants