website/src/content/docs/actors/postgres.mdx
Actors can be used with common SQL databases, such as PostgreSQL and MySQL.
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
There are several options for places to host your SQL database:
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();
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();