Skip to content

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

Open
artile wants to merge 4 commits into
MapleTechLabs:mainfrom
artile:feat/make-repository
Open

artile wants to merge 4 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 42 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: 76f1446f-7ca6-4bc2-8ed3-d2d52d11a155
Commits

Reviewing files that changed from the base of the PR and between c5fa679 and b4f5c51.

Files selected for processing: 16
  • .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.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

Comment @coderabbitai help to get the list of available commands.

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

1 participant