Skip to content

Commit 6078c53

Browse files
committed
feat(drivers): ✨ add Deno and @db/sqlite support
Add Deno-compatible npm imports and a synchronous JSR SQLite driver with statement cleanup, typed rows, blobs, bigint values, and insert IDs. Use the existing WASM workflow and add focused documentation, examples, and tests. The Deno example runs and regenerates without Node.js. Validated 31 generator tests, four SQLite integration tests, Deno checks for all four npm drivers, and WASM output with sqlc 1.24.0 and 1.31.1.
1 parent 395a0ba commit 6078c53

26 files changed

Lines changed: 1700 additions & 8 deletions

‎.github/workflows/ci.yml‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@ jobs:
1818
- run: chmod +x javy-x86_64-linux-v1.2.0
1919
- run: npm install
2020
- run: npx tsc --noEmit
21+
- run: npm test
2122
- run: npx esbuild --bundle src/app.ts --tree-shaking=true --format=esm --target=es2020 --outfile=out.js
2223
- run: ./javy-x86_64-linux-v1.2.0 compile out.js -o examples/plugin.wasm
2324
- run: sqlc -f sqlc.dev.yaml diff
24-
working-directory: examples
25+
working-directory: examples
26+
- uses: denoland/setup-deno@v2
27+
with:
28+
deno-version: v2.x
29+
- run: node tests/check-deno.mjs
30+
- run: make test-deno
31+
- run: git diff --exit-code -- examples/deno-db-sqlite

‎.gitignore‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@ node_modules
22
out.js
33
*.wasm
44
javy
5-
tests

‎Makefile‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: generate
1+
.PHONY: generate test test-deno
22

