*.db.* instead of hitting third-party APIs on every page load. Third-party data becomes an extension of your own DB, always fresh.
Get started
Run the migration once, then pass your connection tocreateCorsair({ database, ... }). See Quick Start for a full setup example.
- SQLite
- PostgreSQL
Install the driver, then run the migration:
- SQLite
- PostgreSQL
View migration SQL
View migration SQL
migration.sql
- macOS / Linux
- Windows (PowerShell)
Example: Slack
All data is scoped to tenants —withTenant() ensures you only read and write that tenant’s rows, with no cross-contamination.
*.api.* to write or force a fresh read. Use *.db.* for lists, search, and detail pages. Plugin-specific filters are in each plugin’s database reference (e.g. Slack).
Core tables
Four tables for every integration — Slack, GitHub, Linear, and the rest all share the same schema.| Table | Purpose |
|---|---|
corsair_integrations | One row per enabled plugin (slack, github, …) |
corsair_accounts | Per-tenant connection + encrypted credentials (tenant_id, integration_id) |
corsair_entities | Synced resources — messages, channels, issues, repos (entity_id, entity_type, data) |
corsair_events | Audit log of API calls and webhooks |
packages/corsair/db/index.ts.
Keeping data fresh
Corsair doesn’t snapshot data once and forget it. Every write path keeps the same row current:- API calls — when you call
tenant.slack.api.messages.post, Corsair sends the request and upserts the response intocorsair_entities. - Webhooks — when Slack notifies you that a message changed, Corsair finds the row by
entity_idand updates it in place. - Out-of-order events — if an update arrives before you’ve seen the create, Corsair fetches from the API and backfills the row (see Webhooks).
corsair_entities (or *.db.*) and trust that Corsair is maintaining it.
How to use it
Query through Corsair
Use the typed ORM on each plugin — no raw SQL required for most reads:Join to your own tables
Referencecorsair_entities.id from your schema and treat third-party data like first-class rows in your app:
corsair_entities on every API call and webhook, foreign keys to that table always point at current data — not a stale cache you wrote once.
When to use API vs DB
| Use | When |
|---|---|
*.api.* | Writes, deletes, or you need the latest value right now from the source |
*.db.* | UI lists, search, detail pages — fast reads, no rate limits |
SQLite, Postgres, and ORMs
Corsair runs on your database. Pass a connection tocreateCorsair — it works alongside whatever ORM you already use for your own tables.
- SQLite
- PostgreSQL
- Drizzle / Prisma
Best for local dev and small apps. Pass a
better-sqlite3 instance:pg Pool, better-sqlite3, postgres.js Sql, and Kysely. Use the same database for Corsair tables and your app tables — one migration, one connection string.
What’s next
Multi-Tenancy
Scope credentials and data per user with
withTenant().Webhooks
How incoming events update your rows automatically.
API
Call third-party APIs with full type safety.