# Nuxt PGlite [![npm version][npm-version-src]][npm-version-href] [![npm downloads][npm-downloads-src]][npm-downloads-href] [![License][license-src]][license-href] [![Nuxt][nuxt-src]][nuxt-href] A Nuxt module aimed to simplify the use of [PGlite](https://pglite.dev). > PGlite, an Embeddable Postgres > Run a full Postgres database locally in WASM with reactivity and live sync. - [✨ Release Notes](/CHANGELOG.md) <!-- - [🏀 Online playground](https://stackblitz.com/github/sandros94/nuxt-pglite?file=playground%2Fapp.vue) --> <!-- - [📖 Documentation](https://example.com) --> > [!WARNING] > No docs are available (although planned), please refer to the [playground code](/playground). ## Features <!-- Highlight some of the features your module provide here --> - ⚡️ Server-side `usePGlite`, running in your Node or Bun server. - 🧑💻 Client-side `usePGlite`, running inside Web Workers. - 🪢 Client-side `useLiveQuery` and `useLiveIncrementalQuery` to subscribe to live changes. ## Quick Setup Install the module to your Nuxt application with one command: ```bash 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: ```ts 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](https://pglite.dev/docs/filesystems). ### Extensions Extensions are automatically configured with full type support and can be added via `nuxt.config.ts`: ```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](https://pglite.dev/extensions). If a new extension is missing feel free to open up a new PR by adding it to [this file](/src/templates.ts#L62-L87). I do plan to support only official and contrib extensions. > [!WARNING] > Auto configuration for server-side extensions will be supported once Nuxt `v3.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`: ```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: ```vue <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](https://pglite.dev/docs/framework-hooks/vue#uselivequery). ## Hooks We can use hooks to customize or extend PGlite at runtime. This becomes particularly useful in conjunction with [`RLS`](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) 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`: ```ts export default defineNuxtPlugin((nuxtApp) => { const { user } = useUserSession() if (user) { nuxtApp.hook('pglite:config', (options) => { options.username = user.id }) } }) ``` This, in combination with [`Sync`](https://pglite.dev/docs/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`](https://pglite.dev/docs/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`: ```ts 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 Nuxt `v3.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, while `pgliteHooks` imported from `#pglite-utils` for server-side, hooks available are: - `pglite:config`: provides access to `PGliteOptions` before initializing a new PGlite instance. - `pglite`: called on every PGlite execution. - To improve types when manually adding extensions we use `PGliteClientExtensions` and `PGliteServerExtensions` 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: ```ts import { drizzle } from 'drizzle-orm/pglite' import * as schema from '../my-path-to/schema' export function useDB() { return drizzle(usePGlite(), { schema }) } ``` ## Contribution <details> <summary>Local development</summary> ```bash # 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 ``` </details> ## License Published under the [MIT](/LICENSE) license. <!-- Badges --> [npm-version-src]: https://img.shields.io/npm/v/nuxt-pglite/latest.svg?style=flat&colorA=020420&colorB=00DC82 [npm-version-href]: https://npmjs.com/package/nuxt-pglite [npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-pglite.svg?style=flat&colorA=020420&colorB=00DC82 [npm-downloads-href]: https://npmjs.com/package/nuxt-pglite [license-src]: https://img.shields.io/npm/l/nuxt-pglite.svg?style=flat&colorA=020420&colorB=00DC82 [license-href]: https://npmjs.com/package/nuxt-pglite [nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js [nuxt-href]: https://nuxt.com