33
generate: examples/plugin.wasm examples/sqlc.dev.yaml
44
cd examples && sqlc-dev -f sqlc.dev.yaml generate
@@ -13,3 +13,11 @@ out.js: src/app.ts $(wildcard src/drivers/*.ts) src/gen/plugin/codegen_pb.ts
1313

1414
src/gen/plugin/codegen_pb.ts: buf.gen.yaml
1515
buf generate --template buf.gen.yaml buf.build/sqlc/sqlc --path plugin/
16+
17+
test:
18+
npx tsc --noEmit
19+
npm test
20+
21+
test-deno:
22+
test -f examples/plugin.wasm
23+
cd examples/deno-db-sqlite && deno task generate && deno task check && deno task test && deno task start

‎README.md‎

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,18 @@ sql:
2828
2929
- PostgreSQL via [pg](https://www.npmjs.com/package/pg) or [postgres](https://www.npmjs.com/package/postgres).
3030
- MySQL via [mysql2](https://www.npmjs.com/package/mysql2).
31-
- SQLite via [sqlite3](https://www.npmjs.com/package/better-sqlite3).
31+
- SQLite via [better-sqlite3](https://www.npmjs.com/package/better-sqlite3) or
32+
Deno's [@db/sqlite](https://jsr.io/@db/sqlite).
33+
34+
Set `runtime` to `node`, `bun`, or `deno`. Deno output imports npm drivers with
35+
`npm:` specifiers, such as `npm:pg`, `npm:postgres`, `npm:mysql2`, and
36+
`npm:better-sqlite3`. The `@db/sqlite` driver requires `runtime: deno` and imports
37+
`jsr:@db/sqlite`. `driver: "jsr:@db/sqlite"` is an alias for `driver: "@db/sqlite"`.
38+
39+
Deno and `@db/sqlite` support require a build from this source tree; the released
40+
`0.1.3` WASM plugin shown in the existing configurations does not include them.
41+
See [Deno and @db/sqlite](#deno-and-dbsqlite) for local build instructions and a
42+
complete configuration.
3243

3344
## Getting started
3445

@@ -37,8 +48,9 @@ This tutorial assumes that the latest version of sqlc is
3748

3849
We'll generate TypeScript here, but other [language
3950
plugins](https://docs.sqlc.dev/en/latest/reference/language-support.html) are
40-
available. You'll need Bun (or Node.js) installed if you want to build and run a
41-
program with the code sqlc generates, but sqlc itself has no dependencies.
51+
available. This tutorial uses Bun (or Node.js) to run the generated program.
52+
For Deno, use the [Deno SQLite example](examples/deno-db-sqlite/README.md).
53+
sqlc itself has no dependencies.
4254

4355
We'll also rely on sqlc's [managed databases](https://docs.sqlc.dev/en/latest/howto/managed-databases.html),
4456
which require a sqlc Cloud project and auth token. You can get those from
@@ -334,6 +346,37 @@ sql:
334346
driver: better-sqlite3 # npm package name
335347
```
336348

349+
### Deno and @db/sqlite
350+
351+
Use a WASM plugin built from this source tree, following the
352+
[development instructions](#development). Set its absolute path in `sqlc.yaml`:
353+
354+
```yaml
355+
version: "2"
356+
plugins:
357+
- name: ts
358+
wasm:
359+
url: file:///absolute/path/to/sqlc-gen-typescript/examples/plugin.wasm
360+
sql:
361+
- schema: "schema.sql"
362+
queries: "query.sql"
363+
engine: sqlite
364+
codegen:
365+
- out: db
366+
plugin: ts
367+
options:
368+
runtime: deno
369+
driver: "@db/sqlite"
370+
```
371+
372+
The generated code runs with Deno alone. It imports `jsr:@db/sqlite` and emits
373+
synchronous functions that work inside `database.transaction` callbacks. Each
374+
call finalizes its prepared statement. Integer types allow `number | bigint`;
375+
open the database with `{ int64: true }` to preserve large SQLite integers.
376+
377+
The [Deno example](examples/deno-db-sqlite/README.md) includes a pinned driver,
378+
a runnable program, query tests, and generation instructions.
379+
337380
## Development
338381

339382
If you want to build and test sqlc-gen-typescript locally, follow these steps:
@@ -389,5 +432,8 @@ Check the `Makefile` for details.
389432
sqlc generate
390433
```
391434

435+
After building `examples/plugin.wasm`, run `make test-deno` with Deno 2 and
436+
sqlc installed to regenerate, check, test, and run the Deno example.
437+
392438
For more details on sqlc development, refer to the sqlc core development guide. This guide provides additional information on setting up and working with sqlc in general, which may be useful for contributors to this project.
393439
https://docs.sqlc.dev/en/latest/guides/development.html

‎examples/deno-db-sqlite/README.md‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Deno and @db/sqlite
2+
3+
This example runs the shared SQLite author queries with Deno 2 and
4+
[`@db/sqlite`](https://jsr.io/@db/sqlite). It uses an in-memory database and
5+
needs no database server. `deno.json` pins the driver to version `0.13.0`.
6+
7+
To run the checked-in generated code, install Deno 2, then run from this folder:
8+
9+
```sh
10+
deno task check
11+
deno task start
12+
deno task test
13+
```
14+
15+
Node.js and sqlc are not needed to run the example. To regenerate its code,
16+
install sqlc and build the WASM plugin from this source tree. The released
17+
`0.1.3` plugin does not include Deno or `@db/sqlite` support. The source build
18+
uses the repository's existing Node.js/npm and Javy toolchain. Place the
19+
[Javy v1.2.0 executable](https://github.com/bytecodealliance/javy/releases/tag/v1.2.0)
20+
at `./javy`, then run from the repository root:
21+
22+
```sh
23+
npm install
24+
make examples/plugin.wasm
25+
cd examples/deno-db-sqlite
26+
deno task generate
27+
```
28+
29+
`sqlc.yaml` uses the local `../plugin.wasm` build. Once built, generating code
30+
requires only sqlc; the plugin runs as WASM.
31+
32+
The `start` and `test` tasks use `-A` because the driver loads a native SQLite
33+
library through Deno's FFI. On the first run, the driver also downloads and
34+
caches that library. See the
35+
[driver's permission notes](https://jsr.io/@db/sqlite) for more detail.
36+
37+
Generated functions are synchronous, so they work inside `database.transaction`
38+
callbacks. They finalize each prepared statement, return `null` when a `:one`
39+
query finds no row, and return camelCase properties for SQL column names such as
40+
`display_name`. Blobs use `Uint8Array`. The example opens its database with
41+
`{ int64: true }` so large SQLite integers retain their value; generated integer
42+
types allow `number | bigint`.
43+
44+
The tests cover author CRUD, missing rows, nullable values, camelCase results,
45+
blobs, large integers, inserted row IDs, and rollback after a failed query.

‎examples/deno-db-sqlite/deno.json‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"imports": {
3+
"jsr:@db/sqlite": "jsr:@db/sqlite@0.13.0"
4+
},
5+
"tasks": {
6+
"generate": "sqlc generate",
7+
"check": "deno check src/main.ts tests/queries_test.ts",
8+
"start": "deno run -A src/main.ts",
9+
"test": "deno test -A tests/"
10+
}
11+
}

‎examples/deno-db-sqlite/deno.lock‎

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎examples/deno-db-sqlite/sqlc.yaml‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
version: "2"
2+
plugins:
3+
- name: ts
4+
wasm:
5+
url: file://../plugin.wasm
6+
sql:
7+
- schema: "../authors/sqlite/schema.sql"
8+
queries: "../authors/sqlite/query.sql"
9+
engine: sqlite
10+
codegen:
11+
- plugin: ts
12+
out: src/db
13+
options:
14+
runtime: deno
15+
driver: "@db/sqlite"
16+
- schema: "tests/fixtures/schema.sql"
17+
queries: "tests/fixtures/query.sql"
18+
engine: sqlite
19+
codegen:
20+
- plugin: ts
21+
out: tests/db
22+
options:
23+
runtime: deno
24+
driver: "jsr:@db/sqlite"
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Code generated by sqlc. DO NOT EDIT.
2+
3+
import type { Database } from "jsr:@db/sqlite";
4+
5+
export const getAuthorQuery = `-- name: GetAuthor :one
6+
SELECT id, name, bio FROM authors
7+
WHERE id = ? LIMIT 1`;
8+
9+
export interface GetAuthorArgs {
10+
id: number | bigint;
11+
}
12+
13+
export interface GetAuthorRow {
14+
id: number | bigint;
15+
name: string;
16+
bio: string | null;
17+
}
18+
19+
export function getAuthor(database: Database, args: GetAuthorArgs): GetAuthorRow | null {
20+
const stmt = database.prepare(getAuthorQuery);
21+
try {
22+
const row = stmt.value<[
23+
number | bigint,
24+
string,
25+
string | null
26+
]>(args.id);
27+
if (row === undefined) {
28+
return null;
29+
}
30+
return {
31+
id: row[0],
32+
name: row[1],
33+
bio: row[2]
34+
};
35+
}
36+
finally {
37+
stmt.finalize();
38+
}
39+
}
40+
41+
export const listAuthorsQuery = `-- name: ListAuthors :many
42+
SELECT id, name, bio FROM authors
43+
ORDER BY name`;
44+
45+
export interface ListAuthorsRow {
46+
id: number | bigint;
47+
name: string;
48+
bio: string | null;
49+
}
50+
51+
export function listAuthors(database: Database): ListAuthorsRow[] {
52+
const stmt = database.prepare(listAuthorsQuery);
53+
try {
54+
const rows = stmt.values<[
55+
number | bigint,
56+
string,
57+
string | null
58+
]>();
59+
return rows.map(row => ({
60+
id: row[0],
61+
name: row[1],
62+
bio: row[2]
63+
}));
64+
}
65+
finally {
66+
stmt.finalize();
67+
}
68+
}
69+
70+
export const createAuthorQuery = `-- name: CreateAuthor :exec
71+
INSERT INTO authors (
72+
name, bio
73+
) VALUES (
74+
?, ?
75+
)`;
76+
77+
export interface CreateAuthorArgs {
78+
name: string;
79+
bio: string | null;
80+
}
81+
82+
export function createAuthor(database: Database, args: CreateAuthorArgs): void {
83+
const stmt = database.prepare(createAuthorQuery);
84+
try {
85+
stmt.run(args.name, args.bio);
86+
}
87+
finally {
88+
stmt.finalize();
89+
}
90+
}
91+
92+
export const deleteAuthorQuery = `-- name: DeleteAuthor :exec
93+
DELETE FROM authors
94+
WHERE id = ?`;
95+
96+
export interface DeleteAuthorArgs {
97+
id: number | bigint;
98+
}
99+
100+
export function deleteAuthor(database: Database, args: DeleteAuthorArgs): void {
101+
const stmt = database.prepare(deleteAuthorQuery);
102+
try {
103+
stmt.run(args.id);
104+
}
105+
finally {
106+
stmt.finalize();
107+
}
108+
}
109+

0 commit comments

Comments
 (0)