xiaohongshu
xiaohongshu
do-platform-neon
Neon serverless PostgreSQL ์ ๋ฌธ๊ฐ - auto-scaling, database branching, PITR, connection pooling ์ง์. serverless ์ฑ์์ PostgreSQL ํ์์ ์ฌ์ฉ
Full skill instructions
Neon Serverless PostgreSQL ์ ๋ฌธ ์ง์: auto-scaling, scale-to-zero ์ปดํจํธ, database branching, Point-in-Time Recovery, ์ต์ ORM ํตํฉ์ ์ํ ์ ๋ฌธํ๋ ์ง์ ์ ๊ณต
Neon: /โneondatabase/โneon
ํจํค์ง ์ค์น:
npm install @neondatabase/โserverless
npm install drizzle-orm # Optional: Drizzle ORM
npm install @prisma/โclient prisma # Optional: Prisma ORM
ํ๊ฒฝ ๋ณ์ ์ค์ :
# Direct connection (migrations์ฉ)
DATABASE_URL=postgresql://user:[email protected]/โdbname?sslmode=require
# Pooled connection (serverless/โedge์ฉ)
DATABASE_URL_POOLED=postgresql://user:[email protected]/โdbname?sslmode=require
# Neon API (branching์ฉ)
NEON_API_KEY=neon_api_key_xxx
NEON_PROJECT_ID=project-xxx
๊ธฐ๋ณธ ์ฟผ๋ฆฌ ์คํ:
import { neon } from '@neondatabase/โserverless'
const sql = neon(process.env.DATABASE_URL!)
// ๊ฐ๋จํ ์ฟผ๋ฆฌ
const users = await sql`SELECT * FROM users WHERE active = true`
// ํ๋ผ๋ฏธํฐํ๋ ์ฟผ๋ฆฌ (SQL injection ๋ฐฉ์ง)
const userId = 'user-123'
const user = await sql`SELECT * FROM users WHERE id = ${userId}`
// ํธ๋์ญ์
์ง์
const result = await sql.transaction([
sql`UPDATE accounts SET balance = balance - 100 WHERE id = ${fromId}`,
sql`UPDATE accounts SET balance = balance + 100 WHERE id = ${toId}`
])
์ธ์ ์ง์์ฑ์ ์ํ WebSocket ์ฐ๊ฒฐ:
import { Pool, neonConfig } from '@neondatabase/โserverless'
import ws from 'ws'
// Node.js ํ๊ฒฝ์์ ํ์
neonConfig.webSocketConstructor = ws
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
// ์ธ์
๊ธฐ๋ฐ ์์
์ pool ์ฌ์ฉ
const client = await pool.connect()
try {
await client.query('BEGIN')
await client.query('INSERT INTO logs (message) VALUES ($1)', ['Action'])
await client.query('COMMIT')
} finally {
client.release()
}
Branch ๊ด๋ฆฌ API:
class NeonBranchManager {
private apiKey: string
private projectId: string
private baseUrl = 'https://console.neon.tech/โapi/โv2'
constructor(apiKey: string, projectId: string) {
this.apiKey = apiKey
this.projectId = projectId
}
private async request(path: string, options: RequestInit = {}) {
const response = await fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/โjson',
...options.headers
}
})
if (!response.ok) throw new Error(`Neon API error: ${response.statusText}`)
return response.json()
}
async createBranch(name: string, parentId: string = 'main') {
return this.request(`/โprojects/โ${this.projectId}/โbranches`, {
method: 'POST',
body: JSON.stringify({
branch: { name, parent_id: parentId }
})
})
}
async deleteBranch(branchId: string) {
return this.request(`/โprojects/โ${this.projectId}/โbranches/โ${branchId}`, {
method: 'DELETE'
})
}
async listBranches() {
return this.request(`/โprojects/โ${this.projectId}/โbranches`)
}
async getBranchConnectionString(branchId: string) {
const endpoints = await this.request(
`/โprojects/โ${this.projectId}/โbranches/โ${branchId}/โendpoints`
)
return endpoints.endpoints[0]?.connection_uri
}
}
Pull Request์ฉ Preview Branch:
async function createPreviewEnvironment(prNumber: number) {
const branchManager = new NeonBranchManager(
process.env.NEON_API_KEY!,
process.env.NEON_PROJECT_ID!
)
// main์์ branch ์์ฑ
const branch = await branchManager.createBranch(`pr-${prNumber}`, 'main')
// connection string ํ๋
const connectionString = await branchManager.getBranchConnectionString(branch.branch.id)
return {
branchId: branch.branch.id,
branchName: branch.branch.name,
connectionString
}
}
async function cleanupPreviewEnvironment(branchId: string) {
const branchManager = new NeonBranchManager(
process.env.NEON_API_KEY!,
process.env.NEON_PROJECT_ID!
)
await branchManager.deleteBranch(branchId)
}
ํน์ ์์ ์ผ๋ก ๋ณต์:
async function restoreToPoint(timestamp: Date) {
const response = await fetch(
`https://console.neon.tech/โapi/โv2/โprojects/โ${process.env.NEON_PROJECT_ID}/โbranches`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.NEON_API_KEY}`,
'Content-Type': 'application/โjson'
},
body: JSON.stringify({
branch: {
name: `restore-${timestamp.toISOString().replace(/[:.]/โg, '-')}`,
parent_id: 'main',
parent_timestamp: timestamp.toISOString()
}
})
}
)
return response.json()
}
// ์ฌ์ฉ ์์: 1์๊ฐ ์ ์ผ๋ก ๋ณต์
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000)
const restoredBranch = await restoreToPoint(oneHourAgo)
์คํค๋ง ์ ์:
// schema.ts
import { pgTable, uuid, text, timestamp, boolean, jsonb } from 'drizzle-orm/โpg-core'
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: text('email').notNull().unique(),
name: text('name'),
createdAt: timestamp('created_at').defaultNow(),
metadata: jsonb('metadata')
})
export const projects = pgTable('projects', {
id: uuid('id').primaryKey().defaultRandom(),
name: text('name').notNull(),
ownerId: uuid('owner_id').references(() => users.id),
isPublic: boolean('is_public').default(false),
createdAt: timestamp('created_at').defaultNow()
})
Drizzle ํด๋ผ์ด์ธํธ ์ค์ :
// db.ts
import { neon } from '@neondatabase/โserverless'
import { drizzle } from 'drizzle-orm/โneon-http'
import * as schema from './โschema'
const sql = neon(process.env.DATABASE_URL!)
export const db = drizzle(sql, { schema })
// ์ฟผ๋ฆฌ ์์
const allUsers = await db.select().from(schema.users)
const userProjects = await db
.select()
.from(schema.projects)
.where(eq(schema.projects.ownerId, userId))
.orderBy(desc(schema.projects.createdAt))
Prisma ์คํค๋ง:
// schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
name String?
projects Project[]
createdAt DateTime @default(now())
}
model Project {
id String @id @default(uuid())
name String
owner User @relation(fields: [ownerId], references: [id])
ownerId String
isPublic Boolean @default(false)
createdAt DateTime @default(now())
}
Prisma + Neon Serverless ๋๋ผ์ด๋ฒ:
// db.ts
import { Pool, neonConfig } from '@neondatabase/โserverless'
import { PrismaNeon } from '@prisma/โadapter-neon'
import { PrismaClient } from '@prisma/โclient'
neonConfig.webSocketConstructor = require('ws')
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
const adapter = new PrismaNeon(pool)
export const prisma = new PrismaClient({ adapter })
// ์ฟผ๋ฆฌ ์์
const users = await prisma.user.findMany({
include: { projects: true }
})
Edge Function ์ค์ :
import { neon } from '@neondatabase/โserverless'
// edge ํ๊ฒฝ์์๋ pooled connection ์ฌ์ฉ
const sql = neon(process.env.DATABASE_URL_POOLED!)
export const config = {
runtime: 'edge'
}
export default async function handler(request: Request) {
const users = await sql`SELECT id, name FROM users LIMIT 10`
return Response.json(users)
}
GitHub Actions ํตํฉ:
name: Preview Environment
on:
pull_request:
types: [opened, synchronize, closed]
jobs:
create-preview:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- name: Create Neon Branch
id: create-branch
run: |
BRANCH=$(curl -s -X POST \
-H "Authorization: Bearer ${{ secrets.NEON_API_KEY }}" \
-H "Content-Type: application/โjson" \
-d '{"branch":{"name":"pr-${{ github.event.number }}"}}' \
"https://console.neon.tech/โapi/โv2/โprojects/โ${{ secrets.NEON_PROJECT_ID }}/โbranches")
echo "branch_id=$(echo $BRANCH | jq -r '.branch.id')" >> $GITHUB_OUTPUT
cleanup-preview:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- name: Delete Neon Branch
run: |
curl -X DELETE \
-H "Authorization: Bearer ${{ secrets.NEON_API_KEY }}" \
"https://console.neon.tech/โapi/โv2/โprojects/โ${{ secrets.NEON_PROJECT_ID }}/โbranches/โpr-${{ github.event.number }}"
API๋ฅผ ํตํ ์ปดํจํธ ์ค์ :
async function configureAutoScaling(endpointId: string) {
const response = await fetch(
`https://console.neon.tech/โapi/โv2/โprojects/โ${process.env.NEON_PROJECT_ID}/โendpoints/โ${endpointId}`,
{
method: 'PATCH',
headers: {
'Authorization': `Bearer ${process.env.NEON_API_KEY}`,
'Content-Type': 'application/โjson'
},
body: JSON.stringify({
endpoint: {
autoscaling_limit_min_cu: 0.25, // Scale to zero
autoscaling_limit_max_cu: 4, // ์ต๋ 4 compute units
suspend_timeout_seconds: 300 // 5๋ถ ์ ํด ํ ์ค๋จ
}
})
}
)
return response.json()
}
๊ฐ๋ฐ์์ ํ๋ก๋์ ์ผ๋ก:
// direct connection์์ ๋ง์ด๊ทธ๋ ์ด์
์คํ (pooled ์๋)
import { migrate } from 'drizzle-orm/โneon-http/โmigrator'
import { neon } from '@neondatabase/โserverless'
import { drizzle } from 'drizzle-orm/โneon-http'
async function runMigrations() {
// ๋ง์ด๊ทธ๋ ์ด์
์๋ direct connection ์ฌ์ฉ
const sql = neon(process.env.DATABASE_URL!)
const db = drizzle(sql)
await migrate(db, { migrationsFolder: './โdrizzle' })
console.log('Migrations completed')
}
Status: Production Ready Generated with: Do Skill Factory v2.0 Last Updated: 2025-12-07 Technology: Neon Serverless PostgreSQL
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgรคnger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience