nuxt-pglite
v0.2.9
Published
A Nuxt module aimed to simplify the use of PGlite.
Downloads
460
Readme
Nuxt PGlite
A Nuxt module aimed to simplify the use of PGlite.
PGlite, an Embeddable Postgres Run a full Postgres database locally in WASM with reactivity and live sync.
[!WARNING]
No docs are available (although planned), please refer to the playground code.
Features
- ⚡️ Server-side
usePGlite
, running in your Node or Bun server. - 🧑💻 Client-side
usePGlite
, running inside Web Workers. - 🪢 Client-side
useLiveQuery
anduseLiveIncrementalQuery
to subscribe to live changes.
Quick Setup
Install the module to your Nuxt application with one command:
npx nuxi module add nuxt-pglite
That's it! You can now use Nuxt PGlite in your Nuxt app ✨
Storage
You can configure where to store data in your nuxt.config.ts
. Server-side storage accepts relative baths:
export default defineNuxtConfig({
modules: ['nuxt-pglite'],
pglite: {
client: {
options: {
dataDir: 'idb://nuxt-pglite',
},
},
server: {
options: {
dataDir: './database/pglite', // will use `~~/server/database/pglite`
},
},
},
})
For supported filesystem please refer to the official documentation.
Extensions
Extensions are automatically configured with full type support and can be added via nuxt.config.ts
:
export default defineNuxtConfig({
modules: ['nuxt-pglite'],
pglite: {
client: {
extensions: ['live', 'electricSync'],
},
},
})
For a full list of available extensions please refer to the official docs. If a new extension is missing feel free to open up a new PR by adding it to this file. I do plan to support only official and contrib extensions.
[!WARNING]
Auto configuration for server-side extensions will be supported once Nuxtv3.15
gets released. See below how to use hooks to add extensions server-side.
Live Queries
With Live Queries we can subscrive to events happening in the database and reactively update the user interface. This becomes particularly usefuly client-side thanks to Web Workers, allowing us to keep content in sync even when the user opens up multiple tabs.
To get started simply add live
extension to your nuxt.config.ts
:
export default defineNuxtConfig({
modules: ['nuxt-pglite'],
pglite: {
client: {
extensions: [
// ...
'live',
],
},
},
})
This will enable auto-import for useLiveQuery
and useLiveIncrementalQuery
. The quick implementation would be:
<script setup lang="ts">
const maxNumber = ref(100)
const items = useLiveQuery.sql`
SELECT *
FROM my_table
WHERE number <= ${maxNumber.value}
ORDER BY number;
`
</script>
Live queries are currently a port of the upstream implementation, you can read more here.
Hooks
We can use hooks to customize or extend PGlite at runtime. This becomes particularly useful in conjunction with RLS
or adding custom extensions server-side.
RLS
PGlite supports RLS out of the box, but being a single-user/single-connection database it is more frequent to be used only client side. Lets take in example a basic implementation with nuxt-auth-utils
. We'll need to create a client-only Nuxt plugin /plugins/rls.client.ts
:
export default defineNuxtPlugin((nuxtApp) => {
const { user } = useUserSession()
if (user) {
nuxtApp.hook('pglite:config', (options) => {
options.username = user.id
})
}
})
This, in combination with Sync
, will make us able to create an offline-first application with the ability for the users to save their data in a centralized postgres instance.
Customizing extensions
We can also use hooks to pass custom options to extensions like Sync
as well as improve typing for the whole project.
In the following example we are creating a /server/plugins/extend-pglite.ts
plugin that adds and configure pgvector
and Sync
:
import { vector } from '@electric-sql/pglite/vector'
import { electricSync } from '@electric-sql/pglite-sync'
import { pgliteHooks } from '#pglite-utils'
export default defineNitroPlugin(() => {
pgliteHooks.hook('pglite:config', (options) => {
options.extensions = {
vector,
electric: electricSync({
metadataSchema: 'my-electric',
}),
}
})
pgliteHooks.hookOnce('pglite', async (pg) => {
await pg.query('CREATE EXTENSION IF NOT EXISTS vector;')
})
})
declare module '#pglite-utils' {
interface PGliteServerExtensions {
vector: typeof vector
electric: ReturnType<typeof electricSync>
}
}
[!WARNING]
Until Nuxtv3.15
gets released this is the only way to add extensions server-side.
Hooking Notes
A few things to consider are that:
- we rely on
nuxtApp
hooks for client-side, whilepgliteHooks
imported from#pglite-utils
for server-side, hooks available are:pglite:config
: provides access toPGliteOptions
before initializing a new PGlite instance.pglite
: called on every PGlite execution.
- To improve types when manually adding extensions we use
PGliteClientExtensions
andPGliteServerExtensions
for client and server respectively.
ORM support
Any ORM that accept a PGlite instance should be supported both server and client side.
Drizzle
Drizzle integration is as simple as:
import { drizzle } from 'drizzle-orm/pglite'
import * as schema from '../my-path-to/schema'
export function useDB() {
return drizzle(usePGlite(), { schema })
}
Contribution
# Install dependencies
pnpm install
# Generate type stubs
pnpm run dev:prepare
# Develop with the playground
pnpm run dev
# Build the playground
pnpm run dev:build
# Run ESLint
pnpm run lint
# Run Vitest
pnpm run test
pnpm run test:watch
# Release new version
pnpm run release