· 8 min read

Getting Started with MongoDB in Swift using MongoKitten

MongoDB is a popular NoSQL database that stores data in flexible, JSON-like documents. MongoKitten provides a Swift-native way to interact with MongoDB, complete with type-safe queries and Codable support.

Learn how to integrate MongoDB with your Swift application using MongoKitten. This tutorial shows you how to set up a connection, work with BSON data, and perform basic database operations.

Setting Up MongoDB

The quickest way to get started with MongoDB is using Docker. Run this command in your terminal:

docker run --name mongodb -d -p 27017:27017 mongo:latest

Adding MongoKitten to Your Project

First, add MongoKitten to your package dependencies:

// Package.swift
let package = Package(
    name: "MyApp",
    dependencies: [
        .package(url: "https://github.com/orlandos-nl/MongoKitten.git", from: "7.0.0")
    ],
    targets: [
        .target(
            name: "MyApp",
            dependencies: [
                .product(name: "MongoKitten", package: "MongoKitten")
            ]
        )
    ]
)

Connecting to MongoDB

Let’s create our first connection:

let db = try await MongoDatabase.connect(to: "mongodb://localhost:27017/social_network")
print("Connected to MongoDB!")
mongokitten-basics.swift:4

Defining Our Models

For our social network, we’ll create a Post model using the Codable protocol:

struct Post: Codable {
    let _id: ObjectId
    let author: String
    let content: String
    let createdAt: Date
    
    init(author: String, content: String) {
        self._id = ObjectId()
        self.author = author
        self.content = content
        self.createdAt = Date()
    }
}
mongokitten-basics.swift:9

The ObjectId type is MongoDB’s native unique identifier, similar to a UUID. Every document in MongoDB has an _id field, which must be unique within a collection.

Creating Posts

Let’s create a function to save posts:

func createPost(author: String, content: String) async throws {
    // 1. Create the post
    let post = Post(author: author, content: content)
    
    // 2. Get the posts collection
    let posts = db["posts"]
    
    // 3. Insert the post
    try await posts.insertEncoded(post)
}
mongokitten-basics.swift:25
  1. Create the post as a regular Swift struct

  2. Access the posts MongoCollection, this is similar to a table in a relational database

  3. Call the MongoCollection.insertEncoded(_:writeConcern:) method to insert the post into the database. This method automatically converts the object to BSON and inserts it into the database.

In BSON, all entities are stored as ‘documents’.

Reading Posts

To read posts, we can use MongoKitten’s query API:

// 1. Find all posts
let allPosts = try await db["posts"]
    .find()
    .decode(Post.self)
    .drain()
mongokitten-basics.swift:38

All the methods are chainable, and modify the query. MongoKitten will only execute the query when you call MongoCursor.drain, or iterate over the results of any query.

Before draining the query, you can also call QueryCursor.decode(_:using:) to decode the results into a specific type. This takes the database rows (documents) and decodes them into the specified Decodable type.

The drain function will execute the query and return the results as an array of the specified type.

Filtering Results

MongoKitten supports filtering and sorting on most queries.

// 2. Find posts by a specific author
let authorPosts = try await db["posts"]
    .find("author" == "swift_developer")
    .decode(Post.self)
    .drain()
mongokitten-basics.swift:46

The MongoCollection.find(_:) method returns a FindQueryBuilder, a type of MongoCursor that allows you to chain more methods.

Sorting and Limiting

// 3. Find recent posts, sorted by creation date
let recentPosts = try await db["posts"]
    .find()
    .sort(["createdAt": .descending])
    .limit(10)
    .decode(Post.self)
    .drain()
mongokitten-basics.swift:54

The find method accepts one argument, a filter. By providing the find filter, MongoDB will only return documents that match the filter.

Then, chain the following methods:

Understanding BSON

MongoDB, and by extension MongoKitten, uses BSON (Binary JSON) as its native data format. While MongoKitten handles most BSON conversions automatically through Codable, you can also work with BSON directly:

struct Person: Codable {
    let name: String
    let age: Int
    let tags: [String]
    let active: Bool
}

// Creating a BSON Document manually
let document: Document = [
    "name": "Swift Developer",
    "age": 25,
    "tags": ["swift", "mongodb", "backend"] as Document,
    "active": true
]

