{"id":16683998,"url":"https://github.com/noppoman/swiftknex","last_synced_at":"2026-02-27T14:43:05.510Z","repository":{"id":63919325,"uuid":"78434862","full_name":"noppoMan/SwiftKnex","owner":"noppoMan","description":"A Mysql Native Client and Query Builder written in Pure Swift","archived":false,"fork":false,"pushed_at":"2019-09-10T08:32:31.000Z","size":111,"stargazers_count":38,"open_issues_count":11,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-04-14T06:51:43.912Z","etag":null,"topics":["expressive","json-data","mysql","mysql-protocol","pure-swift","query-builder","schema-migrations","server-side-swift"],"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/noppoMan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-09T14:12:51.000Z","updated_at":"2023-07-07T07:25:50.000Z","dependencies_parsed_at":"2023-01-14T14:00:46.357Z","dependency_job_id":null,"html_url":"https://github.com/noppoMan/SwiftKnex","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FSwiftKnex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FSwiftKnex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FSwiftKnex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FSwiftKnex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noppoMan","download_url":"https://codeload.github.com/noppoMan/SwiftKnex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221817627,"owners_count":16885580,"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":["expressive","json-data","mysql","mysql-protocol","pure-swift","query-builder","schema-migrations","server-side-swift"],"created_at":"2024-10-12T14:27:48.509Z","updated_at":"2026-02-27T14:43:05.439Z","avatar_url":"https://github.com/noppoMan.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SwiftKnex\nA Mysql Native Client and Query Builder that works on Mac and Linux.  \nThis library is powerded by [Prorsum](https://github.com/noppoman/Prorsum)\n\n**SwiftKnex is aimed to use at production services at my company**\n\n\u003cimg src=\"https://camo.githubusercontent.com/93de6573350b91e48ab570a4fe710e4e8baa38b8/687474703a2f2f696d672e736869656c64732e696f2f62616467652f73776966742d332e302d627269676874677265656e2e737667\"\u003e [\u003cimg src=\"https://travis-ci.org/noppoMan/Prorsum.svg?branch=master\"\u003e](https://travis-ci.org/noppoMan/SwiftKnex)\n\n## Features\n* [x] Pure Swift Implementation(doesn't depend on libmysqlclient)\n* [x] Expressive querying\n* [x] Supporting Database Migration and Rollback\n* [x] Supported Mysql 5.7 JSON Data Type\n* [ ] Async I/O\n\n## TODO\n* BlobType\n* GeoType\n* Investigate Performance\n* Async I/O Mode\n* Documentation\n\n## Contributing\nAll developers should feel welcome and encouraged to contribute to SwiftKnex.\n\nTo contribute a feature or idea to SwiftKnex please submit an issue! If you find bugs, of course you can create the PR(s) directory.\n\n# Query Builder Reference\n\n## Initializing the Library\n\n```swift\nlet config = KnexConfig(\n    host: \"localhost\",\n    user: \"user\",\n    database: \"my_database\",\n    isShowSQLLog: true\n)\n\nlet con = try KnexConnection(config: config)\nlet knex = con.knex()\n```\n\n# Query Builder\n\n## Select\n\n### where equal\n```swift\nlet results = try knex.table(\"users\").where(\"id\" == 1).fetch()\nprint(results)\n```\n\n### where between and limit and offset\n\nYou can chain the conditional clauses like SQL and it's amazing expressive.\n\n```swift\nlet results = try knex\n                  .table(\"users\")\n                  .where(between(\"id\", 1, 1000))\n                  .limit(10)\n                  .offset(10)\n                  .fetch()\n\nprint(result)\n```\n\n### join\n```swift\nlet results = try knex\n                  .table(\"users\")\n                  .join(\"payments\")\n                  .on(\"users.id\" == \"payment.user_id\")\n                  .where(\"users.id\" == 1)\n                  .limit(10)\n                  .offset(10)\n                  .fetch()\n\nprint(results)\n```\n\n### count\n```swift\nlet results = try knex\n                  .select(count(\"id\").as(\"count\"))\n                  .table(\"users\")\n                  .fetch()\n\nprint(results)\n```\n\n### where based on priority\n```swift\nlet results = try knex\n                    .table(\"users\")\n                    .where((\"country\" == \"Japan\" \u0026\u0026 \"age\" \u003e 20) || \"country\" == \"USA\")\n\nprint(results)\n```\n\n### Sub Query\n\n#### fetch all rows from the results that taken by subquery.\n```swift\nlet results = try knex\n                    .table(\n                        Table(\n                            QueryBuilder()\n                              .table(\"users\")\n                              .where(\"country\" == \"USA\")\n                            )\n                        ).as(\"t1\")\n                    )\n                    .where(\"t1.id\" == 1)\n\nprint(results)\n```\n\n#### Use ids taken by subquery as argument of in clause\n```swift\nlet results = try knex\n                    .table(\"users\")\n                    .where(SwiftKnex.in(\"id\",\n                        QueryBuilder()\n                          .select(col(\"id\"))\n                          .table(\"t1\")\n                          .where(\"country\" == \"USA\")\n                    )).fetch()\n\nprint(results)\n```\n\n## Operators, Conditions and Clauses\n\n### Operators\n\nSwiftKnex can evaluate comparison formula as function.\nIf you input `\"id\" == 1` in `where` clause, it is evaluated as `function`(Not BOOL) and tnrasform to SQL Comparison Literal.\n\n| SwiftKnex           |  Mysql             |\n| ------------------- |:------------------:|\n| where(\"id\" == 1)    | where `id` = 1     |\n| where(\"id\" \u003e 1)     | where `id` \u003e 1     |\n| where(\"id\" \u003e= 1)    | where `id` \u003e= 1    |\n| where(\"id\" \u003c 1)     | where `id` \u003c 1     |\n| where(\"id\" \u003c= 1)    | where `id` \u003c= 1    |\n| where(\"id\" != 1)    | where `id` != 1    |\n\n### Conditions\n\nSwiftKnex provides conditions as function. Here are list of conditions available now.\n\n* `where(_ filter: ConditionFilter)`\n  - like: `knex().where(like(\"name\", \"%a%\"))`\n  - in: `knex().where(in(\"id\", [1, 2, 3]))`\n  - notIn: `knex().where(notIn(\"id\", [1, 2, 3]))`\n  - between: `knex().where(between(\"date\", \"2017-01-01\", \"2017-01-31\"))`\n  - notBetween: `knex().where(notBetween(\"date\", \"2017-01-01\", \"2017-01-31\"))`\n  - isNull: `knex().where(isNull(\"deleted_at\"))`\n  - isNotNull: `knex().where(isNotNull(\"deleted_at\"))`\n  - raw: `knex().where(raw(\"id = ?\", [1]))`\n* `or(_ filter: ConditionFilter)`\n  - `knex().where(in(\"id\", [1, 2, 3])).or(in(\"id\", [4, 5, 6]))`\n \n \n### Clauses\n\nOf cource it supports other clauses. You can use them with `knex()`'s method chain.\n  \n* join(_ table: String): `knex().table(\"a\").join(\"b\")`\n* leftJoin(_ table: String): `knex().table(\"a\").leftJoin(\"b\")`\n* rightJoin(_ table: String): `knex().table(\"a\").rightJoin(\"b\")`\n* innerJoin(_ with table: String): `knex().table(\"a\").innerJoin(\"b\")`\n* on(_ filter: ConditionFilter): `knex().table(\"a\").join(\"b\").on(\"a.id\" == \"b.a_id\")`\n* limit(_ limit: Int): `knex().limit(10)`\n* offset(_ offset: Int): `knex().limit(10).offset(100)`\n* order(by: String, sort: OrderSort = .asc): `knex().order(by: \"created_at\", .desc)`\n* group(by name: String): `knex().group(by: \"company\")`\n* having(_ cond: ConditionFilter): `knex().group(by: \"company\").having(in(\"name\", [\"Google\", \"Apple\"]))`\n\n\n## TypeSafe Querying with Entity protocol\n\nDefine Your Entity with confirming `Entity` protocol and fetch rows as your specified type.\n\n```swift\nstruct User: Entity, Serializable {\n    let id: Int\n    let name: String\n    let email: String\n    let age: Int\n    let country: String?\n\n    init(row: Row) throws {\n        self.id = row[\"id\"] as! Int\n        self.name = row[\"name\"] as! String\n        self.email = row[\"email\"] as! String\n        self.age = row[\"age\"] as! Int\n        self.country = row[\"country\"] as? String\n    }\n\n    func serialize() throws -\u003e [String: Any] {\n        return [\n            \"name\": name,\n            \"email\": email,\n            \"age\": age,\n            \"country\": country\n        ]\n    }\n}\n\n// fetch rows as User(s)\nlet users: [User] = try! con.knex()\n                            .table(\"users\")\n                            .where(\"country\" == \"Japan\")\n                            .fetch()\n\nprint(users.first)\n\n// Insert User(should confirm Serializable)\nlet result = try! con.knex().insert(into: \"users\", values: user)\n\nprint(result?.insertId)\n```\n\n## Insert\n```swift\nlet result = try knex().insert(into: \"users\", values: [\"id\": 1, \"name\": \"bar\"])\n\nprint(result.affectedRows)\n```\n\n## Update\n```swift\nlet result = try knex().table(\"users\").where(\"id\" == 1).update(sets: [\"name\": \"foobar\"])\n\nprint(result.affectedRows)\n```\n\n## Delete\n```swift\nlet result = try knex().table(\"users\").where(\"id\" == 1).delete()\n\nprint(result.affectedRows)\n```\n\n## Transaction\n\n```swift\n\ndo {\n    try con.knex().transaction { trx in // BEGIN TRANSCTION\n        try con.knex().table(\"users\").where(\"id\" == 1).update(sets: [\"name\": \"foo\"], trx: trx)\n        try con.knex().table(\"users\").where(\"id\" == 2).update(sets: [\"name\": \"bar\"], trx: trx)\n        try con.knex().table(\"users\").where(\"id\" == 3).update(sets: [\"name\": \"foobar\"], trx: trx)\n    }\n    // COMMIT\n} catch {\n    // ROLLBACK\n}\n```\n\n# DDL\n\n# Create\n```swift\nlet create = Create(table: \"users\", fields: [\n    Schema.integer(\"id\").asPrimaryKey().asAutoIncrement(),\n    Schema.string(\"name\"),\n    Schema.string(\"email\").asUnique(),\n    Schema.datetime(\"last_logined_at\").asIndex()\n])\n.hasTimeStamps() // add created_at and updated_at\n\ntry knex().execRaw(sql: create.toDDL())\n```\n\n## Schema.Field Reference\n\n### Schema Types Comparison\n\n| SwiftKnex           |  Mysql Type    |\n| ------------------- |:--------------:|\n| Schema.string       | VARCHAR        |\n| Schema.integer      | INT            |\n| Schema.bigInteger   | BIGIMT         |\n| Schema.dateTime     | DATETIME       |\n| Schema.text         | TEXT           |\n| Schema.mediumText   | MEDIUMTEXT     |\n| Schema.float        | FLOAT          |\n| Schema.double       | DOUBLE         |\n| Schema.boolean      | TINYINT(1)     |\n| Schema.json         | JSON           |\n\n### Functions for adding field attributes\n* `default(as value: Any)`\n* `after(for name: String)`\n* `asPrimaryKey()`\n* `asAutoIncrement()`\n* `asNotNullable()`\n* `asUnsigned()`\n* `charset(_ char: Charset)`\n* `asUnique()`\n* `asIndex()`\n\n\n# Drop\n```swift\nlet drop = Drop(table: \"users\")\ntry knex().execRaw(sql: drop.toDDL())\n```\n\n# Raw\nYou can perform raw sql with SwiftKnex\n\n```swift\ntry knex().execRaw(sql: \"SELECT * from users where id = ?\", params: [1])\n```\n\n# Migration\nSwiftKnex supports database migration and rollback features.\n\n## Flows\n\n### 1. Install SwiftKnex into your project.\n\n**Package.swift**\n```swift\nimport PackageDescription\n\nlet package = Package(\n    name: \"MyApp\",\n    dependencies: [\n        .Package(url: \"https://github.com/noppoMan/SwiftKnex.git\", majorVersion: 0, minor: 1)\n    ]\n)\n```\n**run swift build**\n```\n$ swift build\n```\nand then, `SwiftKnexMigration` executable binary was created in the .build/debug directory.\n\n### 2. Create Migration file\n\nthe next step is creating migration class file into your `Sources/Migration` directory with `./build/debug/Migration create {ResourceName}`\n\nhere is an example for creating `CreateUser` migration file\n```\n.build/debug/SwiftKnexMigration create CreateUser\n\n#\n# Created /YourProject/Sources/Migration/20170116015823_CreateUser.swift\n#\n```\n\n### 3. Edit Your Migration File\n\nAfter create migration class file, edit it like following.\n\nThe created migration class has following methods.\n#### up\nPerformed on migrate:latest\n\n#### down\nPerformed on migrate:rollback\n\n```swift\nimport SwiftKnex\nimport Foundation\n\nclass Migration_20170116015823_CreateUser: Migratable {\n\n    var name: String {\n        return \"\\(Mirror(reflecting: self).subjectType)\"\n    }\n\n    func up(_ migrator: Migrator) throws {\n       let create = Create(\n           table: \"users\",\n           fields: [\n               Schema.integer(\"id\").asPrimaryKey().asAutoIncrement(),\n               Schema.string(\"name\").asIndex().asNotNullable(),\n               Schema.string(\"email\").asUnique().asNotNullable()\n           ])\n           .hasTimestamps()\n           .index(columns: [\"name\", \"email\"], unique: true)\n\n       try migrator.run(create)\n    }\n\n    func down(_ migrator: Migrator) throws {\n        try migrator.run(Drop(table: \"users\"))\n    }\n}\n```\n\n\n### 4. Create `main.swift` in the `{$PROJ}/Sources/Migration`\n\nCreate `main.swift` in the `{$PROJ}/Sources/Migration` directory that is created by `Migrate create` at previous section.  \nAnd copy/paste the following code into your `{$PROJ}/Sources/Migration/main.swift` and then, replace the class names in the `knexMigrations` array to correct names, and change the database configuration depending on your environment.\n\nYou need to add the new class name(s) to the `knexMigrations` at every migration resource created.\n\n**main.swif example**\n```swift\nimport SwiftKnex\n\nlet knexMigrations: [Migratable] = [\n    Migration_20170116015823_CreateUser()\n]\n\nlet config = KnexConfig(\n   host: \"localhost\",\n   user: \"root\",\n   database: \"swift_knex_test\"\n)\n\ntry Migrator.run(config: config, arguments: CommandLine.arguments, knexMigrations: knexMigrations)\n```\n\nafter editing main.swift, run `swift build`\n```\nswift build\n```\n\n### 5. Perform Migration and Rollback\nAfter that, you only need to run the migration\n\n**Current supporting commands are**\n* `migrate:latest`:  Perform to migrate recent unmigrated files.\n* `migrate:rollback`: Rollback the migrations recent performed.(The rollback unit is grouped by `batch` number)\n\n#### Try to perform Migration\n```\n.build/debug/Migration migrate:latest\n```\n\n#### Try to perform Rollback\n```\n.build/debug/Migration migrate:rollback\n```\n\n#### Seed\nTODO\n\n# Working with Prorsum\nGo Style async query performing and syncronization with Prorsum\n\n```swift\nimport Prorsum\n\nlet chan = Channel\u003cResultSet\u003e.make()\n\ngo {\n    let rows = try! knex().table(\"users\").where(\"id\" == 1).fetch()\n    try chan.send(rows!)\n}\n\ngo {\n    let rows = try! knex().table(\"users\").where(\"id\" == 2).fetch()\n    try chan.send(rows!)\n}\n\nprint(try! chan.receive())\nprint(try! chan.receive())\n```\n\n# Mysql Library\nThe base Connection and Querying library that used in SwiftKnex.\n\nTODO\n\n## License\nProrsum is released under the MIT license. See LICENSE for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoppoman%2Fswiftknex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoppoman%2Fswiftknex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoppoman%2Fswiftknex/lists"}