PGlite's pluggable architecture for adding PostgreSQL extensions and custom functionality, supporting both official PostgreSQL contrib modules and custom extensions.
Core interface that all extensions must implement.
interface Extension<TNamespace = any> {
/** Unique extension name */
name: string;
/** Extension setup function */
setup: ExtensionSetup<TNamespace>;
}
/**
* Extension setup function
* @param pg - PGlite instance
* @param emscriptenOpts - Emscripten options for modification
* @param clientOnly - Whether running in client-only mode
* @returns Extension setup result
*/
type ExtensionSetup<TNamespace = any> = (
pg: PGliteInterface,
emscriptenOpts: any,
clientOnly?: boolean
) => Promise<ExtensionSetupResult<TNamespace>>;
interface ExtensionSetupResult<TNamespace = any> {
/** Modified emscripten options */
emscriptenOpts?: any;
/** Namespace object to attach to PGlite */
namespaceObj?: TNamespace;
/** Extension bundle URL */
bundlePath?: URL;
/** Post-initialization function */
init?: () => Promise<void>;
/** Cleanup function */
close?: () => Promise<void>;
}How to configure extensions when creating PGlite instances.
interface PGliteOptions<TExtensions = Record<string, Extension>> {
/** Extension map */
extensions?: TExtensions;
// ... other options
}
/**
* Create PGlite with extensions
*/
const db = await PGlite.create({
extensions: {
live,
vector,
// ... other extensions
},
});Incremental View Maintenance extension for efficient materialized view updates.
/**
* PostgreSQL Incremental View Maintenance extension
* Enables efficient updates to materialized views
*/
const pg_ivm: Extension;PostgreSQL contrib extensions available through PGlite.
// Text and string processing
const amcheck: Extension; // Table/index corruption detection
const citext: Extension; // Case-insensitive text type
const fuzzystrmatch: Extension;// Fuzzy string matching functions
const pg_trgm: Extension; // Trigram matching for similarity
// Data types
const cube: Extension; // Multidimensional cube data type
const hstore: Extension; // Key-value pair storage
const isn: Extension; // International Standard Numbers
const ltree: Extension; // Tree-like path data type
const seg: Extension; // Line segment data type
// Indexing
const bloom: Extension; // Bloom filter indexes
const btree_gin: Extension; // B-tree operator classes for GIN
const btree_gist: Extension; // B-tree operator classes for GiST
// Functions and tables
const earthdistance: Extension;// Great circle distance calculations
const tablefunc: Extension; // Functions returning tables
const uuid_ossp: Extension; // UUID generation functions
// Monitoring and logging
const auto_explain: Extension; // Automatic query plan logging
const tcn: Extension; // Triggered change notifications
// Sampling
const tsm_system_rows: Extension; // Row-count tablesample method
const tsm_system_time: Extension; // Time-based tablesample method
// Large objects
const lo: Extension; // Large object supportHow to import and use contrib extensions.
// Import pattern for contrib extensions
import { amcheck } from "@electric-sql/pglite/contrib/amcheck";
import { citext } from "@electric-sql/pglite/contrib/citext";
import { fuzzystrmatch } from "@electric-sql/pglite/contrib/fuzzystrmatch";
import { hstore } from "@electric-sql/pglite/contrib/hstore";
import { ltree } from "@electric-sql/pglite/contrib/ltree";
import { pg_trgm } from "@electric-sql/pglite/contrib/pg_trgm";
import { uuid_ossp } from "@electric-sql/pglite/contrib/uuid-ossp";
// ... and so on for other contrib extensions/** Extension type with namespace */
type ExtensionNamespace<T> = T extends Extension<infer N> ? N : never;
/** Infer extension types from options */
type PGliteWithExtensions<O extends PGliteOptions> =
O extends { extensions: infer E }
? PGliteInterface & {
[K in keyof E]: ExtensionNamespace<E[K]>
}
: PGliteInterface;Usage Examples:
import { PGlite } from "@electric-sql/pglite";
import { live } from "@electric-sql/pglite/live";
import { vector } from "@electric-sql/pglite/vector";
import { pg_ivm } from "@electric-sql/pglite/pg_ivm";
const db = await PGlite.create({
extensions: {
live,
vector,
pg_ivm,
},
});
// Extensions add namespaces to the db object
await db.live.query("SELECT * FROM users");
// db.vector is available for vector operations
// db.pg_ivm is available for incremental viewsimport { PGlite } from "@electric-sql/pglite";
import { hstore } from "@electric-sql/pglite/contrib/hstore";
import { ltree } from "@electric-sql/pglite/contrib/ltree";
import { pg_trgm } from "@electric-sql/pglite/contrib/pg_trgm";
import { uuid_ossp } from "@electric-sql/pglite/contrib/uuid-ossp";
const db = await PGlite.create({
extensions: {
hstore,
ltree,
pg_trgm,
uuid_ossp,
},
});
// Use hstore data type
await db.exec(`
CREATE TABLE products (
id SERIAL PRIMARY KEY,
attributes HSTORE
);
`);
await db.query(
"INSERT INTO products (attributes) VALUES ($1)",
['size => "L", color => "blue"']
);
// Use ltree for hierarchical data
await db.exec(`
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
path LTREE
);
`);
await db.query(
"INSERT INTO categories (path) VALUES ($1)",
["electronics.computers.laptops"]
);
// Use trigram similarity
await db.exec("CREATE EXTENSION IF NOT EXISTS pg_trgm;");
const similar = await db.query(`
SELECT name, similarity(name, $1) as sim
FROM products
WHERE similarity(name, $1) > 0.3
ORDER BY sim DESC
`, ["laptop"]);
// Generate UUIDs
const uuid = await db.query("SELECT uuid_generate_v4()");import { PGlite, Extension } from "@electric-sql/pglite";
// Define custom extension
const myExtension: Extension<{ customFunction: () => string }> = {
name: "my-extension",
setup: async (pg, emscriptenOpts, clientOnly) => {
// Extension setup logic
const customFunction = () => {
return "Hello from custom extension!";
};
return {
namespaceObj: {
customFunction,
},
};
},
};
// Use custom extension
const db = await PGlite.create({
extensions: {
myExtension,
},
});
// Custom namespace is available
console.log(db.myExtension.customFunction()); // "Hello from custom extension!"const complexExtension: Extension<{ helper: (input: string) => Promise<string> }> = {
name: "complex-extension",
setup: async (pg, emscriptenOpts, clientOnly) => {
let initialized = false;
const helper = async (input: string) => {
if (!initialized) {
throw new Error("Extension not initialized");
}
return `processed: ${input}`;
};
return {
namespaceObj: { helper },
init: async () => {
// Perform async initialization
await pg.exec("CREATE TABLE IF NOT EXISTS extension_data (id SERIAL, data TEXT)");
initialized = true;
},
close: async () => {
// Cleanup when database closes
initialized = false;
},
};
},
};
const db = await PGlite.create({
extensions: {
complexExtension,
},
});
// Extension is automatically initialized
const result = await db.complexExtension.helper("test data");
console.log(result); // "processed: test data"