SQLKit 3.x requires SwiftNIO 2.x or later. Previous major versions are no longer supported.
Supported Platforms
SQLKit supports the following platforms:
Ubuntu 20.04+
macOS 10.15+
iOS 13+
tvOS 13+ and watchOS 7+ (experimental)
Overview
SQLKit is an API for building and serializing SQL queries in Swift. SQLKit attempts to abstract away SQL dialect inconsistencies where possible allowing you to write queries that can run on multiple database flavors. Where abstraction is not possible, SQLKit provides powerful APIs for custom or dynamic behavior.
SQLKit does not deal with creating or managing database connections itself. This package is focused entirely around building and serializing SQL queries. To connect to your SQL database, refer to your specific database package’s documentation. Once you are connected to your database and have an instance of SQLDatabase, you are ready to continue.
Database
Instances of SQLDatabase are capable of serializing and executing SQLExpression.
let db: any SQLDatabase = ...
db.execute(sql: any SQLExpression, onRow: (any SQLRow) -> ())
SQLExpression is a protocol that represents a SQL query string and optional bind values. It can represent an entire SQL query or just a fragment.
SQLKit provides SQLExpressions for common queries like SELECT, UPDATE, INSERT, DELETE, CREATE TABLE, and many more.
For query builders that support returning results (e.g. any builder conforming to the SQLQueryFetcher protocol), there are additional methods for handling the database output:
all(): Returns an array of rows.
first(): Returns an optional row.
run(_:): Accepts a closure that handles rows as they are returned.
Each of these methods returns SQLRow, which has methods for access column values.
let row: any SQLRow
let name = try row.decode(column: "name", as: String.self)
print(name) // String
Codable
SQLRow also supports decoding Codable models directly from a row.
struct Planet: Codable {
var name: String
}
let planet = try row.decode(model: Planet.self)
Query builders that support returning results have convenience methods for automatically decoding models.
let planets: [Planet] = try await db.select()
...
.all(decoding: Planet.self)
Select
The SQLDatabase.select() method creates a SELECT query builder:
This code generates the following SQL when used with the PostgresKit driver:
SELECT "id", "name" FROM "planets" WHERE "name" = $1 -- bindings: ["Earth"]
Notice that Encodable values are automatically bound as parameters instead of being serialized directly to the query.
The select builder includes the following methods (typically with several variations):
columns() (specify a list of columns and/or expressions to return)
from() (specify a table to select from)
join() (specify additional tables and how to relate them to others)
where() and orWhere() (specify conditions that narrow down the possible results)
limit() and offset() (specify a limited and/or offsetted range of results to return)
orderBy() (specify how to sort results before returning them)
groupBy() (specify columns and/or expressions for aggregating results)
having() and orHaving() (specify secondary conditions to apply to the results after aggregation)
distinct() (specify coalescing of duplicate results)
for() and lockingClause() (specify locking behavior for rows that appear in results)
Conditional expressions provided to where() or having() are joined with AND. Corresponding orWhere() and orHaving() methods join conditions with OR instead.
This code generates the following SQL when used with the SQLite driver:
DELETE FROM "planets" WHERE "name" = ?1 -- bindings: ["Jupiter"]
The delete builder is also an SQLPredicateBuilder.
Raw
The raw(_:) method allows passing custom SQL query strings, with support for parameterized bindings and correctly-quoted identifiers:
let planets = try await db.raw("SELECT \(SQLLiteral.all) FROM \(ident: table) WHERE \(ident: name) = \(bind: "planet")")
.all()
This code generates the following SQL when used with the PostgreSQL driver:
SELECT * FROM "planets" WHERE "name" = $1 -- bindings: ["planet"]
The \(bind:) interpolation should be used for any user input to avoid SQL injection. The \(ident:) interpolation is used to safely specify identifiers such as table and column names.
⚠️ **Important!**⚠️
Always prefer a structured query (i.e. one for which a builder or expression type exists) over raw queries. Consider writing your own SQLExpressions, and even your own SQLQueryBuilders, rather than using raw queries, and don’t hesitate to open an issue to ask for additional feature support.
Build SQL queries in Swift. Extensible, protocol-based design that supports DQL, DML, and DDL.
Using SQLKit
Use standard SwiftPM syntax to include SQLKit as a dependency in your
Package.swift
file.SQLKit 3.x requires SwiftNIO 2.x or later. Previous major versions are no longer supported.
Supported Platforms
SQLKit supports the following platforms:
Overview
SQLKit is an API for building and serializing SQL queries in Swift. SQLKit attempts to abstract away SQL dialect inconsistencies where possible allowing you to write queries that can run on multiple database flavors. Where abstraction is not possible, SQLKit provides powerful APIs for custom or dynamic behavior.
Supported Databases
These database packages are drivers for SQLKit:
Configuration
SQLKit does not deal with creating or managing database connections itself. This package is focused entirely around building and serializing SQL queries. To connect to your SQL database, refer to your specific database package’s documentation. Once you are connected to your database and have an instance of
SQLDatabase
, you are ready to continue.Database
Instances of
SQLDatabase
are capable of serializing and executingSQLExpression
.SQLExpression
is a protocol that represents a SQL query string and optional bind values. It can represent an entire SQL query or just a fragment.SQLKit provides
SQLExpression
s for common queries likeSELECT
,UPDATE
,INSERT
,DELETE
,CREATE TABLE
, and many more.SQLDatabase
can be used to create fluent query builders for most of these query types.You can execute a query builder by calling
run()
.Rows
For query builders that support returning results (e.g. any builder conforming to the
SQLQueryFetcher
protocol), there are additional methods for handling the database output:all()
: Returns an array of rows.first()
: Returns an optional row.run(_:)
: Accepts a closure that handles rows as they are returned.Each of these methods returns
SQLRow
, which has methods for access column values.Codable
SQLRow
also supports decodingCodable
models directly from a row.Query builders that support returning results have convenience methods for automatically decoding models.
Select
The
SQLDatabase.select()
method creates aSELECT
query builder:This code generates the following SQL when used with the PostgresKit driver:
Notice that
Encodable
values are automatically bound as parameters instead of being serialized directly to the query.The select builder includes the following methods (typically with several variations):
columns()
(specify a list of columns and/or expressions to return)from()
(specify a table to select from)join()
(specify additional tables and how to relate them to others)where()
andorWhere()
(specify conditions that narrow down the possible results)limit()
andoffset()
(specify a limited and/or offsetted range of results to return)orderBy()
(specify how to sort results before returning them)groupBy()
(specify columns and/or expressions for aggregating results)having()
andorHaving()
(specify secondary conditions to apply to the results after aggregation)distinct()
(specify coalescing of duplicate results)for()
andlockingClause()
(specify locking behavior for rows that appear in results)Conditional expressions provided to
where()
orhaving()
are joined withAND
. CorrespondingorWhere()
andorHaving()
methods join conditions withOR
instead.This code generates the following SQL when used with the MySQL driver:
where()
,orWhere()
,having()
, andorHaving()
also support creating grouped clauses:This code generates the following SQL when used with the SQLite driver:
Insert
The
insert(into:)
method creates anINSERT
query builder:This code generates the following SQL when used with the PostgreSQL driver:
The insert builder also has a method for encoding a
Codable
type as a set of values:This code generates the same SQL as would
builder.columns("name").values("Milky Way")
.Update
The
update(_:)
method creates anUPDATE
query builder:This code generates the following SQL when used with the MySQL driver:
The update builder supports the same
where()
andorWhere()
methods as the select builder, via theSQLPredicateBuilder
protocol.Delete
The
delete(from:)
method creates aDELETE
query builder:This code generates the following SQL when used with the SQLite driver:
The delete builder is also an
SQLPredicateBuilder
.Raw
The
raw(_:)
method allows passing custom SQL query strings, with support for parameterized bindings and correctly-quoted identifiers:This code generates the following SQL when used with the PostgreSQL driver:
The
\(bind:)
interpolation should be used for any user input to avoid SQL injection. The\(ident:)
interpolation is used to safely specify identifiers such as table and column names.⚠️ **Important!**⚠️
Always prefer a structured query (i.e. one for which a builder or expression type exists) over raw queries. Consider writing your own
SQLExpression
s, and even your ownSQLQueryBuilder
s, rather than using raw queries, and don’t hesitate to open an issue to ask for additional feature support.