Was macht Neon Serverless PostgreSQL Patterns?
Kurzanleitung: Verwenden Sie
@neondatabase/serverlessfür den Edge-/Serverless-Datenbankzugriff. Bevorzugen Sie dieneon()HTTP-Funktion für einzelne Abfragen (schneller, zustandslos) undPool/Clientfür interaktive Transaktionen. Verwenden Sie gepoolte Verbindungsstrings (-pooler-Suffix) für serverlose Workloads, direkte Verbindungen nur für Migrationen. Erstellen Sie Branches Ihrer Datenbank für Entwicklungs-/Vorschauumgebungen mit Copy-on-Write-Semantik. Behandeln Sie immer Kaltstarts durch Scale-to-Zero (200-500 ms Aufwachzeit).
<critical_requirements>
KRITISCH: Vor der Verwendung dieses Skills
Der gesamte Code muss die Projektkonventionen in CLAUDE.md befolgen (kebab-case, benannte Exporte, Importreihenfolge,
import type, benannte Konstanten)
(Sie MÜSSEN die neon() HTTP-Funktion für einzelne Abfragen in Edge-/Serverless-Laufzeitumgebungen verwenden – sie ist 2-3x schneller als WebSocket für Einmaloperationen)
(Sie MÜSSEN Pool/Client-Verbindungen innerhalb desselben Request-Handlers in serverlosen Umgebungen schließen – WebSocket-Verbindungen können einen einzelnen Request nicht überdauern)
(Sie MÜSSEN gepoolte Verbindungsstrings (-pooler-Suffix) für serverlose Workloads verwenden – direkte Verbindungen erschöpfen die begrenzten Verbindungsslots)
(Sie MÜSSEN die Scale-to-Zero-Aufwachlatenz (200-500 ms) mit angemessenen Verbindungs-Timeouts und Wiederholungslogik behandeln)
(Sie MÜSSEN sql.unsafe() nur für vertrauenswürdige, bekanntermaßen sichere Zeichenfolgen wie Tabellen-/Spaltennamen verwenden – niemals für Benutzereingaben)
</critical_requirements>
Auto-Erkennung: Neon, @neondatabase/serverless, neon(), neonConfig, Neon Serverless-Treiber, Neon-Datenbank, Neon-Branch, neonctl, Neon-Verbindungspooling, Neon Scale-to-Zero, Neon-Autoskalierung, Neon-Postgres, ep-*-pooler
Wann verwenden:
- Postgres aus Edge-/Serverless-Funktionen abfragen (Edge-Laufzeitumgebungen, Serverless-Plattformen)
- Verbindungsstrings (gepoolt vs. direkt) für verschiedene Workloads einrichten
- Datenbank-Branches für Entwicklungs-, Vorschau- oder CI-Umgebungen erstellen
- Scale-to-Zero-Verhalten und Kaltstartoptimierung verwalten
- Transaktionen in serverlosen Kontexten ausführen (HTTP-Batch oder WebSocket)
- Programmatische Branch-Verwaltung über Neon API oder neonctl-CLI
Abgedeckte Schlüsselmuster:
neon()HTTP-Abfragen mit SQL-getaggten Vorlagen und zusammensetzbaren FragmentenPool/ClientWebSocket-Verbindungen mit ordnungsgemäßem Lebenszyklus-Management- Gepoolte (
-pooler) vs. direkte Verbindungsstrings und wann sie verwendet werden - Datenbank-Branching (Entwicklungs-Branches, PR-Vorschau-Branches, Schema-only-Branches)
- Scale-to-Zero-Verhalten, Kaltstartminderung und Autoskalierung
sql.transaction()für nicht-interaktive HTTP-Transaktionen- Neon API und neonctl-CLI für programmatische Branch-Verwaltung
Wann NICHT verwenden:
- Traditionelle langlebige Serververbindungen (verwenden Sie den Standard-
pg-Treiber über TCP) - Komplexe ORM-spezifische Muster (verwenden Sie das Skill Ihres ORMs)
- Allgemeine PostgreSQL-Abfragesyntax (verwenden Sie ein SQL/Postgres-Skill)
Detaillierte Ressourcen:
- Für Entscheidungsrahmen und Schnellnachschlagetabellen siehe reference.md
Treiber & Abfragen:
- examples/core.md – Treibereinrichtung, HTTP-Abfragen, WebSocket-Verbindungen, Transaktionen
Branching & Operationen:
- examples/branching.md – Entwicklungs-Branches, PR-Vorschauen, neonctl-CLI, Neon API, CI/CD-Workflows
Philosophie
Neon trennt Speicher und Rechenleistung für PostgreSQL und ermöglicht serverlose Funktionen, die mit herkömmlichem Postgres unmöglich sind: Scale-to-Zero, sofortiges Branching und Autoskalierung. Der @neondatabase/serverless-Treiber ersetzt TCP durch HTTP und WebSockets und macht Postgres für Edge-Laufzeitumgebungen zugänglich, die kein TCP unterstützen.
Kernprinzipien:
- HTTP für Geschwindigkeit, WebSocket für Sitzungen – Die
neon()-Funktion verwendet HTTP-Fetch (~3 Roundtrips) für einzelne Abfragen.Pool/Clientverwenden WebSockets (~8 Roundtrips), wenn Sie Sitzungen oder interaktive Transaktionen benötigen. Wählen Sie den richtigen Transport für die Aufgabe. - Standardmäßig gepoolt – Gepoolte Verbindungen werden über PgBouncer (Transaktionsmodus) geleitet und bewältigen bis zu 10.000 gleichzeitige Clients. Direkte Verbindungen sind durch die Computergröße begrenzt (100-4.000) und sollten nur für Migrationen oder Funktionen verwendet werden, die Sitzungsstatus erfordern.
- Branches sind günstig – Copy-on-Write bedeutet, dass ein Branch einer 500-GB-Datenbank keinen zusätzlichen Speicher zuweist, bis die Daten abweichen. Verwenden Sie Branches frei für Entwicklung, Vorschau, Tests und CI.
- Scale-to-Zero ist die Voreinstellung – Computes werden nach 5 Minuten Inaktivität ausgesetzt. Kaltstarts dauern 200-500 ms. Planen Sie dies mit Timeouts, Wiederholungen und Verbindungspooling ein.
- Sicherheit vor SQL-Injection integriert – Die getaggte Vorlagenfunktion parametrisiert automatisch. Seit v1.0 ist das Aufrufen von
neon()als normale Funktion ein Typfehler, der unbeabsichtigte Injection verhindert.
Wann der Neon Serverless-Treiber verwendet werden sollte:
- Edge-/Serverless-Funktionen, die keine TCP-Verbindungen öffnen können
- Anwendungen, die von Datenbank-Branching profitieren (Vorschauumgebungen pro PR)
- Kostenbewusste Workloads, die von Scale-to-Zero profitieren
- Hochparallele Serverless-Apps, die Verbindungspooling benötigen
Wann NICHT verwenden:
- Lang laufende Serverprozesse mit persistenten Verbindungen (verwenden Sie Standard-
pgüber TCP) - Workloads, die sitzungsbezogene Funktionen über PgBouncer erfordern (LISTEN/NOTIFY, SET, temporäre Tabellen)
- Datenbanken größer als 16 CU, die ständig laufende Compute benötigen (Scale-to-Zero ist über 16 CU nicht verfügbar)
Kernmuster
Muster 1: HTTP-Abfragen mit neon()
Die neon()-Funktion erstellt eine HTTP-basierte Abfragefunktion mit SQL-getaggten Vorlagen. Dies ist der schnellste Weg für einzelne, nicht interaktive Abfragen.
import { neon } from "@neondatabase/serverless";
const DATABASE_URL = process.env.DATABASE_URL!;
const sql = neon(DATABASE_URL);
// Getaggte Vorlage – Parameter werden automatisch parametrisiert (sicher vor Injection)
const userId = "abc-123";
const posts =
await sql`SELECT id, title FROM posts WHERE author_id = ${userId}`;
Warum gut: Die getaggte Vorlage parametrisiert Werte automatisch und verhindert SQL-Injection, HTTP ist ~3 Roundtrips vs. ~8 für WebSocket, zustandslos (keine zu verwaltende Verbindung)
// SCHLECHT: Aufruf von neon() als normale Funktion (v1.0+ Typfehler)
const sql = neon(DATABASE_URL);
const result = await sql(`SELECT * FROM posts WHERE id = ${id}`); // TYPFEHLER + SQL-Injection-Risiko
Warum schlecht: Seit v1.0 ist das Aufrufen der Abfragefunktion als normale Funktion (keine getaggte Vorlage) ein Laufzeit- und Typfehler – dies wurde speziell geändert, um SQL-Injection durch Zeichenfolgeninterpolation zu verhindern
sql.query() für dynamisches SQL
Wenn die Abfragezeichenfolge in einer Variable (kein Template-Literal) vorliegt, verwenden Sie die Methode .query() mit nummerierten Platzhaltern:
const q = "SELECT * FROM posts WHERE id = $1 AND status = $2";
const posts = await sql.query(q, [postId, "published"]);
Wann verwenden: Dynamische SQL-Zeichenfolgen, die zur Laufzeit erstellt werden, oder in Variablen gespeicherte Abfragen. Parameter werden weiterhin sicher über $1, $2 usw. parametrisiert.
Muster 2: Zusammensetzbare Abfragefragmente
Vorlagenabfragen unterstützen die Komposition mit automatischer Parameternummerierung über Fragmente hinweg.
import { neon } from "@neondatabase/serverless";
const sql = neon(DATABASE_URL);
// Erstellen Sie Abfragen aus wiederverwendbaren Fragmenten
const whereClause = sql`WHERE status = ${"active"} AND role = ${"admin"}`;
const orderClause = sql`ORDER BY created_at DESC`;
const PAGE_SIZE = 20;
const limitClause = sql`LIMIT ${PAGE_SIZE}`;
const users =
await sql`SELECT id, name, email FROM users ${whereClause} ${orderClause} ${limitClause}`;
Warum gut: Parameter werden automatisch über zusammengesetzte Fragmente hinweg neu nummeriert, benannte Konstante für die Seitengröße, Fragmente sind über Abfragen hinweg wiederverwendbar
Dynamische Tabellen-/Spaltennamen mit sql.unsafe()
const TABLE_NAME = "posts";
const results =
await sql`SELECT * FROM ${sql.unsafe(TABLE_NAME)} WHERE id = ${postId}`;
Wann verwenden: Nur wenn Sie vertrauenswürdige Identifikatoren (Tabellennamen, Spaltennamen) interpolieren müssen, die nicht in SQL parametrisiert werden können. Übergeben Sie niemals benutzergelieferte Werte an sql.unsafe().
Muster 3: HTTP-Transaktionen mit sql.transaction()
Führen Sie mehrere Abfragen atomar über HTTP aus, ohne eine WebSocket-Verbindung zu benötigen.
import { neon } from "@neondatabase/serverless";
const sql = neon(DATABASE_URL);
// Array-Form – alle Abfragen werden in einem einzigen HTTP-Roundtrip ausgeführt
const [posts, totalCount] = await sql.transaction([
sql`SELECT id, title FROM posts ORDER BY created_at DESC LIMIT 10`,
sql`SELECT count(*) FROM posts`,
]);
// Funktionsform mit Transaktionsoptionen
const [, transferResult] = await sql.transaction(
(txn) => [
txn`UPDATE accounts SET balance = balance - ${amount} WHERE id = ${fromId}`,
txn`UPDATE accounts SET balance = balance + ${amount} WHERE id = ${toId}`,
],
{ isolationLevel: "Serializable" },
);
Warum gut: Atomare Ausführung ohne WebSocket-Overhead, Array-Form ist prägnant für schreibgeschützte Batches, Funktionsform unterstützt Transaktionsoptionen, Isolationsstufe steuert Konsistenzgarantien
// SCHLECHT: Ausführung verwandter Abfragen als separate HTTP-Anfragen
const posts = await sql`SELECT * FROM posts WHERE author_id = ${authorId}`;
const author = await sql`SELECT * FROM users WHERE id = ${authorId}`;
// Zwei separate HTTP-Roundtrips, nicht atomar, Race Conditions möglich
Warum schlecht: Separate HTTP-Aufrufe sind nicht atomar, Daten können sich zwischen Abfragen ändern, doppelte Netzwerklatenz
Transaktionsoptionen
isolationLevel:ReadUncommitted|ReadCommitted|RepeatableRead|SerializablereadOnly: boolean (Standardfalse)deferrable: boolean (Standardfalse, nur wirksam mitreadOnly: true+Serializable)
Muster 4: WebSocket-Verbindungen mit Pool/Client
Verwenden Sie Pool und Client für interaktive Transaktionen oder node-postgres-API-Kompatibilität. In serverlosen Umgebungen müssen Verbindungen innerhalb eines einzelnen Request-Handlers erstellt und geschlossen werden.
import { Pool } from "@neondatabase/serverless";
export async function handleRequest(request: Request): Promise<Response> {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
try {
// ... Abfragen ...
} finally {
await pool.end(); // IMMER innerhalb desselben Requests schließen
}
}
Schlüsselregel: Erstellen Sie Pool innerhalb des Handlers, schließen Sie mit pool.end() in finally. Ein globaler Pool verliert WebSocket-Verbindungen zwischen serverlosen Aufrufen.
Siehe examples/core.md – Muster 5-7 für request-spezifische Pools, interaktive Transaktionen und Node.js-WebSocket-Konfiguration.
Muster 5: Einrichtung von Verbindungsstrings
Neon bietet zwei Formate für Verbindungsstrings: gepoolt (über PgBouncer) und direkt.
# Gepoolte Verbindung (Hinweis: -pooler-Suffix bei der Endpunkt-ID)
# Verwenden für: Serverless-Funktionen, Web-Apps, Workloads mit hoher Parallelität
DATABASE_URL=postgresql://user:[email protected]/dbname?sslmode=require
# Direkte Verbindung (kein -pooler-Suffix)
# Verwenden für: Migrationen, pg_dump, LISTEN/NOTIFY, sitzungsbezogene Funktionen
DIRECT_DATABASE_URL=postgresql://user:[email protected]/dbname?sslmode=require
Warum gut: Gepoolt bewältigt bis zu 10.000 gleichzeitige Clients über PgBouncer im Transaktionsmodus, direkt bietet vollständige Sitzungsfunktionen für Verwaltungsaufgaben, separate Umgebungsvariablen machen die Unterscheidung explizit
Einschränkungen des PgBouncer-Transaktionsmodus
Die gepoolte Verbindung führt PgBouncer im Transaktionsmodus aus, was bedeutet, dass Verbindungen nach jeder Transaktion an den Pool zurückgegeben werden. Dies verbietet:
SET/RESET-Anweisungen (verwenden Sie stattdessenALTER ROLE ... SET)LISTEN/NOTIFYWITH HOLD CURSORPREPARE/DEALLOCATEauf SQL-Ebene (vorbereitete Anweisungen auf Protokollebene bis zu 1.000 werden unterstützt)- Temporäre Tabellen mit
PRESERVE/DELETE ROWS - Sitzungsberatungssperren
Muster 6: Kaltstart- und Scale-to-Zero-Handhabung
Neon-Computes werden nach 5 Minuten Inaktivität (Standard) automatisch ausgesetzt. Kaltstarts dauern 200-500 ms. Planen Sie dies mit angemessenen Timeouts und Wiederholungslogik.
const CONNECTION_TIMEOUT_MS = 10_000;
const sql = neon(process.env.DATABASE_URL!, {
fetchOptions: { signal: AbortSignal.timeout(CONNECTION_TIMEOUT_MS) },
});
Schlüsselregel: Legen Sie explizite Timeouts (10-15 s) fest, um die Kaltstartlatenz zu berücksichtigen. Verwenden Sie exponentielle Backoff-Wiederholungen für Zuverlässigkeit.
Siehe examples/core.md – Muster 8 für vollständige Timeout-Konfiguration und Wiederholungsmuster.
Scale-to-Zero-Fakten
- Standard-Auto-Suspend: 5 Minuten Inaktivität
- Kaltstartlatenz: ~200-500 ms (variiert je nach Region und Computergröße)
- Nur für Computes bis zu 16 CU verfügbar (größere Computes bleiben ständig eingeschaltet)
- Kostenpflichtige Pläne können das Suspend-Timeout anpassen (bis zu 7 Tage) oder vollständig deaktivieren
- Aktive logische Replikationsabonnenten verhindern eine Suspendierung
Muster 7: Datenbank-Branching
Neon-Branches verwenden Copy-on-Write-Semantik – das Verzweigen einer 500-GB-Datenbank ist sofort und weist keinen zusätzlichen Speicher zu, bis die Daten abweichen.
# neonctl installieren
npm install -g neonctl
# Einen Entwicklungs-Branch von main erstellen
neonctl branches create --name dev-alice --project-id <project-id>
# Einen Branch mit automatischem Ablauf erstellen (für CI/Vorschau)
neonctl branches create --name preview/pr-42 --project-id <project-id> --expires-at "2025-04-01T00:00:00Z"
# Schema-only-Branch erstellen (keine Daten kopiert – für sensible Umgebungen)
neonctl branches create --name ci-test --schema-only --project-id <project-id>
# Einen Entwicklungs-Branch zurücksetzen, um ihn an die aktuelle Produktion anzupassen
neonctl branches reset dev-alice --parent --project-id <project-id>
# Einen Branch löschen
neonctl branches delete preview/pr-42 --project-id <project-id>
Warum gut: Benannte Branches entsprechen dem Git-Workflow, TTL-Ablauf bereinigt CI-Branches automatisch, Schema-only-Branching schützt sensible Daten, Zurücksetzen synchronisiert Entwicklung mit Produktion, ohne neu zu erstellen