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.
Scanned 9/10/2026
Install to Claude Code
npx -y skills add luokai0/ai-agent-skills-by-luo-kai --skill postgres-nio --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Postgres Nio?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/luokai0-postgres-nio)More formats (shields.io, HTML) on the badges page.
---
author: luo-kai
name: postgres-nio
description: '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.'
references:
- postgres-patterns.md
---
# PostgreSQL
The **postgres-nio** library provides a Swift PostgreSQL client with full async/await support, type-safe query building, connection pooling, and prepared statements.
## Quick Start
### Installation
Add to `Package.swift`:
```swift
dependencies: [
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.21.0")
]
```
### Basic Usage
```swift
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()
}
```
## Type-Safe Queries
Queries use string interpolation to prevent SQL injection:
```swift
let userId = 123
let email = "alice@example.com"
// 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, "alice@example.com"]
let rows = try await client.query(query)
```
### Dynamic Identifiers
Use `unescaped` for table/column names (not user input!):
```swift
let tableName = "users" // Must be trusted, not user input
let query: PostgresQuery = "SELECT * FROM \(unescaped: tableName) WHERE id = \(userId)"
```
## Row Decoding
### Decode by Position
```swift
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))
}
```
### Decode by Column Name
```swift
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
}
```
### Custom Types
```swift
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)
}
```
## Prepared Statements
Define reusable prepared statements for performance:
```swift
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: "alice@example.com"))
for try await (id, name, createdAt) in results {
print("Found user \(id)")
}
```
## Transactions
```swift
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
}
}
```
## Bulk Loading (COPY FROM)
Efficiently load large datasets:
```swift
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")
```
## Connection Configuration
```swift
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)
```
### TLS Options
```swift
// Disable TLS (local development)
tls: .disable
// Prefer TLS (use if available)
tls: .prefer(.makeClientConfiguration())
// Require TLS (production)
tls: .require(.makeClientConfiguration())
```
### Unix Domain Socket
```swift
let config = PostgresClient.Configuration(
unixSocketPath: "/var/run/postgresql/.s.PGSQL.5432",
username: "postgres",
database: "myapp"
)
```
## Error Handling
```swift
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
}
}
```
## Streaming Large Results
Results are streamed automatically with backpressure:
```swift
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)
}
```
## Supported Types
| 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` |
## Reference Files
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 encoding
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!