{"id":28635312,"url":"https://github.com/galvez/kysely-tables","last_synced_at":"2025-06-25T09:39:46.957Z","repository":{"id":294148899,"uuid":"981652220","full_name":"galvez/kysely-tables","owner":"galvez","description":"Use the same Kysely types for your schema, migrations and queries.","archived":false,"fork":false,"pushed_at":"2025-05-20T07:46:20.000Z","size":262,"stargazers_count":46,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-18T18:54:12.216Z","etag":null,"topics":["database","database-management","kysely","migration-tool","migrations"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/galvez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-11T15:41:56.000Z","updated_at":"2025-06-16T21:37:05.000Z","dependencies_parsed_at":"2025-05-19T05:43:22.382Z","dependency_job_id":null,"html_url":"https://github.com/galvez/kysely-tables","commit_stats":null,"previous_names":["galvez/kysely-tables"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/galvez/kysely-tables","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/galvez%2Fkysely-tables","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/galvez%2Fkysely-tables/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/galvez%2Fkysely-tables/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/galvez%2Fkysely-tables/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/galvez","download_url":"https://codeload.github.com/galvez/kysely-tables/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/galvez%2Fkysely-tables/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261846607,"owners_count":23218806,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","database-management","kysely","migration-tool","migrations"],"created_at":"2025-06-12T17:01:35.484Z","updated_at":"2025-06-25T09:39:46.941Z","avatar_url":"https://github.com/galvez.png","language":"TypeScript","funding_links":[],"categories":["Addons"],"sub_categories":[],"readme":"# kysely-tables\n\n\u003cbr\u003e\u003cimg width=\"492\" src=\"https://github.com/user-attachments/assets/89f956f4-10a8-40e0-9a5c-73a9d9cb9bbf\" /\u003e\n\n\u003cbr\u003eUse the **same Kysely types** for your **SQL table schema**, **migrations** and **queries**.\n\n- No need for an intermediary [schema defining API](https://orm.drizzle.team/docs/schemas) or [schema language](https://www.prisma.io/docs/orm/prisma-schema/overview).\n- No need to [connect to the database to retrieve the schema structure](https://github.com/RobinBlomberg/kysely-codegen).\n- No need to [bloat](https://github.com/drizzle-team/drizzle-kysely) [your](https://github.com/eoin-obrien/prisma-extension-kysely) [setup](https://github.com/valtyr/prisma-kysely) mixing multiple database libraries.\n\n**Your Kysely types** become the **single source of truth** for your `CREATE TABLE` statements.\n\n**And also** for **creating and running migrations**, very much like Prisma and Drizzle.\n\n\u003e [!IMPORTANT]\n\u003e\n\u003e This is a **proof-of-concept** project.\n\u003e\n\u003e The idea is to use _annotation types_ to enhance regular Kysely types while keeping them working as before.\n\u003e\n\u003e The _annotations_ are then used by a processor to generate SQL schemas and migrations (when diffing).\n\u003e\n\u003e It's in a state where it's **just about good enough** to start being tested in real projects.\n\u003e\n\u003e Bug reports and feature requests are extremely welcome.\n\n[**Read the blog post.**](https://hire.jonasgalvez.com.br/2025/may/19/kysely-tables/)\n\n## Tutorial\n\n1. Check out this repository, `pnpm install` and `cd` to [`./examples/sqlite`](https://github.com/galvez/kysely-tables/tree/main/examples/sqlite).\n\n2. Inspect `db.ts` to see how tables are defined. Note that these types are fully Kysely-compatible. The schema types serve as hints for schema generation, but Kysely receives the underlying types it expects.\n\n   ```ts\n   export interface UsersTable {\n     id: Generated\u003cPrimary\u003cnumber\u003e\u003e\n     name: Sized\u003cstring, 100\u003e | null\n     email: Unique\u003cSized\u003cstring, 255\u003e\u003e\n     passwordHash: Text\u003cstring\u003e\n     role: Default\u003cstring, \"'member'\"\u003e\n     createdAt: Default\u003cDate, 'now()'\u003e\n     updatedAt: Default\u003cDate, 'now()'\u003e\n     deletedAt: null | Date \n   }\n   ```\n\n   In order for a table to recognized as such, the interface name needs to end with `Table`. Note also how we can use Kysely's [`Generated`](https://kysely-org.github.io/kysely-apidoc/types/Generated.html) type together with this library's schema types. Same is true for [`ColumnType`](https://kysely-org.github.io/kysely-apidoc/types/ColumnType.html).\n\n3. Still in `db.ts`, you'll notice how the Kysely database instance is created through a wrapper, `createDatabase()`, and also that `dialect` is a top-level export.\n\n   ```ts\n   const driver = new SQLite3Database('db.sqlite')\n   export const dialect = new SqliteDialect({ database: driver })\n\n   export default createDatabase\u003cDatabase\u003e({\n     driver,\n     config: {\n       dialect,\n     },\n   })\n   ```\n\n   This is to ensure the **runner** knows which dialect to use.\n\n4. Now it gets interesting: instead of packing a CLI, `kysely-tables` turns your schema file into one. This is what the `createDatabase()` wrapper is responsible for: parsing and understanding certain CLI flags when this file is executed directly. This is called the **runner**.\n\n5. Let's begin by creating the database and applying the initial table schema:\n\n   `% tsx db.ts --create`\n\n   \u003cimg width=\"612\" alt=\"SCR-20250518-uhmg\" src=\"https://github.com/user-attachments/assets/722062b9-dcb7-4b2f-a015-35cae2ef063a\" /\u003e\n\n   Proceed and you'll see that both `db.sql` and `db.snapshot.ts` are created.\n\n   \u003cimg width=\"612\" alt=\"SCR-20250518-uiux\" src=\"https://github.com/user-attachments/assets/9f875afd-bac8-470f-9ba1-764aebbe4254\" /\u003e\n\n   This _snapshot_ file is used for diffing purposes: when you change `db.ts`, the runner uses it to know how the schema changed. Now let's create a **migration**, referred to as **schema revision** in this library.\n\n6. Edit `db.ts` and remove any column from `UsersTable` (adding/removing tables also work):\n\n   ```diff\n     export interface UsersTable {\n       id: Generated\u003cPrimary\u003cnumber\u003e\u003e\n       name: Sized\u003cstring, 100\u003e | null\n       email: Unique\u003cSized\u003cstring, 255\u003e\u003e\n   -   passwordHash: Text\u003cstring\u003e\n       role: Default\u003cstring, \"'member'\"\u003e\n       createdAt: Default\u003cDate, 'now()'\u003e\n       updatedAt: Default\u003cDate, 'now()'\u003e\n       deletedAt: null | Date\n     }\n   ```\n   \n   Then run:\n\n   `% tsx db.ts --revision`\n\n   \u003cimg width=\"612\" alt=\"SCR-20250518-ukwr\" src=\"https://github.com/user-attachments/assets/f0ddeb7d-4511-4d76-95f7-052d434e8923\" /\u003e\n\n\u003e [!CAUTION]\n\u003e \n\u003e Automated revisions are the most fragile part of this library at this moment. There are no tests for this feature yet, and it may break badly depending on the schema changes you carry out. It should handle most basic cases fine, but if you see anything wrong, [**please file a bug report**](https://github.com/galvez/kysely-tables/issues/new). To goal is to polish it to perfection. In case you want to dive in and debug it yourself, check out the [`schemaDiff()`](https://github.com/galvez/kysely-tables/blob/dev/package/dialects/base.ts#L158) function, though that is one extremely delicate piece of code. See the \u003ca href=\"#internals\"\u003e\u003cb\u003eInternals\u003c/b\u003e\u003c/a\u003e section for more info.\n\nRunning with `--revision --empty` creates stub empty revisions, when you really need to write the SQL yourself.\n\nRunning with `--revision \u003crev\u003e` gives a custom name to the revision.\n\nRunning with `--apply` bypasses the prompt check.\n\nRunning only with `--apply` will sync up to the latest revision.\n\nRunning only with `--apply \u003crev\u003e` will sync up (or down) to the specified revision.\n\nEven though `kysely-tables` is responsible for diffing and generating the SQL statements, the migrations run through [Postgrator](https://github.com/rickbergfalk/postgrator) under the hood. Postgrator is a mature and extremely well tested migration runner with support for PostgreSQL, SQLite, MySQL and MSSQL. It's used by [Platformatic](https://github.com/platformatic/platformatic).\n\n## Reset\n\nFor convenience, a `--reset` flag is also available:\n\n\u003cimg width=\"612\" alt=\"SCR-20250519-bkcm\" src=\"https://github.com/user-attachments/assets/286b033b-a249-481b-8e7e-30f92fa037b5\" /\u003e\n\n## Syntax\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cb\u003eType Utility\u003c/b\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cb\u003eDescription\u003c/b\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`Sized\u003cT, Size extends number\u003e`\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nGenerates `VARCHAR` columns (when available).\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`Text\u003cT\u003e`\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nGenerates `TEXT` columns.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`Reference\u003cTable, Key, T\u003e`\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nGenerates `FOREIGN KEY` constraints.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`Default\u003cT, value\u003e`\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nGenerates `DEFAULT \u003cvalue\u003e` clauses.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`Primary\u003cT\u003e`\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nGenerates `PRIMARY KEY` clauses.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n`Unique\u003cT\u003e`\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nGenerates `UNIQUE` clauses and associated indexes.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n## Internals\n\nI wrote this because I was unhappy with the APIs and workflows available in [other](https://orm.drizzle.team/docs/migrations) [libraries](https://www.prisma.io/docs/orm/prisma-migrate/getting-started). Even Kysely itself [has its own API for migrations](https://kysely.dev/docs/migrations), which differs from the types used to define tables. I wanted my database management layer to be **extremely light**, but also architected in an transparent way, that would make me feel like I know what's going on behind the scenes.\n\nThe main class is `KyselyTables`, which provides the `buildSchema()`, `buildSchemaReset()` and `buildSchemaRevision()` methods. The main method that analyzes the table interfaces and their column fields is `#registerTableColumns()`. They all use [TypeScript's compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) to properly parse the source file, no regexes involved. The whole API is heavily inspired by Kysely, and of course, compatible with Kysely.\n\nThe main class uses a `DialectAdapter` to generate the correct SQL statements for the database used.\n\nAs for parsing each column definition, it's done by a helper function called `extractType()`, which will check for all special types and use them to populate flags in each `ColumnDefinition`.\n\nThe trickiest part of the library is the schema diff detection. \n\nThis first iteration uses [`json-diff`](https://github.com/andreyvit/json-diff), which is quite nice, but it still required some [massive data reconciliation glue code](https://github.com/galvez/kysely-tables/blob/dev/package/dialects/base.ts#L158). I aged six months in a week writing that function and do not recommend obsessing over it unless you have a very good alternative in mind and are willing to venture into the dark.\n\nThe [embedded runner](https://github.com/galvez/kysely-tables/blob/dev/package/runner.ts) that turns `db.ts` into a CLI is as simple as it can get. It uses [`minimist`](https://www.npmjs.com/package/minimist) for `process.argv` parsing and [`@clack/prompts`](https://www.npmjs.com/package/@clack/prompts) for the nice flows.\n\nThis should be enough for you to start digging in and contribute if you wish!\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgalvez%2Fkysely-tables","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgalvez%2Fkysely-tables","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgalvez%2Fkysely-tables/lists"}