postgres-nio/SKILL.md
Expert guidance on using PostgreSQL with Swift. Use when developers mention: (1) PostgreSQL or Postgres in Swift, (2) postgres-nio library, (3) SQL queries in Swift, (4) PostgreSQL connection pooling, (5) prepared statements, (6) type-safe database access, (7) bulk loading or COPY FROM, (8) PostgresClient or PostgresConnection.
npx skillsauth add wendylabsinc/claude-skills postgres-nioInstall 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.
The postgres-nio library provides a Swift PostgreSQL client with full async/await support, type-safe query building, connection pooling, and prepared statements.
Add to Package.swift:
dependencies: [
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.21.0")
]
import PostgresNIO
// Using PostgresClient (recommended - includes connection pooling)
let client = PostgresClient(
configuration: .init(
host: "localhost",
port: 5432,
username: "postgres",
password: "secret",
database: "myapp",
tls: .disable
)
)
// Run as a service
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask { try await client.run() }
// Use client for queries
let rows = try await client.query("SELECT id, name FROM users")
for try await row in rows {
let (id, name) = try row.decode((Int.self, String.self))
print("User \(id): \(name)")
}
group.cancelAll()
}
Queries use string interpolation to prevent SQL injection:
let userId = 123
let email = "[email protected]"
// Interpolated values become parameter bindings ($1, $2, ...)
let query: PostgresQuery = "SELECT * FROM users WHERE id = \(userId) AND email = \(email)"
// Generates: "SELECT * FROM users WHERE id = $1 AND email = $2" with bindings [123, "[email protected]"]
let rows = try await client.query(query)
Use unescaped for table/column names (not user input!):
let tableName = "users" // Must be trusted, not user input
let query: PostgresQuery = "SELECT * FROM \(unescaped: tableName) WHERE id = \(userId)"
let rows = try await client.query("SELECT id, name, email, created_at FROM users")
for try await row in rows {
let (id, name, email, createdAt) = try row.decode((Int.self, String.self, String.self, Date.self))
}
for try await row in rows {
let id = try row["id"].decode(Int.self)
let name = try row["name"].decode(String.self)
let email = try row["email"].decode(String?.self) // Optional
}
struct User: Decodable {
let id: Int
let name: String
let email: String
}
// Decode into custom type
for try await row in rows {
let user = try row.decode(User.self)
}
Define reusable prepared statements for performance:
struct GetUserByEmail: PostgresPreparedStatement {
typealias Row = (Int, String, Date)
static let sql = "SELECT id, name, created_at FROM users WHERE email = $1"
var email: String
func makeBindings() -> PostgresBindings {
var bindings = PostgresBindings()
bindings.append(email)
return bindings
}
func decodeRow(_ row: PostgresRow) throws -> Row {
try row.decode(Row.self)
}
}
// Execute prepared statement
let results = try await connection.execute(GetUserByEmail(email: "[email protected]"))
for try await (id, name, createdAt) in results {
print("Found user \(id)")
}
try await client.withConnection { connection in
try await connection.query("BEGIN")
do {
try await connection.query("INSERT INTO accounts (id, balance) VALUES (\(fromId), \(fromBalance - amount))")
try await connection.query("INSERT INTO accounts (id, balance) VALUES (\(toId), \(toBalance + amount))")
try await connection.query("COMMIT")
} catch {
try await connection.query("ROLLBACK")
throw error
}
}
Efficiently load large datasets:
let rowCount = try await connection.copyFrom(
table: "users",
columns: ["name", "email", "created_at"],
format: .csv(delimiter: ",", header: false)
) { writer in
for user in users {
var buffer = ByteBuffer()
buffer.writeString("\(user.name),\(user.email),\(user.createdAt)\n")
try await writer.write(buffer)
}
}
print("Inserted \(rowCount) rows")
let config = PostgresClient.Configuration(
host: "localhost",
port: 5432,
username: "postgres",
password: "secret",
database: "myapp",
tls: .prefer(.makeClientConfiguration()),
options: .init(
minimumConnections: 2,
maximumConnections: 10,
connectionIdleTimeout: .minutes(5),
keepAliveBehavior: .init(frequency: .seconds(30))
)
)
let client = PostgresClient(configuration: config)
// Disable TLS (local development)
tls: .disable
// Prefer TLS (use if available)
tls: .prefer(.makeClientConfiguration())
// Require TLS (production)
tls: .require(.makeClientConfiguration())
let config = PostgresClient.Configuration(
unixSocketPath: "/var/run/postgresql/.s.PGSQL.5432",
username: "postgres",
database: "myapp"
)
do {
let rows = try await client.query("SELECT * FROM users WHERE id = \(id)")
// ...
} catch let error as PSQLError {
switch error.code {
case .uniqueViolation:
throw MyError.duplicateEntry
case .foreignKeyViolation:
throw MyError.invalidReference
default:
throw error
}
}
Results are streamed automatically with backpressure:
let rows = try await client.query("SELECT * FROM large_table")
// Process one row at a time - memory efficient
for try await row in rows {
let data = try row.decode(LargeData.self)
try await processData(data)
}
| Swift Type | PostgreSQL Type |
|------------|-----------------|
| Int, Int32, Int64 | integer, bigint |
| Float, Double | real, double precision |
| String | text, varchar |
| Bool | boolean |
| Date | timestamp, timestamptz |
| Data | bytea |
| UUID | uuid |
| [T] | array |
| JSON types | json, jsonb |
Load these files as needed for specific topics:
references/postgres-patterns.md - String interpolation for SQL safety, protocol hierarchy for encoding/decoding, variadic generics for row decoding, prepared statement patterns, hierarchical state machines, backpressure-aware row streaming, COPY FROM bulk loading, wire protocol encodingdevelopment
Expert guidance on Swift best practices, patterns, and implementation. Use when developers mention: (1) Swift configuration or environment variables, (2) swift-log or logging patterns, (3) OpenTelemetry or swift-otel, (4) Swift Testing framework or @Test macro, (5) Foundation avoidance or cross-platform Swift, (6) platform-specific code organization, (7) Span or memory safety patterns, (8) non-copyable types (~Copyable), (9) API design patterns or access modifiers.
tools
Expert guidance on building and deploying apps to WendyOS edge devices. Use when developers mention: (1) Wendy or WendyOS, (2) wendy CLI commands, (3) wendy.json or entitlements, (4) deploying apps to edge devices, (5) remote debugging Swift on ARM64, (6) NVIDIA Jetson or Raspberry Pi apps, (7) cross-compiling Swift for ARM64.
development
Curated Swift package ecosystem for WendyOS and Linux. Use when developers mention: (1) Swift packages for Linux or ARM64/AMD64, (2) choosing a Swift library, (3) Swift Package Index, (4) swiftpackageindex.com, (5) what Swift library to use, (6) Swift on WendyOS dependencies, (7) edge computing Swift libraries.
development
Expert guidance on building WASM apps for Wendy Lite MCU firmware on ESP32-C6. Use when developers mention: (1) Wendy Lite or wendy-lite, (2) WASM apps on ESP32 or microcontrollers, (3) WendyLite Swift package or import WendyLite, (4) building C/Rust/Swift/Zig apps for ESP32, (5) WAMR runtime on embedded devices, (6) GPIO/I2C/SPI/UART/NeoPixel from WASM, (7) Embedded Swift on WASM or wasm32-none-none-wasm, (8) BLE provisioning on ESP32-C6, (9) uploading WASM binaries to MCU, (10) TLS/networking on ESP32 from Swift.