AutoGen Tutorial (TypeScript): connecting to PostgreSQL for intermediate developers

By Cyprian AaronsUpdated 2026-04-21
autogenconnecting-to-postgresql-for-intermediate-developerstypescript

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-node or a build step
  • Packages:
    • @autogenai/autogen
    • pg
    • dotenv
    • typescript
    • ts-node
    • @types/pg

Step-by-Step

  1. 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
  1. 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.');
  1. 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;
}
  1. 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,
  };
}
  1. 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);
  1. 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

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

Related Guides