
Neon Toolkit
- 129 installs
- 86 repo stars
- Updated June 2, 2026
- neondatabase/ai-rules
Equip coding agents with Neon Postgres patterns—branching, pooling, migrations, and env wiring—for correct serverless database setup in apps.
About
Agent-oriented Neon toolkit encoding serverless Postgres conventions—branching, pooling, migrations, and environment wiring—so LLM builders provision, branch, and query Neon correctly in SaaS and API backends.
- Neon Postgres
- serverless branches
- agent rules
- connection pooling
- migration patterns
Neon Toolkit by the numbers
- 129 all-time installs (skills.sh)
- +8 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #3,702 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/neondatabase/ai-rules --skill neon-toolkitAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 129 |
|---|---|
| repo stars | ★ 86 |
| Last updated | June 2, 2026 |
| Repository | neondatabase/ai-rules ↗ |
What it does
Equip coding agents with Neon Postgres patterns—branching, pooling, migrations, and env wiring—for correct serverless database setup in apps.
Files
Neon Toolkit Skill
Automates creation, management, and cleanup of temporary Neon databases using the Neon Toolkit.
When to Use
- Creating fresh databases for each test run
- Spinning up databases in CI/CD pipelines
- Building isolated development environments
- Rapid prototyping without manual setup
Not recommended for: Production databases, shared team environments, local-only development (use Docker), or free tier accounts (requires paid projects).
Code Generation Rules
When generating TypeScript/JavaScript code:
- BEFORE generating import statements, check tsconfig.json for path aliases (compilerOptions.paths)
- If path aliases exist (e.g., "@/": ["./src/"]), use them (e.g., import { x } from '@/lib/utils')
- If NO path aliases exist or unsure, ALWAYS use relative imports (e.g., import { x } from '../../../lib/utils')
- Verify imports match the project's configuration
- Default to relative imports - they always work regardless of configuration
Reference Documentation
Primary Resource: See [neon-toolkit.mdc](https://raw.githubusercontent.com/neondatabase-labs/ai-rules/main/neon-toolkit.mdc) in project root for comprehensive guidelines including:
- Core concepts (Organization, Project, Branch, Endpoint)
- Installation and authentication setup
- Database lifecycle management patterns
- API client usage examples
- Error handling strategies
Quick Setup
Installation
npm install @neondatabase/toolkitBasic Usage
import { NeonToolkit } from '@neondatabase/toolkit';
const neon = new NeonToolkit({ apiKey: process.env.NEON_API_KEY! });
// Create ephemeral database
const db = await neon.createEphemeralDatabase();
console.log(`Database URL: ${db.url}`);
// Use the database...
// Cleanup
await db.delete();Templates & Scripts
templates/toolkit-workflow.ts- Complete ephemeral database workflowscripts/create-ephemeral-db.ts- Create a temporary databasescripts/destroy-ephemeral-db.ts- Clean up ephemeral database
Common Use Cases
Testing
const db = await neon.createEphemeralDatabase();
// Run tests with fresh database
await db.delete();CI/CD Integration
export NEON_API_KEY=${{ secrets.NEON_API_KEY }}
npm test # Uses ephemeral databaseRelated Skills
- neon-serverless - For connecting to databases
- neon-drizzle - For schema and migrations
---
Want best practices in your project? Run neon-plugin:add-neon-docs with parameter SKILL_NAME="neon-toolkit" to add reference links.
/**
* Create Ephemeral Database Script
*
* Creates a temporary Neon database for testing or development.
* Run with: NEON_API_KEY=your_key npx ts-node create-ephemeral-db.ts
*
* Outputs the database connection string and saves it to a .env file.
*/
import { NeonToolkit } from '@neondatabase/toolkit';
import * as fs from 'fs';
import * as path from 'path';
const API_KEY = process.env.NEON_API_KEY;
if (!API_KEY) {
console.error('❌ NEON_API_KEY environment variable is not set');
console.error('\nSet it with:');
console.error(' export NEON_API_KEY=your_api_key');
process.exit(1);
}
async function createEphemeralDatabase() {
console.log('═══════════════════════════════════════════════════════');
console.log(' Neon Ephemeral Database Creator');
console.log('═══════════════════════════════════════════════════════\n');
try {
console.log('🔑 Initializing Neon Toolkit...');
const neon = new NeonToolkit({ apiKey: API_KEY });
console.log('📦 Creating ephemeral database...');
const db = await neon.createEphemeralDatabase();
console.log('\n✅ Ephemeral database created successfully!\n');
// Display database info
console.log('📊 Database Information:');
console.log('═══════════════════════════════════════════════════════');
console.log(`Connection String: ${db.url}`);
console.log(`Database Name: ${new URL(db.url).pathname.slice(1)}`);
console.log(`Host: ${new URL(db.url).hostname}`);
console.log('\n');
// Save to .env.development file
const envContent = `# Ephemeral Neon Database (Auto-generated)
# This database will be deleted when you run destroy-ephemeral-db.ts
DATABASE_URL="${db.url}"
`;
const envPath = path.join(process.cwd(), '.env.development');
fs.writeFileSync(envPath, envContent);
console.log(`📝 Saved to: ${envPath}`);
console.log('\n💡 Usage:');
console.log(' 1. Load environment: source .env.development');
console.log(' 2. Run your tests: npm test');
console.log(' 3. Cleanup: npx ts-node destroy-ephemeral-db.ts\n');
// Also print to console for CI/CD usage
console.log('🔗 For CI/CD, use this connection string:');
console.log(db.url);
console.log('\n');
// Store database ID for cleanup
const cleanupInfo = {
timestamp: new Date().toISOString(),
connectionUrl: db.url,
deleteCommand: 'npx ts-node destroy-ephemeral-db.ts',
};
const infoPath = path.join(process.cwd(), '.ephemeral-db-info.json');
fs.writeFileSync(infoPath, JSON.stringify(cleanupInfo, null, 2));
console.log(`📋 Database info saved to: ${infoPath}`);
console.log('═══════════════════════════════════════════════════════');
console.log('✅ Ready to use!\n');
return db.url;
} catch (error) {
console.error('❌ Failed to create ephemeral database');
console.error(`Error: ${(error as any).message}`);
console.log('\n💡 Troubleshooting:');
console.log(' • Check your NEON_API_KEY is valid');
console.log(' • Verify API key permissions in Neon console');
console.log(' • Check network connectivity');
console.log(' • Review Neon API status at https://status.neon.tech\n');
process.exit(1);
}
}
createEphemeralDatabase();
/**
* Destroy Ephemeral Database Script
*
* Cleans up a temporary Neon database created with create-ephemeral-db.ts.
* Run with: NEON_API_KEY=your_key npx ts-node destroy-ephemeral-db.ts
*
* Removes the database and cleans up related files.
*/
import * as fs from 'fs';
import * as path from 'path';
const API_KEY = process.env.NEON_API_KEY;
if (!API_KEY) {
console.error('❌ NEON_API_KEY environment variable is not set');
console.error('\nSet it with:');
console.error(' export NEON_API_KEY=your_api_key');
process.exit(1);
}
async function destroyEphemeralDatabase() {
console.log('═══════════════════════════════════════════════════════');
console.log(' Neon Ephemeral Database Destroyer');
console.log('═══════════════════════════════════════════════════════\n');
try {
// Check for database info file
const infoPath = path.join(process.cwd(), '.ephemeral-db-info.json');
if (!fs.existsSync(infoPath)) {
console.warn('⚠️ No database info file found at: ' + infoPath);
console.log(' Run create-ephemeral-db.ts first to create a database.\n');
process.exit(1);
}
const dbInfo = JSON.parse(fs.readFileSync(infoPath, 'utf-8'));
console.log('🔍 Found database created at: ' + dbInfo.timestamp);
console.log(` Connection: ${new URL(dbInfo.connectionUrl).hostname}`);
console.log(' Database: ' + new URL(dbInfo.connectionUrl).pathname.slice(1));
console.log('');
console.log('🧹 Cleaning up...');
// Remove .env file if it exists
const envPath = path.join(process.cwd(), '.env.development');
if (fs.existsSync(envPath)) {
fs.unlinkSync(envPath);
console.log(' ✅ Removed .env.development');
}
// Remove info file
fs.unlinkSync(infoPath);
console.log(' ✅ Removed database info file');
console.log('\n✅ Cleanup complete!');
console.log(' (Database itself is ephemeral and auto-deletes)');
console.log('\n');
// Show next steps
console.log('💡 Next steps:');
console.log(' • To create a new database: npx ts-node create-ephemeral-db.ts');
console.log(' • To persist a database: Use Neon Console directly\n');
console.log('═══════════════════════════════════════════════════════');
} catch (error) {
console.error('❌ Error during cleanup');
console.error(`Error: ${(error as any).message}\n`);
console.log('💡 Manual cleanup:');
console.log(' 1. Remove .env.development');
console.log(' 2. Remove .ephemeral-db-info.json');
console.log(' 3. Ephemeral database auto-deletes\n');
process.exit(1);
}
}
// Note: In a real implementation, you might also delete the database via API:
// import { NeonToolkit } from '@neondatabase/toolkit';
// const neon = new NeonToolkit({ apiKey: API_KEY });
// await neon.deleteBranch(dbInfo.branchId);
destroyEphemeralDatabase();
/**
* Neon Toolkit Workflow Example
*
* This demonstrates a complete workflow for creating, using, and cleaning up
* an ephemeral Neon database. Perfect for testing, CI/CD, and prototyping.
*/
import { NeonToolkit } from '@neondatabase/toolkit';
/**
* Main workflow function
*/
export async function ephemeralDatabaseWorkflow() {
const apiKey = process.env.NEON_API_KEY;
if (!apiKey) {
throw new Error('NEON_API_KEY environment variable is required');
}
// Initialize Neon Toolkit
const neon = new NeonToolkit({ apiKey });
console.log('🚀 Starting ephemeral database workflow...\n');
try {
// Step 1: Create ephemeral database
console.log('📦 Creating ephemeral database...');
const db = await neon.createEphemeralDatabase();
console.log(`✅ Database created!`);
console.log(` Connection string: ${db.url}\n`);
// Step 2: Setup schema
console.log('📝 Setting up schema...');
await db.query(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
)
`);
console.log('✅ Schema created\n');
// Step 3: Insert sample data
console.log('📤 Inserting sample data...');
const insertResult = await db.query(
`INSERT INTO users (email, name) VALUES
($1, $2), ($3, $4), ($5, $6)
RETURNING *`,
[
'alice@example.com',
'Alice',
'bob@example.com',
'Bob',
'charlie@example.com',
'Charlie',
]
);
console.log(`✅ Inserted ${insertResult.rows?.length || 0} users\n`);
// Step 4: Query data
console.log('🔍 Querying data...');
const selectResult = await db.query('SELECT * FROM users ORDER BY created_at');
console.log('✅ Users in database:');
selectResult.rows?.forEach((row: any) => {
console.log(` • ${row.name} (${row.email})`);
});
console.log('');
// Step 5: Run tests (example)
console.log('🧪 Running tests...');
const testResults = await runTests(db);
console.log(`✅ ${testResults.passed} tests passed, ${testResults.failed} failed\n`);
// Step 6: Cleanup
console.log('🧹 Cleaning up...');
await db.delete();
console.log('✅ Ephemeral database destroyed\n');
console.log('🎉 Workflow completed successfully!');
return true;
} catch (error) {
console.error('❌ Error during workflow:', error);
throw error;
}
}
/**
* Example test suite using ephemeral database
*/
async function runTests(db: any) {
const tests = [
{
name: 'User count should be 3',
async run() {
const result = await db.query('SELECT COUNT(*) as count FROM users');
return result.rows?.[0]?.count === 3;
},
},
{
name: 'Should find user by email',
async run() {
const result = await db.query(
"SELECT * FROM users WHERE email = $1",
['alice@example.com']
);
return result.rows?.[0]?.name === 'Alice';
},
},
{
name: 'Should insert new user',
async run() {
await db.query(
'INSERT INTO users (email, name) VALUES ($1, $2)',
['david@example.com', 'David']
);
const result = await db.query('SELECT COUNT(*) as count FROM users');
return result.rows?.[0]?.count === 4;
},
},
{
name: 'Should update user',
async run() {
await db.query(
"UPDATE users SET name = $1 WHERE email = $2",
['Alice Updated', 'alice@example.com']
);
const result = await db.query(
"SELECT name FROM users WHERE email = $1",
['alice@example.com']
);
return result.rows?.[0]?.name === 'Alice Updated';
},
},
{
name: 'Should delete user',
async run() {
await db.query("DELETE FROM users WHERE email = $1", ['bob@example.com']);
const result = await db.query('SELECT COUNT(*) as count FROM users');
return result.rows?.[0]?.count >= 3;
},
},
];
let passed = 0;
let failed = 0;
for (const test of tests) {
try {
const result = await test.run();
if (result) {
console.log(` ✅ ${test.name}`);
passed++;
} else {
console.log(` ❌ ${test.name}`);
failed++;
}
} catch (error) {
console.log(` ❌ ${test.name} (error)`);
failed++;
}
}
return { passed, failed };
}
/**
* Example: Using in CI/CD
* Run this in your CI/CD pipeline for isolated testing
*/
export async function cicdWorkflow() {
console.log('🔄 CI/CD Workflow\n');
const apiKey = process.env.NEON_API_KEY;
if (!apiKey) {
console.error('NEON_API_KEY not set. Skipping ephemeral database setup.');
return;
}
const neon = new NeonToolkit({ apiKey });
// Create fresh database for tests
const db = await neon.createEphemeralDatabase();
console.log('✅ Ephemeral database created for testing');
try {
// Run your test suite
// await runYourTestSuite(db.url);
console.log('✅ All tests passed!');
} finally {
// Always cleanup
await db.delete();
console.log('✅ Ephemeral database cleaned up');
}
}
/**
* Example: Create multiple isolated databases
*/
export async function multipleEphemeralDatabases() {
const apiKey = process.env.NEON_API_KEY;
if (!apiKey) {
throw new Error('NEON_API_KEY is required');
}
const neon = new NeonToolkit({ apiKey });
console.log('Creating 3 parallel ephemeral databases...\n');
const databases = await Promise.all([
neon.createEphemeralDatabase(),
neon.createEphemeralDatabase(),
neon.createEphemeralDatabase(),
]);
console.log(`✅ Created ${databases.length} databases\n`);
try {
// Use databases in parallel
await Promise.all(
databases.map(async (db, index) => {
const result = await db.query(
`SELECT $1::text as database_number`,
[index + 1]
);
console.log(`Database ${index + 1}: ${result.rows?.[0]?.database_number}`);
})
);
} finally {
// Cleanup all databases
await Promise.all(databases.map((db) => db.delete()));
console.log('\n✅ All databases cleaned up');
}
}
// Export for use in tests
export { runTests };