AutoGen Tutorial (TypeScript): connecting to PostgreSQL for intermediate developers
This tutorial shows how to wire an AutoGen TypeScript agent to PostgreSQL so the agent can read and write structured data during a conversation. You need this when your agent has to persist state, query customer records, or fetch operational data instead of relying only on chat history.
What You'll Need
- •Node.js 18+
- •A PostgreSQL database you can connect to
- •An OpenAI API key set as
OPENAI_API_KEY - •A TypeScript project with
ts-nodeor a build step - •Packages:
- •
@autogenai/autogen - •
pg - •
dotenv - •
typescript - •
ts-node - •
@types/pg
- •
Step-by-Step
- •Install the dependencies and initialize your project.
This example assumes you already have a TypeScript app; if not, create one first and keep the runtime simple.
npm init -y
npm install @autogenai/autogen pg dotenv
npm install -D typescript ts-node @types/node @types/pg
npx tsc --init
- •Create a PostgreSQL table for the data your agent will use.
Keep the schema small and explicit. For agent workflows, a narrow table beats a generic blob store almost every time.
CREATE TABLE IF NOT EXISTS customer_notes (
id SERIAL PRIMARY KEY,
customer_email TEXT NOT NULL,
note TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
INSERT INTO customer_notes (customer_email, note)
VALUES
('alice@example.com', 'Customer asked about invoice timing.'),
('bob@example.com', 'Requested callback after 3 PM.');
- •Add your environment variables and database client.
The agent should never talk to PostgreSQL directly without a thin service layer in front of it. That keeps connection handling, SQL, and validation in one place.
OPENAI_API_KEY=your_openai_key_here
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/autogen_demo
// db.ts
import "dotenv/config";
import { Pool } from "pg";
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
export async function getCustomerNotes(email: string) {
const result = await pool.query(
"SELECT id, customer_email, note, created_at FROM customer_notes WHERE customer_email = $1 ORDER BY created_at DESC",
[email]
);
return result.rows;
}
- •Expose the database query as an AutoGen tool.
In AutoGen, tools are just typed functions the model can call. The important part is keeping the tool narrow: one function, one job, one SQL query path.
// tools.ts
import { getCustomerNotes } from "./db";
export async function lookupCustomerNotes(args: { email: string }) {
const rows = await getCustomerNotes(args.email);
return {
email: args.email,
count: rows.length,
notes: rows,
};
}
- •Create an AutoGen agent that can call the tool and answer from PostgreSQL data.
This example uses a single assistant with tool access and a simple prompt that tells it when to query the database.
// index.ts
import "dotenv/config";
import { AssistantAgent } from "@autogenai/autogen";
import { lookupCustomerNotes } from "./tools";
async function main() {
const agent = new AssistantAgent({
name: "support_agent",
modelClient: {
apiKey: process.env.OPENAI_API_KEY!,
model: "gpt-4o-mini",
},
systemMessage:
"You are a support assistant. When asked about customer history, use the lookupCustomerNotes tool.",
tools: [
{
name: "lookupCustomerNotes",
description: "Fetch notes for a customer by email address.",
parameters: {
type: "object",
properties: {
email: { type: "string" },
},
required: ["email"],
additionalProperties: false,
},
execute: lookupCustomerNotes,
},
],
});
const response = await agent.run({
messages: [
{
role: "user",
content: "What do we know about alice@example.com?",
},
],
});
console.log(response.messages.at(-1)?.content);
}
main().catch(console.error);
- •Run it and keep the connection lifecycle clean.
In production, make sure you close the pool on shutdown and avoid creating a new database client per request.
// shutdown.ts
import { pool } from "./db";
process.on("SIGINT", async () => {
await pool.end();
process.exit(0);
});
process.on("SIGTERM", async () => {
await pool.end();
process.exit(0);
});
Testing It
Run your Postgres container or local server, apply the table setup, then start the TypeScript script with npx ts-node index.ts. If everything is wired correctly, the agent should call lookupCustomerNotes and return the matching rows for alice@example.com.
If you get no tool calls, check that your system message explicitly tells the agent when to use PostgreSQL-backed lookup logic. If you get connection errors, verify DATABASE_URL, network access, and whether your Postgres instance requires SSL.
A good sanity test is to change the email in the user prompt to one that does not exist. The response should still be valid JSON-shaped tool output with count: 0, not an exception.
Next Steps
- •Add write tools for inserting notes or updating case status.
- •Wrap database access in transactions for multi-step workflows.
- •Add row-level authorization so agents only query records they are allowed to see
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit