@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.
- package/CHANGELOG.md +9 -0
- package/README.md +22 -194
- 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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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/
|
|
8
|
+
npm install @typesql/core @typesql/drizzle drizzle-orm
|
|
10
9
|
```
|
|
11
10
|
|
|
12
|
-
`drizzle-orm`
|
|
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
|
|
19
|
-
import {
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|