skills/bun-guides-ecosystem-neon-drizzle/SKILL.md
Use Neon Postgres through Drizzle ORM
npx skillsauth add jarle/bun-skills Bun Use Neon Postgres through Drizzle ORMInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Neon is a fully managed serverless Postgres, separating compute and storage to offer features like autoscaling, branching and bottomless storage. Neon can be used from Bun directly using the @neondatabase/serverless driver or through an ORM like Drizzle.
Drizzle ORM supports both a SQL-like "query builder" API and an ORM-like Queries API. Get started by creating a project directory, initializing the directory using bun init, and installing Drizzle and the Neon serverless driver.
mkdir bun-drizzle-neon
cd bun-drizzle-neon
bun init -y
bun add drizzle-orm @neondatabase/serverless
bun add -D drizzle-kit
Create a .env.local file and add your Neon Postgres connection string to it.
DATABASE_URL=postgresql://usertitle:[email protected]/neondb?sslmode=require
We will connect to the Neon database using the Neon serverless driver, wrapped in a Drizzle database instance.
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
// Bun automatically loads the DATABASE_URL from .env.local
// Refer to: https://bun.com/docs/runtime/environment-variables for more information
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql);
To see the database in action, add these lines to index.ts.
import { db } from "./db";
import { sql } from "drizzle-orm";
const query = sql`select 'hello world' as text`;
const result = await db.execute(query);
console.log(result.rows);
Then run index.ts with Bun.
bun run index.ts
[
{
text: "hello world",
}
]
We can define a schema for our database using Drizzle ORM primitives. Create a schema.ts file and add this code.
import { pgTable, integer, serial, text, timestamp } from "drizzle-orm/pg-core";
export const authors = pgTable("authors", {
id: serial("id").primaryKey(),
title: text("name").notNull(),
bio: text("bio"),
createdAt: timestamp("created_at").notNull().defaultNow(),
});
We then use the drizzle-kit CLI to generate an initial SQL migration.
bunx drizzle-kit generate --dialect postgresql --schema ./schema.ts --out ./drizzle
This creates a new drizzle directory containing a .sql migration file and meta directory.
drizzle
├── 0000_aspiring_post.sql
└── meta
├── 0000_snapshot.json
└── _journal.json
We can execute these migrations with a simple migrate.ts script. This script creates a new connection to the Neon database and executes all unexecuted migrations in the drizzle directory.
import { db } from "./db";
import { migrate } from "drizzle-orm/neon-http/migrator";
const main = async () => {
try {
await migrate(db, { migrationsFolder: "drizzle" });
console.log("Migration completed");
} catch (error) {
console.error("Error during migration:", error);
process.exit(1);
}
};
main();
We can run this script with bun to execute the migration.
bun run migrate.ts
Migration completed
We can now add some data to our database. Create a seed.ts file with the following contents.
import { db } from "./db";
import * as schema from "./schema";
async function seed() {
await db.insert(schema.authors).values([
{
title: "J.R.R. Tolkien",
bio: "The creator of Middle-earth and author of The Lord of the Rings.",
},
{
title: "George R.R. Martin",
bio: "The author of the epic fantasy series A Song of Ice and Fire.",
},
{
title: "J.K. Rowling",
bio: "The creator of the Harry Potter series.",
},
]);
}
async function main() {
try {
await seed();
console.log("Seeding completed");
} catch (error) {
console.error("Error during seeding:", error);
process.exit(1);
}
}
main();
Then run this file.
bun run seed.ts
Seeding completed
We now have a database with a schema and sample data. We can use Drizzle to query it. Replace the contents of index.ts with the following.
import * as schema from "./schema";
import { db } from "./db";
const result = await db.select().from(schema.authors);
console.log(result);
Then run the file. You should see the three authors we inserted.
bun run index.ts
[
{
id: 1,
title: "J.R.R. Tolkien",
bio: "The creator of Middle-earth and author of The Lord of the Rings.",
createdAt: 2024-05-11T10:28:46.029Z,
}, {
id: 2,
title: "George R.R. Martin",
bio: "The author of the epic fantasy series A Song of Ice and Fire.",
createdAt: 2024-05-11T10:28:46.029Z,
}, {
id: 3,
title: "J.K. Rowling",
bio: "The creator of the Harry Potter series.",
createdAt: 2024-05-11T10:28:46.029Z,
}
]
This example used the Neon serverless driver's SQL-over-HTTP functionality. Neon's serverless driver also exposes Client and Pool constructors to enable sessions, interactive transactions, and node-postgres compatibility. Refer to Neon's documentation for a complete overview.
Refer to the Drizzle website for more documentation on using the Drizzle ORM.
development
Using TypeScript with Bun, including type definitions and compiler options
development
Learn how to write tests using Bun's Jest-compatible API with support for async tests, timeouts, and various test modifiers
testing
Learn how to use snapshot testing in Bun to save and compare output between test runs
testing
Learn about Bun test's runtime integration, environment variables, timeouts, and error handling