{"id":13468260,"url":"https://github.com/vapor/sql-kit","last_synced_at":"2025-04-07T17:07:22.098Z","repository":{"id":31896575,"uuid":"130444879","full_name":"vapor/sql-kit","owner":"vapor","description":"*️⃣ Build SQL queries in Swift. Extensible, protocol-based design that supports DQL, DML, and DDL.","archived":false,"fork":false,"pushed_at":"2024-09-23T01:37:02.000Z","size":570,"stargazers_count":258,"open_issues_count":6,"forks_count":58,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-03-31T16:55:25.063Z","etag":null,"topics":["ddl","dml","mysql","postgresql","server-side-swift","spm","sql","sqlite","swift","vapor"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vapor.png","metadata":{"funding":{"github":["vapor"],"open_collective":"vapor"},"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-21T06:02:52.000Z","updated_at":"2025-03-22T15:47:06.000Z","dependencies_parsed_at":"2024-02-23T17:25:25.280Z","dependency_job_id":"9fa234bf-fd6f-4b70-8efa-2291f474b3f9","html_url":"https://github.com/vapor/sql-kit","commit_stats":{"total_commits":196,"total_committers":34,"mean_commits":5.764705882352941,"dds":0.5408163265306123,"last_synced_commit":"e0b35ff07601465dd9f3af19a1c23083acaae3bd"},"previous_names":["vapor/sql"],"tags_count":82,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vapor%2Fsql-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vapor%2Fsql-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vapor%2Fsql-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vapor%2Fsql-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vapor","download_url":"https://codeload.github.com/vapor/sql-kit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247694875,"owners_count":20980733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ddl","dml","mysql","postgresql","server-side-swift","spm","sql","sqlite","swift","vapor"],"created_at":"2024-07-31T15:01:07.794Z","updated_at":"2025-04-07T17:07:22.079Z","avatar_url":"https://github.com/vapor.png","language":"Swift","readme":"\u003cp align=\"center\"\u003e\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://github.com/vapor/sql-kit/assets/1130717/b5828634-c1a1-4d91-b25d-20e033b77269\"\u003e\n  \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://github.com/vapor/sql-kit/assets/1130717/f45e4b01-0579-4011-8b06-0f159e0d386f\"\u003e\n  \u003cimg src=\"https://github.com/vapor/sql-kit/assets/1130717/f45e4b01-0579-4011-8b06-0f159e0d386f\" height=\"96\" alt=\"SQLKit\"\u003e\n\u003c/picture\u003e \n\u003cbr\u003e\n\u003cbr\u003e\n\u003ca href=\"https://docs.vapor.codes/4.0/\"\u003e\u003cimg src=\"https://design.vapor.codes/images/readthedocs.svg\" alt=\"Documentation\"\u003e\u003c/a\u003e\n\u003ca href=\"https://discord.gg/vapor\"\u003e\u003cimg src=\"https://design.vapor.codes/images/discordchat.svg\" alt=\"Team Chat\"\u003e\u003c/a\u003e\n\u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://design.vapor.codes/images/mitlicense.svg\" alt=\"MIT License\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/vapor/sql-kit/actions/workflows/test.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/vapor/sql-kit/test.yml?event=push\u0026style=plastic\u0026logo=github\u0026label=tests\u0026logoColor=%23ccc\" alt=\"Continuous Integration\"\u003e\u003c/a\u003e\n\u003ca href=\"https://codecov.io/github/vapor/sql-kit\"\u003e\u003cimg src=\"https://img.shields.io/codecov/c/github/vapor/sql-kit?style=plastic\u0026logo=codecov\u0026label=codecov\"\u003e\u003c/a\u003e\n\u003ca href=\"https://swift.org\"\u003e\u003cimg src=\"https://design.vapor.codes/images/swift58up.svg\" alt=\"Swift 5.8+\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr\u003e\n\nBuild SQL queries in Swift. Extensible, protocol-based design that supports DQL, DML, and DDL.\n\n## Using SQLKit\n\nUse standard SwiftPM syntax to include SQLKit as a dependency in your `Package.swift` file.\n\n```swift\n.package(url: \"https://github.com/vapor/sql-kit.git\", from: \"3.0.0\")\n```\n\nSQLKit 3.x requires [SwiftNIO](https://github.com/apple/swift-nio) 2.x or later. Previous major versions are no longer supported.\n\n### Supported Platforms\n\nSQLKit supports the following platforms:\n\n- Ubuntu 20.04+\n- macOS 10.15+\n- iOS 13+\n- tvOS 13+ and watchOS 7+ (experimental)\n\n## Overview\n\nSQLKit 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.\n\n### Supported Databases\n\nThese database packages are drivers for SQLKit:\n\n- [vapor/postgres-kit](https://github.com/vapor/postgres-kit): PostgreSQL\n- [vapor/mysql-kit](https://github.com/vapor/mysql-kit): MySQL and MariaDB\n- [vapor/sqlite-kit](https://github.com/vapor/sqlite-kit): SQLite\n\n### Configuration\n\nSQLKit 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.\n\n### Database\n\nInstances of `SQLDatabase` are capable of serializing and executing `SQLExpression`s.\n\n```swift\nlet db: any SQLDatabase = ...\ndb.execute(sql: any SQLExpression, onRow: (any SQLRow) -\u003e ())\n```\n\n`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.\n\nSQLKit provides `SQLExpression`s for common queries like `SELECT`, `UPDATE`, `INSERT`, `DELETE`, `CREATE TABLE`, and many more.\n\n```swift\nvar select = SQLSelect()\nselect.columns = [...]\nselect.tables = [...]\nselect.predicate = ...\n```\n\n`SQLDatabase` can be used to create fluent query builders for most of these query types.\n\n```swift\nstruct Planet: Codable { var id: Int, name: String }\n\nlet db: some SQLDatabase = ...\ntry await db.create(table: \"planets\")\n    .column(\"id\", type: .int, .primaryKey(autoIncrement: true), .notNull)\n    .column(\"name\", type: .string, .notNull)\n    .run()\ntry await db.insert(into: \"planets\")\n    .columns(\"id\", \"name\")\n    .values(SQLLiteral.default, SQLBind(\"Earth\"))\n    .values(SQLLiteral.default, SQLBind(\"Mars\"))\n    .run()\nlet planets = try await db.select()\n    .columns(\"id\", \"name\")\n    .from(\"planets\")\n    .all(decoding: Planet.self)\nprint(planets) // [Planet(id: 1, name: \"Earth\"), Planet(id: 2, name: \"Mars\")]\n```\n\nYou can execute a query builder by calling `run()`. \n\n### Rows\n\nFor query builders that support returning results (e.g. any builder conforming to the `SQLQueryFetcher` protocol), there are additional methods for handling the database output:\n\n- `all()`: Returns an array of rows.\n- `first()`: Returns an optional row.\n- `run(_:)`: Accepts a closure that handles rows as they are returned.\n\nEach of these methods returns `SQLRow`, which has methods for access column values.\n\n```swift\nlet row: any SQLRow\nlet name = try row.decode(column: \"name\", as: String.self)\nprint(name) // String\n```\n\n### Codable\n\n`SQLRow` also supports decoding `Codable` models directly from a row.\n\n```swift\nstruct Planet: Codable {\n    var name: String\n}\n\nlet planet = try row.decode(model: Planet.self)\n```\n\nQuery builders that support returning results have convenience methods for automatically decoding models.\n\n```swift\nlet planets: [Planet] = try await db.select()\n    ...\n    .all(decoding: Planet.self)\n```\n\n## Select\n\nThe `SQLDatabase.select()` method creates a `SELECT` query builder:\n\n```swift\nlet planets: [any SQLRow] = try await db.select()\n    .columns(\"id\", \"name\")\n    .from(\"planets\")\n    .where(\"name\", .equal, \"Earth\")\n    .all()\n```\n\nThis code generates the following SQL when used with the PostgresKit driver:\n\n```PLpgsql\nSELECT \"id\", \"name\" FROM \"planets\" WHERE \"name\" = $1 -- bindings: [\"Earth\"]\n```\n\nNotice that `Encodable` values are automatically bound as parameters instead of being serialized directly to the query.\n\nThe select builder includes the following methods (typically with several variations):\n\n- `columns()` (specify a list of columns and/or expressions to return)\n- `from()` (specify a table to select from)\n- `join()` (specify additional tables and how to relate them to others)\n- `where()` and `orWhere()` (specify conditions that narrow down the possible results)\n- `limit()` and `offset()` (specify a limited and/or offsetted range of results to return)\n- `orderBy()` (specify how to sort results before returning them)\n- `groupBy()` (specify columns and/or expressions for aggregating results)\n- `having()` and `orHaving()` (specify secondary conditions to apply to the results after aggregation)\n- `distinct()` (specify coalescing of duplicate results)\n- `for()` and `lockingClause()` (specify locking behavior for rows that appear in results)\n\nConditional expressions provided to `where()` or `having()` are joined with `AND`. Corresponding `orWhere()` and `orHaving()` methods join conditions with `OR` instead.\n\n```swift\nbuilder.where(\"name\", .equal, \"Earth\").orWhere(\"name\", .equal, \"Mars\")\n```\n\nThis code generates the following SQL when used with the MySQL driver:\n\n```mysql\nWHERE `name` = ? OR `name` = ? -- bindings: [\"Earth\", \"Mars\"]\n```\n\n`where()`, `orWhere()`, `having()`, and `orHaving()` also support creating grouped clauses:\n\n```swift\nbuilder.where(\"name\", .notEqual, SQLLiteral.null).where {\n    $0.where(\"name\", .equal, SQLBind(\"Milky Way\"))\n      .orWhere(\"name\", .equal, SQLBind(\"Andromeda\"))\n}\n```\n\nThis code generates the following SQL when used with the SQLite driver:\n\n```sql\nWHERE \"name\" \u003c\u003e NULL AND (\"name\" = ?1 OR \"name\" = ?2) -- bindings: [\"Milky Way\", \"Andromeda\"]\n```\n\n## Insert\n\nThe `insert(into:)` method creates an `INSERT` query builder:\n\n```swift\ntry await db.insert(into: \"galaxies\")\n    .columns(\"id\", \"name\")\n    .values(SQLLiteral.default, SQLBind(\"Milky Way\"))\n    .values(SQLLiteral.default, SQLBind(\"Andromeda\"))\n    .run()\n```\n\nThis code generates the following SQL when used with the PostgreSQL driver:\n\n```PLpgsql\nINSERT INTO \"galaxies\" (\"id\", \"name\") VALUES (DEFAULT, $1), (DEFAULT, $2) -- bindings: [\"Milky Way\", \"Andromeda\"]\n```\n\nThe insert builder also has a method for encoding a `Codable` type as a set of values:\n\n```swift\nstruct Galaxy: Codable {\n    var name: String\n}\n\ntry builder.model(Galaxy(name: \"Milky Way\"))\n```\n\nThis code generates the same SQL as would `builder.columns(\"name\").values(\"Milky Way\")`.\n\n## Update\n\nThe `update(_:)` method creates an `UPDATE` query builder:\n\n```swift\ntry await db.update(\"planets\")\n    .set(\"name\", to: \"Jupiter\")\n    .where(\"name\", .equal, \"Jupiter\")\n    .run()\n```\n\nThis code generates the following SQL when used with the MySQL driver:\n\n```mysql\nUPDATE `planets` SET `name` = ? WHERE `name` = ? -- bindings: [\"Jupiter\", \"Jupiter\"]\n```\n\nThe update builder supports the same `where()` and `orWhere()` methods as the select builder, via the `SQLPredicateBuilder` protocol.\n\n## Delete\n\nThe `delete(from:)` method creates a `DELETE` query builder:\n\n```swift\ntry await db.delete(from: \"planets\")\n    .where(\"name\", .equal, \"Jupiter\")\n    .run()\n```\n\nThis code generates the following SQL when used with the SQLite driver:\n\n```sql\nDELETE FROM \"planets\" WHERE \"name\" = ?1 -- bindings: [\"Jupiter\"]\n```\n\nThe delete builder is also an `SQLPredicateBuilder`.\n\n## Raw\n\nThe `raw(_:)` method allows passing custom SQL query strings, with support for parameterized bindings and correctly-quoted identifiers:\n\n```swift\nlet planets = try await db.raw(\"SELECT \\(SQLLiteral.all) FROM \\(ident: table) WHERE \\(ident: name) = \\(bind: \"planet\")\")\n    .all()\n```\n\nThis code generates the following SQL when used with the PostgreSQL driver:\n\n```PLpgsql\nSELECT * FROM \"planets\" WHERE \"name\" = $1 -- bindings: [\"planet\"]\n```\n\nThe `\\(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.\n\n\u003e [!IMPORTANT]\n\u003e 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 own `SQLQueryBuilder`s, rather than using raw queries, and don't hesitate to [open an issue](https://github.com/vapor/sql-kit/issues/new) to ask for additional feature support.\n","funding_links":["https://github.com/sponsors/vapor","https://opencollective.com/vapor"],"categories":["Swift"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvapor%2Fsql-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvapor%2Fsql-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvapor%2Fsql-kit/lists"}