Swiftpack.co - Package - OpenKitten/MongoKitten



Installation | Tutorial | Basic usage | About BSON | Codable | Community | How to help

A fast, pure swift MongoDB driver based on Swift NIO built for Server Side Swift. It features a great API and a battle-tested core. Supporting both MongoDB in server and embedded environments.

⭐️ Please leave a star to support MongoKitten – it really helps!

🐈 Community

Join our slack here and become a part of the welcoming community. Or join discord if you prefer that. Slack is by far the biggest community.


A couple of MongoKitten based projects have arisen, check them out!

🤝 How to help

Support MongoKitten development

You can sponsor us via GitHub.. This enables us to provide a higher quality and more documentation as well as building more tools.


The App

MongoKitten App can help you browse your dataset, support customers and debug complex aggregates.

The Company

Autimatisering is the company currently backing MongoKitten. Hire us

Contribute to MongoKitten

  • Donate so that we can spend more time on improving the docs.
  • See CONTRIBUTING.md for info on contributing to MongoKitten
  • You can help us out by resolving TODOs and replying on issues
  • Of course, all feedback, positive and negative, also really helps to improve the project

🕶 Installation

Set up MongoDB server

If you haven't already, you should set up a MongoDB server to get started with MongoKitten

For development, this can be on your local machine.

Install MongoDB for Ubuntu, macOS or any other supported Linux Distro.

Alternatively, make use of a DAAS (Database-as-a-service) like MongoDB Atlas, MLab, IBM Cloud or any other of the many services.

Add MongoKitten to your Swift project 🚀

If you're using a SwiftNIO 1.x framework such as Vapor 3, use MongoKitten 5 instead.

MongoKitten supports the Swift Package Manager for server-side applications. Add MongoKitten to your dependencies in your Package.swift file:

.package(url: "https://github.com/OpenKitten/MongoKitten.git", from: "6.0.0")

Also, don't forget to add "MongoKitten" as a dependency for your target.

🚲 Basic usage

Check out my Ray Wenderlich Article to learn the basics!

Connect to your database

import MongoKitten

let db = try MongoDatabase.synchronousConnect("mongodb://localhost/my_database")

Vapor users should register the database as a service.

