Skip to content

Commit 3bfdff7

Browse files
committed
feat: inital commit
1 parent 5747568 commit 3bfdff7

32 files changed

Lines changed: 1444 additions & 0 deletions

.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
root = true
2+
3+
[*]
4+
indent_style = tab
5+
end_of_line = lf
6+
charset = utf-8
7+
trim_trailing_whitespace = true
8+
insert_final_newline = true
9+
10+
[*.md]
11+
trim_trailing_whitespace = false

.github/workflows/ci.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches:
9+
- master
10+
11+
jobs:
12+
lint:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
- run: corepack enable
18+
- uses: actions/setup-node@v4
19+
with:
20+
node-version: 20
21+
22+
- name: Install dependencies
23+
run: npx nypm@latest i
24+
25+
- name: Lint
26+
run: npm run lint
27+
28+
test:
29+
runs-on: ubuntu-latest
30+
31+
steps:
32+
- uses: actions/checkout@v4
33+
- run: corepack enable
34+
- uses: actions/setup-node@v4
35+
with:
36+
node-version: 20
37+
38+
- name: Install dependencies
39+
run: npx nypm@latest i
40+
41+
- name: Playground prepare
42+
run: npm run dev:prepare
43+
44+
- name: Test
45+
run: npm run test

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
node_modules
2+
playground/node_modules
3+
*.log*
4+
5+
dist
6+
.nuxt
7+
.output
8+
.data
9+
.build-*
10+
11+
.env
12+
13+
# Testing
14+
reports
15+
coverage
16+
*.lcov
17+
.nyc_output
18+

.npmrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
shamefully-hoist=true
2+
strict-peer-dependencies=false