// Converting between BSON and Codable
let bsonDocument = try BSONEncoder().encode(document)
let decodedPerson = try BSONDecoder().decode(Person.self, from: bsonDocument)
mongokitten-basics.swift:64

Next Steps

You’ve learned the basics of working with MongoDB using MongoKitten! Here’s what you can explore next:

  • Advanced queries and aggregations

  • Indexing for better performance

  • Working with multiple collections

  • Implementing authentication and authorization

  • Handling relationships between documents

Resources

Related posts

· 8 min read

Realtime MongoDB Updates with ChangeStreams and WebSockets

Learn how to implement real-time updates over WebSockets using ChangeStreams and MongoKitten

SwiftCraft logo

SwiftCraft

SwiftCraft is a UK based Swift Conference, 19th-21st May 2025!
There'll be talks, workshops, and a lot of Swift (Server) fun!

Get Tickets
class MongoDatabase

A reference to a MongoDB database, over a MongoConnectionPool.

static func connect(to uri: String, logger: Logger = Logger(label: "org.orlandos-nl.mongokitten")) async throws -> MongoDatabase

Connect to a MongoDB database using a connection string

func print(_ items: Any..., separator: String = " ", terminator: String = "\n")

Writes the textual representations of the given items into the standard output.

typealias Codable = Decodable & Encodable

A type that can convert itself into and out of an external representation.

struct ObjectId

A BSON ObjectId. This can represent a 12-byte value consisting of a 4-byte timestamp, a 3-byte machine identifier, a 2-byte process id, and a 3-byte counter. The timestamp is the number of seconds since the Unix epoch. The machine identifier, process id, and counter are random values in new ObjectId values. See https://docs.mongodb.com/manual/reference/method/ObjectId/

@frozen struct String

A Unicode string value that is a collection of characters.

struct Date

Date represents a single point in time.

struct UUID

Represents UUID strings, which can be used to uniquely identify types, interfaces, and other items.

subscript(collection: String) -> MongoCollection { get }

Get a collection in this database

@discardableResult func insertEncoded<E>(_ model: E, writeConcern: WriteConcern? = nil) async throws -> InsertReply where E : Encodable

Creates a new document in this collection with the given model encoded to a BSON Document.

final class MongoCollection

A reference to a collection in a MongoDatabase.

func find(_ query: Document = [:]) -> FindQueryBuilder

Finds documents in this collection matching the given query. If no query is given, it returns all documents in the collection.

func decode<D>(_ type: D.Type, using decoder: BSONDecoder = BSONDecoder()) -> MappedCursor<Self, D> where D : Decodable

Generates a MappedCursor with decoded instances of D as its element type.

func drain(failable: Bool = false) async throws -> [Element]

Executes the cursor and returns all results as an array.

func drain() async throws -> [Document]

Collects all results into an array, until the cursor is drained

final class MongoCursor

A cursor returned from a query, used to iterate over the results. Used in find, aggregate, and listCollections. This is a reference type, and should be used as such.

protocol QueryCursor : Sendable

A cursor with results from a query. Implemented by FindCursor and AggregateCursor.

protocol Decodable

A type that can decode itself from an external representation.

func == (lhs: String, rhs: Primitive?) -> Document
func find<Query>(_ query: Query) -> FindQueryBuilder where Query : MongoKittenQuery

Finds documents in this collection matching the given query. If no query is given, it returns all documents in the collection.

final class FindQueryBuilder

A builder that constructs a FindCommand, created by running find(_:)

func sort(_ sort: Sorting) -> FindQueryBuilder

Sorts the documents returned by this cursor

case descending
func limit(_ limit: Int) -> FindQueryBuilder

Limits the amount of documents returned by this cursor

import BSON
@frozen struct Int

A signed integer value type.

@frozen struct Bool

A value type whose instances are either true or false.

struct Document
final class BSONEncoder

An object that encodes instances of Encodable types as BSON Documents.

func encode(_ value: Encodable) throws -> Document

Returns the BSON-encoded representation of the value you supply

struct BSONDecoder

A helper that is able to decode BSON data types into a Decodable type.

func decode<D>(_ type: D.Type, from document: Document) throws -> D where D : Decodable
import MongoKitten