{"id":16586046,"url":"https://github.com/windwp/kysely-zod-sqlite","last_synced_at":"2025-07-23T23:05:01.938Z","repository":{"id":196221365,"uuid":"671281909","full_name":"windwp/kysely-zod-sqlite","owner":"windwp","description":"An flexible api for Cloudflare D1 and sqlite. It has an simple api of Prisma and a powerful query with Kysely.","archived":false,"fork":false,"pushed_at":"2025-01-25T02:18:01.000Z","size":718,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-07T11:58:34.417Z","etag":null,"topics":["cloudflare","d1","kysely","serverless","sqlite"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/windwp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","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},"funding":{"custom":["https://paypal.me/trieule1vn","https://ko-fi.com/windwp"]}},"created_at":"2023-07-27T01:11:27.000Z","updated_at":"2024-07-25T10:11:35.000Z","dependencies_parsed_at":"2023-12-21T11:34:17.742Z","dependency_job_id":"7cd8b4f4-0340-402c-b1f5-f0b47acf6567","html_url":"https://github.com/windwp/kysely-zod-sqlite","commit_stats":null,"previous_names":["windwp/kysely-zod-sqlite"],"tags_count":116,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwp%2Fkysely-zod-sqlite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwp%2Fkysely-zod-sqlite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwp%2Fkysely-zod-sqlite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwp%2Fkysely-zod-sqlite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/windwp","download_url":"https://codeload.github.com/windwp/kysely-zod-sqlite/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238795341,"owners_count":19531719,"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":["cloudflare","d1","kysely","serverless","sqlite"],"created_at":"2024-10-11T22:50:26.023Z","updated_at":"2025-07-23T23:05:01.930Z","avatar_url":"https://github.com/windwp.png","language":"TypeScript","funding_links":["https://paypal.me/trieule1vn","https://ko-fi.com/windwp"],"categories":[],"sub_categories":[],"readme":"# Intro \nAn flexible api for Cloudflare D1 and sqlite.\n\nIt has an simple api of Prisma and a powerful query with Kysely, runtime transform and validation model with zod.\n\n# Feature\n- [x] validation and parse model by zod (json text string on sqlite)\n- [x] remote call from your local app to worker or between worker by binding service\n- [x] api like primsa (support 1 level relation)\n- [x] unit testing D1 on local.\n\n# Install\n`npm install kysely-zod-sqlite`\n\n# Usage\n### Define zod schema\nDefine zod and use it for kysely model.\n\n``` typescript \nimport {z} from zod\nimport {\n  zJsonObject,\n  zJsonSchema,\n  zRelationOne,\n  zBoolean,\n  zDate,\n} from 'kysely-zod-sqlite';\nexport const userSchema = z.object({\n  id: z.string(),\n  name: z.string(),\n  email: z.string().optional(),\n  data: zJsonObject\u003cUserData\u003e(),  // it use JSON.parse\n  config: zJsonSchema(z.object({  // it use zod.parse\n    language:z.string(),\n    status: z.enum(['busy', 'working' ]),\n  })), \n  created_at: zDate(), //custom parse sqlite date\n  updated_at: zDate(),\n  isDelete: zBoolean(), // parse boolean 1,0 or you can use z.coerce.boolean()\n});\nexport const postSchema = z.object({\n  id: z.string(),\n  name: z.string(),\n  user_id: z.string(),\n  is_published: zBoolean,\n  data: z.string(),\n  created_at: zDate,\n  updated_at: zDate,\n});\n// define a relation\nexport const postRelationSchema = postSchema.extend({\n  user: zRelationOne({\n    schema: userSchema,\n    ref: 'user_id',\n    refTarget: 'id',\n    table: 'test_users',\n  }),\n});\nexport const userRelationSchema = userSchema.extend({\n  posts: zRelationMany({\n    schema: postSchema,\n    refTarget: 'user_id',\n    ref: 'id',\n    table: 'test_posts',\n  }),\n});\nexport type PostTable = z.infer\u003ctypeof postRelationSchema\u003e;\nexport type UserTable = z.infer\u003ctypeof userRelationSchema\u003e;\n// define an api Database\nexport const dbSchema = z.object({\n  test_users: userRelationSchema,\n  test_posts: postRelationSchema,\n});\nexport type DbSchema = typeof dbSchema;\n```\nuse schema to define api\n```typescript\nexport class TestApi extends SqliteApi\u003cDbSchema\u003e {\n  \n  get test_users() {\n    return this.table('test_users');\n  } // api like prisma\n  get test_posts() {\n    return this.table('test_posts');\n  }\n}\nconst config = {}; \nconst api = new TestApi({\n  schema: dbSchema,\n  config: {},\n  kysely: createKyselySqlite({\n    driver: new BetterDriver(new Database(':memory:'), config),\n    schema: dbSchema,\n  }),\n})\n```\n### Usage\nprisma similar api\n```typescript\nconst post = await api.test_posts.selectFirst({\n  where: { name: 'test' },\n  include: {\n    user: true, // query 1 level relation\n  },\n})\n// access relation and json data 🔥\nconst language = post.user.config.language\nawait api.test_users.updateOne({\n  where: {\n    name: {\n      like: 'user%', // it use kysely operation  = ('name' , 'like', 'user%') \n    }, \n  },\n  data: { name: 'test' },\n});\n```\nIf you want to write a complex query you can use kysely\n```typescript\nconst data = await api.ky // this is a reference of kysely builder\n    .selectFrom('test_posts')\n    .limit(1)\n    .innerJoin('test_users', 'test_posts.user_id', 'test_users.id')\n    .selectAll()\n    .execute();\n```\n## Driver\n### Local enviroment and unit test\n```typescript\nimport { BetterSqlite3Driver } from 'kysely-zod-sqlite/driver/sqlite-driver';\nconst api = new TestApi({\n  config,\n  schema: dbSchema,\n  kysely: createKyselySqlite({\n    driver: new BetterDriver(new Database(':memory:'), config),\n    schema: dbSchema,\n  }),\n});\n```\n### Working inside worker and pages\n```typescript\nimport { D1Driver } from 'kysely-zod-sqlite/driver/d1-driver';\nconst api = new TestApi({\n  config,\n  schema: dbSchema,\n  kysely: createKyselySqlite({\n    driver: new FetchDriver({\n      apiKey: process.env.API_KEY!,\n      apiUrl: process.env.API_URL!,\n    }),\n    schema: dbSchema,\n  }),\n});\n```\n### Working outside cloudflare worker, pages\nYou need to deploy a custom worker then you can connect to it on your app\n\n[worker](./example/worker/src/worker.ts)\n```typescript\nimport { FetchDriver } from 'kysely-zod-sqlite/driver/fetch-driver';\nconst api = new TestApi({\n  config,\n  schema: dbSchema, \n  kysely: createKyselySqlite({\n    driver: new FetchDriver({\n      apiKey: process.env.API_KEY!,\n      apiUrl: process.env.API_URL!,\n    }),\n    schema: dbSchema,\n  }),\n});\n```\n### Call from cloudflare pages to worker or from worker to worker\n```typescript\nimport { FetchDriver } from 'kysely-zod-sqlite/driver/fetch-driver';\nconst api = new TestApi({\n  config,\n  schema: dbSchema,\n  kysely: createKyselySqlite({\n    driver: new FetchDriver(env.D1_DB, {\n      apiKey: 'test',\n      apiUrl: 'https://{worker}.pages.dev',\n      database: 'Test',\n      bindingService: env.WORKER_BINDING,\n      // it will use env.WORKER_BINDING.fetch not a global fetch\n    }),\n    schema: dbSchema,\n  }),\n});\n```\n### Multiple driver per table\n```typescript\nexport class TestApi extends SqliteApi\u003cDatabase\u003e {\n  //... another table use a default driver\n\n  get TestLog(){\n    return this.table('TestLog',{ driver: new FetchDriver(...)});\n  }\n}\n// dynamic add schema and driver \nconst api = new TestApi(...)\n\nconst extendApi = api.withTables(\n  {\n    TestExtend: z.object({\n      id: z.number().optional(),\n      name: z.string(),\n    }),\n  },\n  { testExtend: o =\u003e o.table('TestExtend',{driver: new D1Driver(...)}),}\n);\n\nconst check = await extendApi.testExtend.selectFirst({\n  where: { name: 'testextend' },\n});\n\n```\n\n### Support batch\n```typescript\n// raw sql query \nawait api.batchOneSmt(\n  sql`update test_users set name = ? where id = ?`, \n  [ ['aaa', 'id1'], ['bbb', 'id2'], ]\n);\n// run kysely query with multiple value\nconst check = await api.batchOneSmt(\n    api.ky\n      .updateTable('test_users')\n      .set({\n        data: sql` json_set(data, '$.value', ?)`,\n      })\n      .where('name', '=', '?'),\n    [ ['aaa', 'user0'], ['bbb', 'user1'], ]\n);\n// run multiple query on batch\nconst result = await api.batchAllSmt([\n  api.ky.selectFrom('test_users').selectAll(), // kysely query\n  api.ky.insertInto('test_posts').values({\n    id: uid(),\n    name: 'post',\n    data: '',\n    is_published: true,\n    user_id: userArr[0].id,\n  }),\n  api.test_users.$selectMany({  // prisma syntax (add $ before select)\n      take: 10,\n      include: {\n        posts: true,\n      },\n      select: {\n        id: true,\n      },\n  })\n]);\nconst users = result.getMany\u003cUserTable\u003e(0);\nconst post = result.getOne\u003cPostTable\u003e(1);\n```\n### Bulk method\nworking with array on batch method is difficult.\nwhen you run query depend on some condition so I create bulk.\nrecommend use bulk for FetchDriver if you have multiple request\n```typescript\nconst check = await api.bulk({\n  // skip that query for normal user\n  allUser: isAdmin ? api.ky.selectFrom('test_users').selectAll(): undefined; \n  insert: api.ky.insertInto('test_posts').values({\n    id: uid(),\n    name: 'post',\n    data: '',\n    is_published: true,\n    user_id: userArr[0].id,\n  }),\n});\n// It use **key - value** to.\nconst allUser = check.getMany\u003cUserTable\u003e('allUser'); \nconst allUser = check.getOne\u003cany\u003e('insert'); \n\n//prisma query can use on bulk too. You can even run batch inside of bulk 🥰\nconst check = await api.bulk({\n  user:\n    api.ky\n      .updateTable('test_users')\n      .set({\n        data: sql` json_set(data, '$.value', ?)`,\n      })\n      .where('name', '=', '?'),\n  ,\n  topUser: api.test_users.$selectMany({\n    take: 10,\n    include: {\n      posts: true,\n    },\n    select: {\n      id: true,\n    },\n  }),\n});\n```\n# FAQ\n\n### Is this library an ORM?\nNo, it is merely a wrapper around Kysely. You can think of it as an API that uses Zod for validation and schema parsing, combined with Kysely for querying.\n\n### Different between using this library vs kysely\n``` typescript\napi.table('aaa').insertOne({...}) // it is validation on runtime value with zod.\napi.ky.insertInto('aaa').values({...}) // it is type checking.\n```\n\n### What is $ on table\n```typescript\napi.table('aaa').selectMany() // use it to get data\napi.table('aaa').$selectMany() \n// it is kysely query you can modify that query or use it on batch\n```\n\n### Column is null\nWhen your database column can be null, you need to use `nullable` instead of `optional` in your model.\n```typescript\naccess_token: z.string().nullable(),\n```\n\n### Parse custom schema on query with join\n```typescript\napi.parseMany\u003cUserTable \u0026 { dynamic: number }\u003e(\n  data,\n 'test_users', \n  // a joinSchema\n  z.object({  \n    dynamic: z.number(),\n  })\n```\n### migration \nuse the migration from kysely\n\n# Thank\n[kysely](https://github.com/kysely-org/kysely)\n[zod](https://github.com/colinhacks/zod)\n[@subframe7536](https://github.com/subframe7536/kysely-sqlite-tools/tree/master/packages/plugin-serialize)\n[@ryansonshine](https://github.com/ryansonshine/typescript-npm-package-template)\n# Links\n[cloudflare](https://developers.cloudflare.com/d1/platform/client-api/)\n[better-sqlite3](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwindwp%2Fkysely-zod-sqlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwindwp%2Fkysely-zod-sqlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwindwp%2Fkysely-zod-sqlite/lists"}