Getting Started
Getting Started with spanner-orm
This guide will walk you through installing spanner-orm
and setting up a basic project.
Installation
You can install spanner-orm
using your preferred package manager:
Using NPM:
npm install spanner-orm
Or using Bun:
bun add spanner-orm
Peer Dependencies
spanner-orm
relies on peer dependencies for the specific database clients. You’ll need to install the ones corresponding to the databases you intend to use:
- For PostgreSQL:
pg
- For Google Cloud Spanner:
@google-cloud/spanner
- For PGLite (optional, for local/embedded use):
@electric-sql/pglite
You can install them like so:
Using NPM:
# Install all, or pick the ones you need:
npm install pg @google-cloud/spanner @electric-sql/pglite
Or using Bun:
# Install all, or pick the ones you need:
bun add pg @google-cloud/spanner @electric-sql/pglite
Your First Project: A “Hello World” Example
Let’s create a simple example to define a schema, connect to a database (we’ll use PGLite for simplicity here), and perform a basic operation.
1. Define Your Schema
Create a file named schema.ts
(e.g., in src/schema.ts
):
// src/schema.ts
import { table, uuid, text, timestamp, sql } from "spanner-orm";
// Common timestamp fields
export const timestamps = {
createdAt: timestamp("created_at", { withTimezone: true })
.default(sql`CURRENT_TIMESTAMP`) // Use backticks for sql template literal
.notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true })
.default(sql`CURRENT_TIMESTAMP`) // Use backticks for sql template literal
.notNull(),
};
// Define a simple 'notes' table
export const notes = table("notes", {
id: uuid("id").primaryKey(), // Automatically uses $defaultFn(() => crypto.randomUUID())
content: text("content").notNull(),
...timestamps,
});
2. Initialize the Client and Interact with the Database
Create a file, for example index.ts
:
// src/index.ts
import { OrmClient, PgliteAdapter } from "spanner-orm";
import { PGlite } from "@electric-sql/pglite";
import { notes } from "./schema"; // Assuming your schema.ts is in the same directory or adjust path
async function main() {
// Initialize PGLite (in-memory for this example)
const pglite = new PGlite();
const adapter = new PgliteAdapter(pglite); // Use PgliteAdapter
// Connect the adapter (important for PgliteAdapter)
// For PgliteAdapter, connect() also runs initial setup like 'CREATE EXTENSION IF NOT EXISTS vector'.
await adapter.connect();
const db = new OrmClient(adapter, "postgres"); // 'postgres' is the dialect for PGLite
console.log("Successfully connected to PGLite!");
// For PGLite, we might need to manually create the table if not using migrations
// This is a simplified way for a quick start.
// NOTE: In a typical workflow, you would generate and run migrations.
try {
const createTableSQL = `
CREATE TABLE IF NOT EXISTS notes (
id TEXT PRIMARY KEY,
content TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL
);
`;
await db.raw(createTableSQL); // Use db.raw for DDL if db.rawExecute is not available
console.log("'notes' table checked/created.");
} catch (error) {
console.error("Error creating table (it might already exist):", error);
}
// Insert a new note
try {
const insertResult = await db
.insert(notes)
.values({ content: "Hello, spanner-orm!" });
console.log("Insert Result:", insertResult);
// Retrieve the note
const allNotes = await db.select().from(notes);
console.log("All Notes:", allNotes);
if (allNotes.length > 0) {
console.log("First note content:", allNotes[0].content);
}
} catch (error) {
console.error("Error during database operation:", error);
} finally {
// Close the adapter connection if necessary
await adapter.disconnect();
}
}
main().catch(console.error);
3. Run Your Example
- Ensure you have
bun
orts-node
(or similar) to execute TypeScript files. - Make sure your
tsconfig.json
is set up appropriately (e.g.,moduleResolution: "node"
or"bundler"
,target: "esnext"
).
Using Bun:
bun run src/index.ts
This simple example demonstrates the basic workflow: define a schema, initialize a client with an adapter, and perform database operations.
For more complex scenarios, you’ll typically use the Migration tools to manage your schema and the Querying Data guide for more advanced database interactions.