swift-microservices/swift-persistence-postgres
The Postgres driver for swift-persistence:
The database
PostgresDatabase is a Database over a PostgresNIO connection pool. Each unit of work borrows a connection, begins a transaction, applies the settings for the call, builds the scope on that connection, and hands it to the work. Returning commits; throwing rolls back and rethrows the same error, unwrapped.
struct PostgresPostsScope: PostgresScope, CreatePostUseCaseScope {
let postRepository: any PostRepository
init(connection: PostgresConnection, logger: Logger) {
self.postRepository = PostgresPostRepository(connection: connection, logger: logger)
}
}
let database = PostgresDatabase<PostgresPostsScope>(
client: client,
settings: ["application_name": "posts"],
logger: logger
)Repositories use PostgresNIO directly. This package adds no query, row, or connection abstraction; the driver's types are that layer.
Settings
PostgresSettings is a dictionary of configuration parameters. Every entry becomes set_config(name, value, true) at the start of a transaction: visible to every statement inside, gone when it ends, by commit or by rollback.
Constant settings are given to the database once. Settings that differ per call, such as who is calling, are bound in the task's ServiceContext where the caller becomes known, and each transaction begun under that task reads them:
var context = ServiceContext.current ?? .topLevel
context.postgresSettings = ["app.caller_user_id": caller.id.uuidString.lowercased()]
return try await ServiceContext.withValue(context) {
try await next(request, context)
}A row-level security policy then reads the caller back:
CREATE POLICY posts_by_author ON posts
USING (author_id = current_setting('app.caller_user_id', true)::uuid);A client for one operation
A long-running service runs its PostgresClient as a service and builds the database on it. For work that needs a client only as long as it runs, such as applying migrations at startup, withClient starts one in a task group and cancels it when the operation returns or throws:
try await PostgresClient.withClient(configuration: configuration, logger: logger) { client in
try await migrations.apply(client: client)
}Requirements
Swift 6.3, macOS 15 or Linux, PostgreSQL 14 or later.
Development
The tests run against a real Postgres named by POSTGRES_HOST, POSTGRES_PORT, POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB, and skip when POSTGRES_HOST is unset.
scripts/test.sh # starts an ephemeral Postgres, runs swift test, stops it
swift-format lint --strict --recursive Sources Tests # what the soundness check runsContributing
Pull requests are welcome. Keep a change focused, prove new behaviour with a test, and label the pull request with its semantic version impact.
License
MIT. See LICENSE.
Package Metadata
Repository: swift-microservices/swift-persistence-postgres
Default branch: main
README: README.md