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.
 
  π
   Community & Docs
 
Join our Discord for any questions and friendly banter.
Read the Docs at our sponsor's website.
Projects
A couple of MongoKitten based projects have arisen, check them out!
  π€
   How to help
 
Support MongoKitten development
You can sponsor the creator via GitHub.. This enables us to provide a higher quality and more documentation as well as building more tools.
Backers
The App
MongoKitten App can help you browse your dataset, support customers and debug complex aggregates.
The Company
MongoKitten is developed by Orlandos. 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.
FAQ
I can't connect to MongoDB, authentication fails!
- Make sure you've specified 
authSource=admin, unless you know what your authSource is. MongoDB's default value is really confusing. - If you've specified an 
authMechanism, try removing it. MongoKitten can detect the correct one automatically. 
  π²
   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 {
        return meow[type]
    }
}
private struct MongoDBStorageKey: StorageKey {
    typealias Value = MongoDatabase
}
extension Application {
    public var mongoDB: MongoDatabase {
        get {
            storage[MongoDBStorageKey.self]!
        }
        set {
            storage[MongoDBStorageKey.self] = newValue
        }
    }
    
    // For Meow users only
    public var meow: MeowDatabase {
        MeowDatabase(mongoDB)
    }
    
    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 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 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: EventLoopFuture = users.insert(myUser)
future.whenSuccess { _ in
	print("Inserted!")
}
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"])
Cursors
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. 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:
users.find()
	.map { document in
		return document["username"] as? String
	}
	.forEach { username: String? in
		print("user: \(username)")
	}
Update
users.updateMany(where: "username" == "kitty", setting: ["age": 3]).whenSuccess { _ in
	print("π")
}
Delete
users.deleteOne(where: "username" == "kitty").whenSuccess { reply in
	print("Deleted \(reply.deletes) 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:
Literals
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,DoubleandBool, as well asDatefrom 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:
BSONEncoderandBSONDecoderwork 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
	print(user.username)
}
  β οΈ
   License
 
MongoKitten is licensed under the MIT license.