extension Request {
    public var mongoDB: MongoDatabase {
        return application.mongoDB.hopped(to: eventLoop)
    // For Meow users only
    public var meow: MeowDatabase {
        return MeowDatabase(mongoDB)
    // For Meow users only
    public func meow<M: ReadableModel>(_ type: M.Type) -> MeowCollection<M> {
        return meow[type]

private struct MongoDBStorageKey: StorageKey {
    typealias Value = MongoDatabase

extension Application {
    public var mongoDB: MongoDatabase {
        get {
        set {
            storage[MongoDBStorageKey.self] = newValue
    // For Meow users only
    public var meow: MeowDatabase {
    public func initializeMongoDB(connectionString: String) throws {
        self.mongoDB = try MongoDatabase.lazyConnect(connectionString, on: self.eventLoopGroup)

And make sure to call app.initializeMongoDB!

NIO Futures

MongoKitten relies on Swift NIO to provide support for asynchronous operations. All MongoKitten operations that talk to the server are asynchronous, and return an EventLoopFuture of some kind.

You can learn all about NIO by reading its readme or the article on RayWenderlich.com, but here are the basics:

Asynchronous operations return a future. NIO implements futures in the EventLoopFuture<T> type. An EventLoopFuture is a holder for a result that will be provided later. The result of the future can either be successful yielding a result of T, or unsuccessful with a result of a Swift Error. This is the asynchronous representation of a successful return or a thrown error.

If you're using Vapor 4, please refer to their Async documentation. Vapor's Async module provides additional helpers on top of NIO, that make working with instances of EventLoopFuture<T> easier.

If you use Vapor or another Swift-NIO based web framework, never use the wait() function on EventLoopFuture instances.

CRUD (Create, Read, Update, Delete)

// The collection "users" in your database
let users = db["users"]

Create (insert)

let myUser: Document = ["username": "kitty", "password": "meow"]

let future: Future<InsertReply> = users.insert(myUser)

future.whenSuccess { _ in

future.whenFailure { error in
	print("Insertion failed", error)

Read (find) and the query builder

To perform the following query in MongoDB:

	"username": "kitty"

Use the following MongoKitten code:

users.findOne("username" == "kitty").whenSuccess { user: Document? in
	// Do something with kitty

To perform the following query in MongoDB:

	"$or": [
		{ "age": { "$lte": 16 } },
		{ "age": { "$exists": false } }

Use the following MongoKitten code:

users.find("age" <= 16 || "age" == nil).forEach { user: Document in
	// Print the user's name
	print(user["username"] as? String)

You can also type out the queries yourself, without using the query builder, like this:

users.findOne(["username": "kitty"])


Find operations return a Cursor. A cursor is a pointer to the result set of a query. You can obtain the results from a cursor by iterating over the results, or by fetching one or all of the results.

Fetching results

You can fetch all results as an array:

let results: EventLoopFuture<[Document]> = users.find().getAllResults()

Note that this is potentially dangerous with very large result sets. Only use getAllResults() when you are sure that the entire result set of your query fits comfortably in memory.

Iterating over results

For more efficient handling of results, you can lazily iterate over a cursor:

let doneIterating: EventLoopFuture<Void> = users.find().forEach { user: Document in
	// ...
Cursors are generic

Find operations return a FindCursor<Document>. As you can see, FindCursor is a generic type. You can lazily transform the cursor into a different result type by using map, which works similar to map on arrays or documents:

	.map { document in
		return document["username"] as? String
	.forEach { username: String? in
		print("user: \(username)")


users.updateMany(where: "username" == "kitty", setting: ["age": 3]).whenSuccess { _ in


users.deleteOne(where: "username" == "kitty").whenSuccess { amountDeleted in
	print("Deleted \(amountDeleted) kitties 😿")

📦 About BSON & Documents

MongoDB is a document database that uses BSON under the hood to store JSON-like data. MongoKitten implements the BSON specification in its companion project, OpenKitten/BSON. You can find out more about our BSON implementation in the separate BSON repository, but here are the basics:


You normally create BSON Documents like this:

let documentA: Document = ["_id": ObjectId(), "username": "kitty", "password": "meow"]
let documentB: Document = ["kitty", 4]

From the example above, we can learn a few things:

  • A BSON document can represent an array or a dictionary
  • You can initialize a document like you initialize normal dictionaries and arrays, using literals
  • The values in a Document (either the array elements or the values of a dictionary pair) can be of any BSON primitive type
  • BSON primitives include core Swift types like Int, String, Double and Bool, as well as Date from Foundation
  • BSON also features some unique types, like ObjectId

Just another collection

Like normal arrays and dictionaries, Document conforms to the Collection protocol. Because of this, you can often directly work with your Document, using the APIs you already know from Array and Dictionary. For example, you can iterate over a document using a for loop:

for (key, value) in documentA {
	// ...

for value in documentB.values {
	// ...

Document also provides subscripts to access individual elements. The subscripts return values of the type Primitive?, so you probably need to cast them using as? before using them.

let username = documentA["username"] as? String

Think twice before converting between Document and Dictionary

Our Document type is implemented in an optimized, efficient way and provides many useful features to read and manipulate data, including features not present on the Swift Dictionary type. On top of that, Document also implements most APIs present on Dictionary, so there is very little learning curve.

💾 Codable

MongoKitten supports the Encodable and Decodable (Codable) protocols by providing the BSONEncoder and BSONDecoder types. Working with our encoders and decoders is very similar to working with the Foundation JSONEncoder and JSONDecoder classes, with the difference being that BSONEncoder produces instances of Document and BSONDecoder accepts instances of Document, instead of Data.

For example, say we want to code the following struct:

struct User: Codable {
	var profile: Profile?
	var username: String
	var password: String
	var age: Int?
	struct Profile: Codable {
		var profilePicture: Data?
		var firstName: String
		var lastName: String

We can encode and decode instances like this:

let user: User = ...

let encoder = BSONEncoder()
let encoded: Document = try encoder.encode(user)

let decoder = BSONDecoder()
let decoded: User = try decoder.decode(User.self, from: encoded)

A few notes:

  • BSONEncoder and BSONDecoder work very similar to other encoders and decoders
  • Nested types can also be encoded and are encouraged
    • Nested structs and classes are most often encoded as embedded documents
  • You can customize the representations using encoding/decoding strategies

Codable and cursors

When doing a find query, the Cursor's results can be transformed lazily. Lazy mapping is much more efficient than keeping the entire result set in memory as it allows for forEach- loops to be leveraged efficiently reducing the memory pressure of your application. You can leverage cursors using Codable as well.

// Find all and decode each Document lazily as a `User` type
users.find().decode(User.self).forEach { user in

☠️ License

MongoKitten is licensed under the MIT license.


Stars: 596

Used By

Total: 0


6.6.3 - 2020-08-25 12:41:15

  1. This release contains a bugfix in a helper that would cause findAndRemove operations to result in a failure.
  2. A lot of new tests have been added in this release
  3. A low-level optimisation has been added, which will result in a lower performance for each message sent to MongoDB.


While updating MongoKitten is great, we've also made a huge step in the BSON library. The new BSON release has seen improved performance up to 150x faster in regular usage.

MongoKitten 6.6.2 - 2020-08-08 17:25:37

Disables support for NIOTransportServices on macOS Big Sur that fixes a bug that caused connection issues.

Added support for `$geoNear` aggregate stage. - 2020-06-19 16:20:25

Adds support for $geoNear aggregate stage. #242

Thread Sanitiser Fixes on Cluster Disconnect - 2020-06-03 18:42:47

Before this release, there was a small chance that disconnecting the cluster would crash because of a simultaneous access with cluster discovery.

Thread Sanitiser Fixes - 2020-05-30 17:36:04

Before this release, there was a small chance that a lot of parallel queries would cause a race condition. That's fixed now!

Create Index improvements - 2020-05-04 22:25:22

This release adds support for creating more customized indexes for improved flexibility.

List Indexes Support - 2020-05-03 18:12:19

This release adds support for the listIndexes command.

Sample Aggregate Stage Bugfix - 2020-04-26 12:08:03

FindAndModify support - 2020-04-26 12:07:50

Match aggregate stage helper - 2020-04-26 12:07:34

GridFS helpers - 2020-04-26 12:06:59

- 2020-04-26 12:06:50

6.2.0 - 2020-03-11 11:57:46

After removing the broken implementation for ChangeStreams, 6.2.0 supports change streams again.

6.1.0 - 2020-03-06 10:14:10

Exposed the GridFS file writer and added support for swift-metrics 2.x

6.0.3 - 2019-11-20 09:34:57

  • Actually released the code meant for 6.0.2
  • Updated Meow to support models as readable or mutable, so that you can have a read-only interface into your models
  • Support hopping EventLoops on the MongoDatabase and MongoCollection types

6.0.2 - 2019-11-11 08:36:05

Fix upsert queries not encoding the upsert property

6.0.0 - 2019-08-18 08:08:25

~MongoKitten 6 is now in beta and has a tag because a lot of people were pinning on the branch.~ MongoKitten 6 is stable

5.1.11 - 2019-04-15 14:31:53

  • Better support for non-SPM macOS targets

5.1.10 - 2019-04-08 13:08:54

  • Fixed a set of warnings

5.1.9 - 2019-04-08 10:18:08

  • Support iOS

5.1.8 - 2019-03-21 11:48:03

  • Fixed a bug that prevented GridFS file upload chunking from working

5.1.7 - 2019-03-18 17:02:28

  • Support a raw ConnectionSettings type for connecting synchronously

5.1.6 - 2019-03-14 16:50:57

  • Added the ability to create 2dsphere indexes

5.1.5 - 2019-03-06 17:22:13

  • Add the fallback to automatic mechanism selection with no server preference

5.1.4 - 2019-02-28 20:28:36

  • Fixed the fix that didn't completely fix the last bug
  • Also fixed another bug in the FindCursor that was doing too many cursor requests if the cursor was already drained the first time

5.1.3 - 2019-02-28 16:48:58


  • Fixed a bug where the wrong requestID was used on a cluster which would occur when requests occurred rapidly to the same host


  • Support iOS 11

5.1.2 - 2019-02-25 20:44:29

No changes for SPM users

  • Initial beta support for MongoKitten mobile through Cocoapods

5.1.1 - 2019-02-21 13:31:27

  • Fixed lazyConnect getting stuck in a wait loop

MongoKitten 5.1 - 2019-02-05 17:38:34

This update we're bringing you a new and often requested feature of lazily connecting. It's not the same as synchronous connect, since it does still connect asynchronously. But lazy connect will only start a MongoDB connection once the first query is being sent to MongoKitten. This way you can use an initializer like you could in MongoKitten 4, but with the performance and NIO-ness of MongoKitten 5.

let database = try Database.lazyConnect("mongodb://localhost", on: eventLoop)

Next, we're adding transactions to MongoKitten.

let transactionDB: TransactionDatabase = try database.startTransaction(with: SessionOptions())
let kittens: TransactionCollection = transactionDB["kittens"]
try kittens.insert(["name": "Mongo"]).then { insert in
  return kittens.commit()

kittens is a normal MongoKitten.Collection (subclass), but not all operations may be allowed within a transaction as they are within a normal collection. Also; don't use the TransactionCollection after your transaction is completed/aborted. They're not a replacement for Collection.

You can use a TransactionDatabase like a normal Database, but only for the duration of the transaction and for queries that are part of a transaction.

Finally we've added listDatabases() on top of Cluster..

try database.cluster.listDatabases()

..and we've added listCollections again

try database.listCollections()

MongoKitten 5 🎉 - 2019-01-13 01:15:49

After months of hard work & testing, we (I and @Joannis) are proud to release MongoKitten 5 (the 150th MongoKitten release!).

MongoKitten 5, together with OpenKitten BSON 6, is a complete rewrite of the MongoKitten project. Because it is based on Swift NIO, it integrates perfectly in modern Swift frameworks.

Some highlights include:

  • Streamlined collection APIs
  • Even better performance
  • Fully asynchronous through the core
  • Improved Codable support
  • Support for Change Streams
  • Fully redesigned aggregate pipeline builder
  • Better, more flexible cursors
  • Support for SCRAM SHA256 authentication
  • Mature cluster support through SDAM
  • Support for the new MongoDB Wire Protocol

And a big thank you to you, the community, for helping MongoKitten get where we stand today.

If you have any thoughts, questions or other feedback about MongoKitten 5, please feel free to raise an issue or contact @Joannis or @Obbut via our Slack.