Back to Rivet

External SQL Database

website/src/content/docs/actors/postgres.mdx

2.2.16.1 KB
Original Source

Actors can be used with common SQL databases, such as PostgreSQL and MySQL.

Libraries

To facilitate interaction with SQL databases, you can use either ORM libraries or raw SQL drivers. Each has its own use cases and benefits:

  • ORM Libraries: Type-safe and easy way to interact with your database

  • Raw SQL Drivers: Direct access to the database for more flexibility

Hosting Providers

There are several options for places to host your SQL database:

Examples

Basic PostgreSQL Connection

Here's a basic example of a user actor that creates a database record on start and tracks request counts:

<CodeGroup> ```typescript index.ts @nocheck import { actor, setup } from "rivetkit"; import { Pool } from "pg";

interface ActorInput { username: string; email: string; }

// Create a database connection pool const pool = new Pool({ user: "your_db_user", host: "localhost", database: "your_db_name", password: "your_db_password", port: 5432, });

// Create the user actor export const userActor = actor({ createState: (c, input: ActorInput) => ({ requestCount: 0, username: input.username, email: input.email, lastActive: Date.now() }),

// Insert user into database when actor creates onCreate: async (c) => { await pool.query( "INSERT INTO users (username, email, created_at) VALUES ($1, $2, $3)", [c.state.username, c.state.email, c.state.lastActive] ); },

// Sync state changes to database onStateChange: async (c, newState) => { await pool.query( "UPDATE users SET email = $1, last_active = $2 WHERE username = $3", [newState.email, newState.lastActive, newState.username] ); },

actions: { // Update user information, this will trigger onStateChange updateUser: async (c, email: string) => { c.state.requestCount++; c.state.email = email; c.state.lastActive = Date.now();

  return { requestCount: c.state.requestCount };
},

// Get user data
getUser: async (c) => {
  c.state.requestCount++;
  c.state.lastActive = Date.now();

  return {
    username: c.key[0],
    email: c.state.email,
    requestCount: c.state.requestCount,
    lastActive: c.state.lastActive
  };
}

} });

export const registry = setup({ use: { userActor }, }); registry.start();


```typescript client.ts @nocheck
import { createClient } from "rivetkit/client";
import type { registry } from "./index";

const client = createClient<typeof registry>("http://localhost:6420");

// Create user
const alice = await client.userActor.create("alice", {
  input: {
    username: "alice",
    email: "[email protected]"
  }
});

alice.updateUser("[email protected]");

const userData = await alice.getUser();
console.log("User data:", userData);

// Create another user
const bob = await client.userActor.create("bob", {
  input: {
    email: "[email protected]"
  }
});
const bobData = await bob.getUser();
</CodeGroup>

Using Drizzle ORM

Here's the same user actor pattern using Drizzle ORM for more type-safe database operations:

<CodeGroup> ```typescript index.ts @nocheck import { actor, setup } from "rivetkit"; import { drizzle } from "drizzle-orm/node-postgres"; import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { eq } from "drizzle-orm"; import { Pool } from "pg";

interface ActorInput { username: string; email: string; }

// Define your schema const users = pgTable("users", { username: text("username").primaryKey(), email: text("email"), createdAt: timestamp("created_at").defaultNow(), lastActive: timestamp("last_active").defaultNow() });

// Create a database connection const pool = new Pool({ connectionString: process.env.DATABASE_URL });

// Initialize Drizzle with the pool const db = drizzle(pool);

// Create the user actor export const userActor = actor({ createState: (c, input: ActorInput) => ({ requestCount: 0, username: input.username, email: input.email, lastActive: Date.now() }),

// Insert user into database when actor creates onCreate: async (c) => { await db.insert(users).values({ username: c.state.username, email: c.state.email, createdAt: new Date(c.state.lastActive) }); },

// Sync state changes to database onStateChange: async (c, newState) => { await db.update(users) .set({ email: newState.email, lastActive: new Date(newState.lastActive) }) .where(eq(users.username, newState.username)); },

actions: { // Update user information, this will trigger onStateChange updateUser: async (c, email: string) => { c.state.requestCount++; c.state.email = email; c.state.lastActive = Date.now();

  return { requestCount: c.state.requestCount };
},

// Get user data
getUser: async (c) => {
  c.state.requestCount++;
  c.state.lastActive = Date.now();

  return {
    username: c.state.username,
    email: c.state.email,
    requestCount: c.state.requestCount,
    lastActive: c.state.lastActive
  };
}

} });

export const registry = setup({ use: { userActor }, }); registry.start();


```typescript client.ts @nocheck
import { createClient } from "rivetkit/client";
import type { registry } from "./index";

const client = createClient<typeof registry>("http://localhost:6420");

// Create user
const alice = await client.userActor.create("alice", {
  input: {
    username: "alice",
    email: "[email protected]"
  }
});

alice.updateUser("[email protected]");

const userData = await alice.getUser();
console.log("User data:", userData);

// Create another user
const bob = await client.userActor.create("bob", {
  input: {
    username: "bob",
    email: "[email protected]"
  }
});
const bobData = await bob.getUser();
</CodeGroup>