{"id":37492754,"url":"https://github.com/jonasb/durable-objects-sql-tag","last_synced_at":"2026-01-20T14:01:04.063Z","repository":{"id":332416212,"uuid":"1133704558","full_name":"jonasb/durable-objects-sql-tag","owner":"jonasb","description":"A library for working with SQLite in Cloudflare Durable Objects.","archived":false,"fork":false,"pushed_at":"2026-01-13T18:46:35.000Z","size":287,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-19T17:58:05.002Z","etag":null,"topics":["cloudflare","cloudflare-workers","durable-objects","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/jonasb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-13T17:54:18.000Z","updated_at":"2026-01-13T19:10:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jonasb/durable-objects-sql-tag","commit_stats":null,"previous_names":["jonasb/durable-objects-sql-tag"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/jonasb/durable-objects-sql-tag","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasb%2Fdurable-objects-sql-tag","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasb%2Fdurable-objects-sql-tag/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasb%2Fdurable-objects-sql-tag/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasb%2Fdurable-objects-sql-tag/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonasb","download_url":"https://codeload.github.com/jonasb/durable-objects-sql-tag/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasb%2Fdurable-objects-sql-tag/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28604712,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T12:01:53.233Z","status":"ssl_error","status_checked_at":"2026-01-20T12:01:46.545Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","cloudflare-workers","durable-objects","sqlite"],"created_at":"2026-01-16T07:40:08.051Z","updated_at":"2026-01-20T14:01:03.981Z","avatar_url":"https://github.com/jonasb.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `durable-objects-sql-tag`\n\n[![npm version](https://badge.fury.io/js/durable-objects-sql-tag.svg)](https://badge.fury.io/js/durable-objects-sql-tag)\n\nA library for working with SQLite in [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/).\n\n`npm install durable-objects-sql-tag`\n\n- [Changelog](./CHANGELOG.md)\n\n## Features\n\n- Type-safe SQL template literals with parameterized queries\n- Database wrapper with convenient query helpers (`queryOne`, `queryMany`, etc.)\n- Built-in migration system with schema versioning\n- SQL injection prevention through proper parameterization\n- Support for all SQLite types including binary data (ArrayBuffer, Uint8Array)\n\n## Usage\n\n### Basic SQL Tag\n\nThe `sql` template literal builds parameterized SQL queries:\n\n```ts\nimport { sql } from \"durable-objects-sql-tag\";\n\n// Simple query with parameters\nconst query = sql`SELECT * FROM users WHERE id = ${userId}`;\nconst { query: sqlString, values } = query.build();\n// sqlString: \"SELECT * FROM users WHERE id = ?\"\n// values: [userId]\n\n// Using sql.join() for IN clauses\nconst ids = [1, 2, 3];\nconst listQuery = sql`SELECT * FROM users WHERE id IN (${sql.join(ids)})`;\n// Builds: \"SELECT * FROM users WHERE id IN (?, ?, ?)\"\n\n// Composing fragments\nconst whereClause = sql`WHERE status = ${\"active\"}`;\nconst fullQuery = sql`SELECT * FROM users ${whereClause}`;\n```\n\n### Database Wrapper\n\nThe `wrapDatabase` function provides a convenient API for executing queries:\n\n```ts\nimport { DurableObject } from \"cloudflare:workers\";\nimport { sql, wrapDatabase, type MigrationVersionDefinition } from \"durable-objects-sql-tag\";\n\nconst migrations: MigrationVersionDefinition[] = [\n  {\n    name: \"Create users table\",\n    migrate(db) {\n      db.run(sql`\n        CREATE TABLE users (\n          id TEXT PRIMARY KEY,\n          name TEXT NOT NULL,\n          email TEXT NOT NULL UNIQUE\n        ) STRICT\n      `);\n    },\n  },\n];\n\nexport class MyDurableObject extends DurableObject {\n  private db;\n\n  constructor(ctx: DurableObjectState, env: Env) {\n    super(ctx, env);\n    this.db = wrapDatabase(ctx.storage, { migrations });\n  }\n\n  async fetch(request: Request): Promise\u003cResponse\u003e {\n    // Query a single row (throws if not exactly one row)\n    const user = this.db.queryOne\u003c{ id: string; name: string }\u003e(\n      sql`SELECT * FROM users WHERE id = ${userId}`\n    );\n\n    // Query zero or one row\n    const maybeUser = this.db.queryNoneOrOne\u003c{ id: string; name: string }\u003e(\n      sql`SELECT * FROM users WHERE id = ${userId}`\n    );\n\n    // Query multiple rows\n    const users = this.db.queryMany\u003c{ id: string; name: string }\u003e(\n      sql`SELECT * FROM users WHERE status = ${\"active\"}`\n    );\n\n    // Execute a write operation\n    const { rowsRead, rowsWritten } = this.db.run(\n      sql`INSERT INTO users (id, name, email) VALUES (${id}, ${name}, ${email})`\n    );\n\n    // Execute without expecting rows (throws if rows returned)\n    this.db.queryNone(sql`DELETE FROM users WHERE id = ${userId}`);\n\n    return new Response(\"OK\");\n  }\n}\n```\n\n### Query Methods\n\n| Method | Description |\n|--------|-------------|\n| `queryOne\u003cT\u003e(statement)` | Returns exactly one row. Throws if 0 or 2+ rows. |\n| `queryNoneOrOne\u003cT\u003e(statement)` | Returns one row or `null`. Throws if 2+ rows. |\n| `queryMany\u003cT\u003e(statement)` | Returns all matching rows as an array. |\n| `queryNone(statement)` | Executes statement. Throws if any rows returned. |\n| `run(statement)` | Executes statement. Returns `{ rowsRead, rowsWritten }`. |\n| `pragma(name)` | Executes PRAGMA, returns single value. |\n| `pragmaFull\u003cT\u003e(name)` | Executes PRAGMA, returns full result set. |\n| `transactionSync(fn)` | Runs function in a synchronous transaction. |\n\n### Migrations\n\nMigrations run automatically when `wrapDatabase` is called. The system tracks applied migrations in a `metadata` table:\n\n```ts\nconst migrations: MigrationVersionDefinition[] = [\n  {\n    name: \"Initial schema\",\n    migrate(db) {\n      db.run(sql`CREATE TABLE users (...) STRICT`);\n    },\n  },\n  {\n    name: \"Add posts table\",\n    beforeMigrate(db) {\n      // Optional: run before migration\n    },\n    migrate(db) {\n      db.run(sql`CREATE TABLE posts (...) STRICT`);\n    },\n  },\n];\n\n// Check migration status without applying\nimport { getMigrationStatus } from \"durable-objects-sql-tag\";\nconst { currentVersion, targetVersion, migrationsToApply } = getMigrationStatus(\n  ctx.storage,\n  migrations\n);\n```\n\n### Query Callbacks\n\nAdd callbacks for logging or instrumentation:\n\n```ts\nconst db = wrapDatabase(ctx.storage, {\n  migrations,\n  beforeQuery: (query) =\u003e console.log(\"Executing:\", query),\n  afterQuery: (query, result) =\u003e console.log(\"Result:\", result),\n  beforeMigration: (migrations) =\u003e console.log(\"Applying:\", migrations),\n});\n\n// Or add callbacks to an existing wrapper\nconst dbWithLogging = db.withCallbacks({\n  beforeQuery: (query) =\u003e console.log(query),\n});\n```\n\n### Supported Types\n\n**Input types (Primitive):**\n- `string`\n- `number`\n- `boolean` (stored as `\"true\"`/`\"false\"` strings)\n- `null`\n- `undefined` (stored as `null`)\n- `ArrayBuffer`\n- `Uint8Array`\n\n**Output types (SqlStorageValue):**\n- `string`\n- `number`\n- `null`\n- `ArrayBuffer`\n\n## License\n\n[MIT](./LICENSE.txt)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonasb%2Fdurable-objects-sql-tag","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonasb%2Fdurable-objects-sql-tag","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonasb%2Fdurable-objects-sql-tag/lists"}