README.md

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
# @witchcraft/nuxt-postgres
2+
3+
[![npm version][npm-version-src]][npm-version-href]
4+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
5+
[![License][license-src]][license-href]
6+
[![Nuxt][nuxt-src]][nuxt-href]
7+
8+
Nuxt module to connect to postgres with drizzle. Also has support for having a client [PGlite](https://pglite.dev/) db and a server pglite instance for testing.
9+
10+
# Setup
11+
12+
```ts
13+
// nuxt.config.ts
14+
export default defineNuxtConfig({
15+
modules: ["@witchcraft/nuxt-postgres"],
16+
postgres: {
17+
connectionsOptions: {
18+
...
19+
},
20+
}
21+
})
22+
```
23+
24+
Use the included drizzle config if you want, it will ensure you define the right variables:
25+
```ts
26+
```ts [drizzleConfig.ts]
27+
// careful with imports, esm is borked, see nuxt-postgres/src/drizzleConfig.ts
28+
// special workarounds were created for the these imports so they work
29+
import { drizzleConfig } from "@witchcraft/nuxt-postgres/drizzleConfig.js"
30+
import { ensureEnv } from "@witchcraft/nuxt-utils/utils/ensureEnv.js"
31+
import { defineConfig } from "drizzle-kit"
32+
import path from "path"
33+
34+
// you can ensure futher env vars here
35+
ensureEnv(process.env, [
36+
"ROOT_DIR",
37+
] as const)
38+
39+
export default defineConfig({
40+
...drizzleConfig,
41+
schema: path.resolve(process.env.ROOT_DIR, "db/schema.ts"),
42+
// change if you changed it
43+
// out: "./db/migrations",
44+
})
45+
```
46+
47+
To migrate the db when starting the server:
48+
```ts [server/plugins/init.ts]
49+
import { migrate } from "#postgres"
50+
51+
export default defineNitroPlugin(() => {
52+
// there's no way to await this yet
53+
// see https://github.com/nitrojs/nitro/issues/915
54+
void migrate({
55+
// initialization script
56+
// e.g. create extensions
57+
preMigrationScript: `CREATE EXTENSION IF NOT EXISTS pg_uuidv7;`
58+
})
59+
})
60+
```
61+
62+
```ts
63+
// in api handler
64+
65+
export default defineEventHandler(async event => {
66+
const pg = event.context.$postgres
67+
})
68+
```
69+
70+
// or
71+
```ts
72+
import { postgres } from "#postgres"
73+
74+
```
75+
76+
## Local Postgres Client
77+
78+
The module also supports having a local postgres client using PGlite, just enable the `postgres.useClientDb` option.
79+
80+
Then define a `db/client-schema.ts` file and create a `drizzle-client.config.ts` file in the root of your project (there is an equivelant `clientDrizzleConfig` you can import like above).
81+
82+
Modify the package.json scripts:
83+
84+
```json
85+
{
86+
"scripts": {
87+
"db:generate": "drizzle-kit generate && drizzle-kit generate --config drizzle-client.config.ts",
88+
"db:migrate": "drizzle-kit migrate",
89+
}
90+
}
91+
```
92+
Migrate does not have a client equivalent. The module handles migrations by generating a `clientMigration.json` file which you can then import.
93+
94+
Now you can use your client side db.
95+
96+
```ts
97+
// db is only defined if import.meta.client is true
98+
99+
const db = await useClientDb("client",
100+
// options only required on the first use
101+
{
102+
// module client postgres options
103+
// this is so that it can also be used where nuxt isn't available
104+
...useRuntimeConfig().public.postgres,
105+
clientMigrationOptions: {
106+
migrationJson: (await import("~~/db/client-migrations/clientMigration.json")).default,
107+
migrationsLogger: useLogger(),
108+
},
109+
clientPgliteOptions: {
110+
// additional pglite options
111+
}
112+
}, {
113+
// clientDatabaseManager options
114+
})
115+
await db?.query(...)
116+
// somewhere else
117+
118+
const db = useClientDb(/*"client" by default*/)
119+
// if your db is not named "client", you must use useSwitchDefaultDatabase to have the default useDb use that instance
120+
useSwitchDefaultDatabase("myDb")
121+
const db = useClientDb(/* now "myDb"*/)
122+
123+
124+
// useClientDb also adds window.db and window.sql in dev mode
125+
// for easier debugging
126+
await window.dbs.client?.query(...)
127+
await window.dbs.custom_name?.query(...)
128+
```
129+
130+
By default, the module will automatically migrate the client side db on first use of `useClientDb` if `migrationsJson` is passed, hence the await on `useClientDb`.
131+
132+
To disable this, just don't pass `migrationsJson` to `useClientDb`.
133+
134+
You will then need to migrate manually and make sure nothing tries to use the db before then or that your app can handled the db schema being out of date or non-existent:
135+
136+
```ts
137+
import {migrate} from "#postgres-client"
138+
139+
// only migrate on the client
140+
if (import.meta.client) {
141+
// careful, you can't use useRuntimeConfig().public.postgres.clientMigrationConfig.migrationJsonPath here
142+
const migrationjson = (await import("~~/db/client-migrations/clientMigration.json")).default
143+
await migrate({
144+
migrationjson
145+
})
146+
}
147+
const db = useClientDb("client")
148+
```
149+
150+
There are several things to keep in mind when using the client side db:
151+
152+
- While the resolved migrationJson location is added to the public runtime config, it cannot be used to import it dynamically since dynamic imports don't work with variables.
153+
- **The client options are exposed to the public runtime config.** There is no such thing as the private runtimeConfig client side.
154+
- `migrate` will try to skip migrations if at all possible. Doing a drizzle migration, even if nothing needs to be done, is expensive (~1500ms), so `migrate` stores a localstorage key `db:lastMigrationHash` (configurable) to prevent unnecessary calls to drizzle's migrate. If the database is configured to use indexedDb, it exists, and the last known hash matches the last migration hash, migration is skipped, reducing the time for non-migrations to around 4-5ms.
155+
156+
## Using a Local Server Database for Testing
157+
158+
You can change to use an in-memory pglite database by setting `usePgLiteOnServer` to `true` or some env variable (e.g. `VITEST`) to only change it when that variable is true. You can make it local by specifying `serverPgLiteDataDir`.
159+
160+
## Usage in Other Contexts
161+
162+
The client db can be used in electron or other contexts that support it. You will probably need to specify some options differently for those environments:
163+
164+
165+
166+
Electron example using [@witchcraft/nuxt-electron](TODO):
167+
```ts
168+
const db = await useClientDb("name", {
169+
clientMigrationOptions: {
170+
migrationJson,
171+
migrationsLogger: useElectronLogger(),
172+
},
173+
...STATIC.ELECTRON_RUNTIME_CONFIG.postgres,
174+
// we need to override the filepath so it can write to disk
175+
clientPgLitePath: path.join(userDataDir, "db.pglite"),
176+
}, {
177+
logger: useElectronLogger(),
178+
// import.meta.client is not defined
179+
bypassEnvCheck: true,
180+
// there is no window in main
181+
addToWindowInDev: false,
182+
}
183+
184+
```
185+
186+
### Proxying to Other Contexts
187+
188+
You can use a proxy instead of the default client with the `drizzleProxy` option.
189+
190+
```ts
191+
await useClientDb("client", {
192+
...useRuntimeConfig().public.postgres,
193+
useWebWorker: false,
194+
drizzleProxy: isElectron()
195+
? async (name: string, sql: string, params: any[], method: "all" | "run" | "get" | "values") => {
196+
// however you choose to proxy it
197+
const res = await window.electron.api.db(name, sql, params, method)
198+
return res
199+
}
200+
: undefined
201+
})
202+
```
203+
204+
<!-- Badges -->
205+
[npm-version-src]: https://img.shields.io/npm/v/@witchcraft/nuxt-postgres/latest.svg?style=flat&colorA=020420&colorB=00DC82
206+
[npm-version-href]: https://npmjs.com/package/@witchcraft/nuxt-postgres
207+
208+
[npm-downloads-src]: https://img.shields.io/npm/dm/@witchcraft/nuxt-postgres.svg?style=flat&colorA=020420&colorB=00DC82
209+
[npm-downloads-href]: https://npmjs.com/package/@witchcraft/nuxt-postgres
210+
211+
[license-src]: https://img.shields.io/npm/l/@witchcraft/nuxt-postgres.svg?style=flat&colorA=020420&colorB=00DC82
212+
[license-href]: https://npmjs.com/package/@witchcraft/nuxt-postgres
213+
214+
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
215+
[nuxt-href]: https://nuxt.com
216+

eslint.config.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @ts-check
2+
import { vueConfig } from "@alanscodelog/eslint-config"
3+
import { createConfigForNuxt } from "@nuxt/eslint-config/flat"
4+
5+
// Run `npx @eslint/config-inspector` to inspect the resolved config interactively
6+
export default createConfigForNuxt({
7+
features: {
8+
tooling: false, // is overriding standalone?
9+
stylistic: false,
10+
standalone: false
11+
},
12+
dirs: {
13+
src: [
14+
"./playground",
15+
],
16+
},
17+
})
18+
.append(
19+
...vueConfig,
20+
{
21+
ignores: [".nuxt/"],
22+
// for auto imports
23+
rules: {
24+
"no-undef": "off",
25+
}
26+
}
27+
)

0 commit comments

Comments
 (0)