@typesql/drizzle 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +22 -194
  3. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @typesql/drizzle
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - The README is shorter: install, one example and links to the docs site, which now has the details it used to repeat.
8
+
9
+ - Updated dependencies:
10
+ - @typesql/core@0.6.0
11
+
3
12
  ## 0.2.0
4
13
 
5
14
  ### Minor Changes
package/README.md CHANGED
@@ -1,41 +1,32 @@
1
1
  # @typesql/drizzle
2
2
 
3
- Use your Drizzle ORM schema as a TypeSQL schema. `FromDrizzle<typeof schema>` turns
4
- `pgTable`, `pgView`, `pgMaterializedView`, `pgEnum` and `pgSchema` definitions into the
5
- `DB` type that `createSql` expects, entirely at the type level: no code generation, and the
6
- SQL you write is checked against the same tables Drizzle manages.
3
+ Write [TypeSQL](https://www.npmjs.com/package/@typesql/core) queries against your [Drizzle](https://orm.drizzle.team) schema, and run them on your Drizzle database. The schema is converted in the type checker: nothing is generated.
4
+
5
+ ## Install
7
6
 
8
7
  ```sh
9
- npm install @typesql/drizzle @typesql/core
8
+ npm install @typesql/core @typesql/drizzle drizzle-orm
10
9
  ```
11
10
 
12
- `drizzle-orm` (0.45 or later) is a peer dependency. Queries run on node-postgres or postgres.js
13
- databases.
11
+ Requires `drizzle-orm` 0.45 or later, on node-postgres or postgres.js.
14
12
 
15
13
  ## Usage
16
14
 
17
15
  ```ts
18
- // schema.ts — your existing Drizzle schema
19
- import { relations } from "drizzle-orm";
20
- import { integer, pgEnum, pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
21
-
22
- export const mood = pgEnum("mood", ["happy", "sad"]);
16
+ // schema.ts: your Drizzle schema
17
+ import { integer, pgTable, serial, text } from "drizzle-orm/pg-core";
23
18
 
24
19
  export const users = pgTable("users", {
25
20
  id: serial().primaryKey(),
26
21
  email: text().notNull(),
27
22
  displayName: text("display_name"),
28
- mood: mood(),
29
23
  });
30
24
 
31
25
  export const posts = pgTable("posts", {
32
26
  id: serial().primaryKey(),
33
27
  authorId: integer("author_id").notNull().references(() => users.id),
34
28
  title: text().notNull(),
35
- createdAt: timestamp("created_at").notNull().defaultNow(),
36
29
  });
37
-
38
- export const usersRelations = relations(users, ({ many }) => ({ posts: many(posts) }));
39
30
  ```
40
31
 
41
32
  ```ts
@@ -48,188 +39,25 @@ import * as schema from "./schema";
48
39
  const db = drizzle(new Pool(), { schema });
49
40
  const sql = createSql<typeof schema>(db);
50
41
 
51
- const q = sql(
52
- "SELECT u.email, u.display_name, p.title FROM users u JOIN posts p ON p.author_id = u.id WHERE u.mood = $1",
53
- "happy",
54
- );
55
- // Query<{ email: string; display_name: string | null; title: string }, ["happy" | "sad"]>
56
-
57
- const post = await sql("INSERT INTO posts (author_id, title) VALUES ($1, $2) RETURNING id, created_at", 1, "Hello").executeTakeFirstOrThrow();
58
- // id and created_at have defaults, so they can be left out
59
-
60
- await sql.transaction(async (tx) => {
61
- await tx("UPDATE posts SET title = upper(title) WHERE author_id = $1", 1).execute();
62
- });
63
- ```
64
-
65
- Queries use SQL names: `display_name`, not `displayName`. Relations and any other
66
- non-table exports are ignored, so `typeof schema` can be passed as is.
67
-
68
- `createSql<Schema, TypeMap, Options>(db)` uses core's `DefaultTypeMap` unless you pass
69
- another one, such as `PgTypeMap` from `@typesql/pg` for node-postgres' exact types:
70
- `createSql<typeof schema, PgTypeMap>(db)`.
71
-
72
- ## Running queries
73
-
74
- `db` is a Drizzle database on node-postgres or postgres.js (`drizzle-orm/node-postgres`,
75
- `drizzle-orm/postgres-js`), or a transaction from `db.transaction(fn)`. Queries run on the
76
- driver client Drizzle holds: the `Pool`, `Client` or postgres.js `sql` you gave `drizzle()`, or
77
- the transaction's connection. Rows are what the driver returns; Drizzle's column mappers and
78
- its logger are not involved.
79
-
80
- | | |
81
- |---|---|
82
- | `q.execute()`, `q.executeTakeFirst()`, `q.executeTakeFirstOrThrow()`, `q.executeCount()` | `Promise<Row[]>`, `Promise<Row \| undefined>`, `Promise<Row>` (`NoResultError` on 0 rows), `Promise<number>` (the driver's row count) |
83
- | `sql.transaction(fn)` | `db.transaction(fn)` with `tx` bound to the transaction; `tx.transaction(fn)` is Drizzle's nested transaction (a savepoint) |
84
- | `executor(db)` | an `Executor` for `q.execute(executor)` and core's `createSql(executor)` |
85
- | `DrizzleSql<Schema, TM, Options>`, `DrizzleDatabase` | the type `createSql` returns, and what it accepts |
86
-
87
- To mix ORM and SQL queries in one transaction, bind a `sql` to Drizzle's `tx`:
88
-
89
- ```ts
90
- await db.transaction(async (tx) => {
91
- await tx.insert(schema.users).values({ email: "a@b.c" });
92
- await createSql<typeof schema>(tx)("UPDATE users SET mood = 'happy' WHERE email = $1", "a@b.c").execute();
93
- });
42
+ const rows = await sql(
43
+ `SELECT u.email, u.display_name, p.title
44
+ FROM users u JOIN posts p ON p.author_id = u.id
45
+ WHERE u.id = $1`,
46
+ 1,
47
+ ).execute();
48
+ // { email: string; display_name: string | null; title: string }[]
94
49
  ```
95
50
 
96
- `drizzle-orm/postgres-js` turns off postgres.js' date parsers on the client it is given, so on
97
- that driver `date`, `timestamp`, `timestamptz` and `interval` come back as strings: pass a type
98
- map that says so.
51
+ Good to know:
99
52
 
100
- To pair the schema with a driver adapter instead, convert it with `FromDrizzle` and use the
101
- adapter's `createSql` on the same pool:
53
+ - SQL uses the database names (`display_name`), not the TypeScript keys (`displayName`).
54
+ - `.$defaultFn()` and `.$default()` run in Drizzle, not in Postgres, so a SQL `INSERT` must set those columns.
55
+ - Drizzle's types don't record `withTimezone`, so `timestamp()` columns are `timestamp`. Pin a column with `pgType` from this package: `pgType(timestamp("at", { withTimezone: true }), "timestamptz")`.
102
56
 
103
- ```ts
104
- import { createSql } from "@typesql/pg";
105
- import type { FromDrizzle } from "@typesql/drizzle";
106
- import type * as schema from "./schema";
57
+ ## Documentation
107
58
 
108
- const sql = createSql<FromDrizzle<typeof schema>>(pool);
109
- ```
59
+ See [From Kysely, Drizzle or Prisma](https://typesql.karsli.net/guide/orms#drizzle) for how the schema is converted, running queries and overrides, and [Limits](https://typesql.karsli.net/guide/limits#orm-schemas) for what the conversion can't tell.
110
60
 
111
- `FromDrizzle` also accepts a single table (`FromDrizzle<typeof users>`) or a record of
112
- tables (`FromDrizzle<{ users: typeof users; posts: typeof posts }>`).
113
-
114
- ## What gets converted
115
-
116
- | Drizzle | TypeSQL |
117
- |---|---|
118
- | `pgTable("users", …)` | `DB["users"]`, keyed by the SQL table name, not the export name |
119
- | `displayName: text("display_name")` | column `display_name`; columns without an explicit name use the key |
120
- | `.notNull()`, `.primaryKey()`, `serial`, identity | `NOT NULL`; otherwise the column is nullable |
121
- | `.default(…)`, `.defaultNow()`, `.defaultRandom()`, `serial`, `generatedAlwaysAsIdentity()`, `generatedByDefaultAsIdentity()`, `generatedAlwaysAs(…)` | has a default: optional in `INSERT` |
122
- | `.$defaultFn(…)` / `.$default(…)` | **no** default: these run in Drizzle, not in Postgres |
123
- | `export const mood = pgEnum("mood", […])` | `DB["$types"]["mood"]`, keyed by the **export name** (see below) |
124
- | `pgSchema("auth").table("users", …)` | `DB["auth"]["users"]` (`auth.users` in SQL) |
125
- | `pgView(…)`, `pgMaterializedView(…)` | a `View<…>`: readable, rejected as an `INSERT`/`UPDATE`/`DELETE` target |
126
- | `relations(…)`, sequences, other exports | ignored |
127
-
128
- ## Column types
129
-
130
- The Postgres type comes from Drizzle's column kind. Drizzle's `mode` options change the JS
131
- value Drizzle produces, but TypeSQL queries go straight to the driver, so the row type comes
132
- from the type map: `bigint({ mode: "number" })` is `int8`, a `string` under node-postgres.
133
-
134
- | Drizzle | Postgres |
135
- |---|---|
136
- | `smallint`, `smallserial` | `int2` |
137
- | `integer`, `serial` | `int4` |
138
- | `bigint`, `bigserial` (any mode) | `int8` |
139
- | `numeric` / `decimal` (any mode) | `numeric` |
140
- | `real` | `float4` |
141
- | `doublePrecision` | `float8` |
142
- | `boolean` | `bool` |
143
- | `text` | `text` |
144
- | `varchar` | `varchar` |
145
- | `char` | `bpchar` |
146
- | `uuid` | `uuid` |
147
- | `date` (any mode) | `date` |
148
- | `time` | `time`, or `timetz` (see below) |
149
- | `timestamp` (any mode) | `timestamp`, or `timestamptz` (see below) |
150
- | `interval` | `interval` |
151
- | `json`, `jsonb` | `json`, `jsonb` |
152
- | `inet`, `cidr`, `macaddr`, `macaddr8` | same name |
153
- | `point` (any mode), `line` (any mode) | `point`, `line` |
154
- | `geometry` (PostGIS) | `geometry` |
155
- | `vector`, `halfvec`, `sparsevec`, `bit` (pgvector) | `vector`, `halfvec`, `sparsevec`, `bit` |
156
- | an enum column | the enum's export name, values in `$types` |
157
- | `customType(…)` | `unknown`, unless pinned with `pgType` |
158
- | `.array()`, `.array().array()` | `T[]`, `T[][]` |
159
-
160
- Types TypeSQL has no TS mapping for (`geometry`, `vector`, …) read as `unknown` unless your
161
- type map has an entry for them.
162
-
163
- ### `timestamp` with time zone, and other types Drizzle's types do not record
164
-
165
- `timestamp("at", { withTimezone: true })` and `timestamp("at")` have the same TypeScript type
166
- in Drizzle, so `FromDrizzle` cannot tell them apart. Both read as `Date` under the default
167
- type maps, but `timestamp` and `timestamptz` behave differently in SQL (`AT TIME ZONE`,
168
- `date_trunc`, comparisons with `now()`). Either set the default for all of them:
169
-
170
- ```ts
171
- createSql<typeof schema, DefaultTypeMap, { timestamp: "timestamptz"; time: "timetz" }>(db);
172
- // or: FromDrizzle<typeof schema, { timestamp: "timestamptz" }>
173
- ```
174
-
175
- or pin a column's type with `pgType`, which returns the builder unchanged at runtime:
176
-
177
- ```ts
178
- import { pgType } from "@typesql/drizzle";
179
-
180
- export const events = pgTable("events", {
181
- at: pgType(timestamp("at", { withTimezone: true }).notNull(), "timestamptz"),
182
- handle: pgType(citext("handle"), "text"), // a customType
183
- });
184
- ```
185
-
186
- `pgType` takes precedence over everything else and works for any column. For an array
187
- column, wrap the whole array: `pgType(citext().array(), "text[]")`.
188
-
189
- ### Enums
190
-
191
- Drizzle's types do not carry an enum's SQL name (`pgEnum("mood", …)` is typed as
192
- `string`), so enums are registered in `$types` under their **export name**, and an enum
193
- column is typed by finding the exported enum with the same values. To use casts such as
194
- `'happy'::mood`, name the export like the SQL type (`export const mood = pgEnum("mood", …)`).
195
- Object enums need `as const` (`pgEnum("role", { Admin: "admin" } as const)`) for their
196
- values to be known. An enum column whose enum is not exported (or when you pass only a
197
- table or a record of tables) is typed as `text`.
198
-
199
- ### `casing`
200
-
201
- If you create Drizzle with `drizzle(client, { casing: "snake_case" })`, pass the same
202
- setting so column names match what Drizzle sends to Postgres:
203
-
204
- ```ts
205
- createSql<typeof schema, DefaultTypeMap, { casing: "snake_case" }>(db);
206
- // createdAt: timestamp() → column created_at
207
- ```
61
+ ## License
208
62
 
209
- ## Limitations
210
-
211
- - **Generated-always columns** (`generatedAlwaysAs`, `generatedAlwaysAsIdentity`) are treated
212
- as columns with a default. TypeSQL has no read-only columns, so an `INSERT` that sets one
213
- type-checks but fails in Postgres (unless `OVERRIDING SYSTEM VALUE` for identities).
214
- - **`$onUpdate` / `$onUpdateFn`** mark the column as having a default in Drizzle's types, with
215
- no way to tell them from a SQL default, so such a column is optional in `INSERT` even
216
- though Postgres will not fill it.
217
- - **`.$type<T>()`** is ignored: rows use the type map (`jsonb().$type<Settings>()` is
218
- `unknown` under the default map). Branded ids lose their brand.
219
- - **`text({ enum: [...] })`, `varchar({ enum: [...] })`** are plain `text` / `varchar`: the
220
- values are only a TypeScript constraint in Drizzle, not a Postgres type.
221
- - **Views** take their column types from the selected Drizzle columns. Columns from the
222
- outer side of a join keep their own nullability, and `sql\`…\`.as("x")` fields are
223
- `unknown`. Views created with `pgSchema(…).view(…)` are placed at the top level, because
224
- Drizzle's view types do not record the schema.
225
- - **`pgTableCreator`** prefixes are applied at runtime only; tables appear under the name
226
- passed to the table function.
227
- - **`casing`** is applied to every column name, including explicitly named ones; Drizzle
228
- applies it only to columns named after their key. These agree unless an explicit name is
229
- not already in the target casing.
230
- - Multi-dimensional arrays are typed `T[][]`; Postgres itself does not distinguish them
231
- from `T[]`.
232
-
233
- ## Examples
234
-
235
- [`examples/schema.ts`](examples/schema.ts) and [`examples/basic.ts`](examples/basic.ts) walk through the features above; they are type-checked with the package (`pnpm typecheck`).
63
+ [MIT](LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typesql/drizzle",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Use Drizzle ORM table definitions as a TypeSQL schema.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,14 +28,14 @@
28
28
  "access": "public"
29
29
  },
30
30
  "dependencies": {
31
- "@typesql/core": "0.5.0"
31
+ "@typesql/core": "0.6.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "drizzle-orm": ">=0.45"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/pg": "^8.23.1",
38
- "@typesql/pg": "0.2.0",
38
+ "@typesql/pg": "0.2.1",
39
39
  "drizzle-orm": "^0.45.4",
40
40
  "pg": "^8.23.1",
41
41
  "postgres": "^3.4.9",