Database Access: AI Agent with Neon Postgres
intermediatewrapProvider·openaineon·3 min read
Database Access: AI Agent with Neon Postgres
Keystore doesn't just manage LLM keys — it handles database credentials too. wrapNeon() routes your Neon Postgres connection through the vault, so your agent never sees the connection string.
What you'll build
An agent that queries a Neon Postgres database, summarizes the results with GPT-4o, and writes a summary back to the database. The agent holds only a ks_ token — never a database password or OpenAI key.
Prerequisites
- A Keystore account with an agent token
- Neon Postgres and OpenAI keys in your vault
- Node.js 18+
Setup
1
Install dependencies
bash
1
npm install @keystore/sdk openai @neondatabase/serverless2
Initialize Keystore with both providers
typescript
1
2
3
4
5
6
7
8
9
10
11
12
import Keystore from "@keystore/sdk";
import { wrapNeon } from "@keystore/sdk/wrap/neon";
import OpenAI from "openai";
import { neon } from "@neondatabase/serverless";
const ks = new Keystore({ agentToken: process.env.KS_TOKEN! });
// Route all fetch-based providers (OpenAI) through the vault
ks.interceptAll();
// Wrap the Neon client separately — it needs a connection string
const sql = wrapNeon(neon, ks);i
Neon uses a connection string rather than HTTP headers, so it needs the dedicated wrapNeon() wrapper instead of interceptAll().
3
Query the database
typescript
1
2
3
4
5
6
7
8
9
10
// Query users — the real DATABASE_URL is resolved from the vault
const users = await sql`
SELECT id, name, email, signup_date
FROM users
WHERE active = true
ORDER BY signup_date DESC
LIMIT 50
`;
console.log(`Found ${users.length} active users`);4
Summarize with GPT-4o
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const openai = new OpenAI();
const summary = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: "You are a data analyst. Summarize user activity concisely.",
},
{
role: "user",
content: `Summarize these active users:\n${JSON.stringify(users, null, 2)}`,
},
],
});
const summaryText = summary.choices[0].message.content!;
console.log("Summary:", summaryText);5
Write results back
typescript
1
2
3
4
5
6
7
8
9
10
// Store the AI-generated summary back in the database
await sql`
INSERT INTO reports (type, content, generated_at)
VALUES ('user-summary', ${summaryText}, NOW())
`;
console.log("Report saved to database");
// Clean up
ks.restore();Full example
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import Keystore from "@keystore/sdk";
import { wrapNeon } from "@keystore/sdk/wrap/neon";
import OpenAI from "openai";
import { neon } from "@neondatabase/serverless";
async function main() {
const ks = new Keystore({ agentToken: process.env.KS_TOKEN! });
ks.interceptAll();
const sql = wrapNeon(neon, ks);
// Read
const users = await sql`SELECT id, name, email FROM users WHERE active = true LIMIT 50`;
// Reason
const openai = new OpenAI();
const summary = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: "Summarize this user data concisely." },
{ role: "user", content: JSON.stringify(users) },
],
});
// Write
const text = summary.choices[0].message.content!;
await sql`INSERT INTO reports (type, content, generated_at) VALUES ('user-summary', ${text}, NOW())`;
console.log("Done:", text);
ks.restore();
}
main();Security model
With this setup, your agent code contains:
- A
ks_token — scoped to specific providers and budgets - No database password — resolved from the vault at query time
- No OpenAI key — resolved from the vault at request time
If the agent token leaks, you revoke it in the dashboard. No database passwords or API keys are compromised.
Next steps
- Add email notifications with Resend
- Try S3 file uploads for report storage
- Set up production controls with rate limits