Server-Side Swift with MongoDB: Getting Started

In this Server-Side Swift tutorial you will learn how to setup MongoDB and use MongoKitten to run basic queries, build Aggregate Pipelines and store files with GridFS. By Joannis Orlandos.

Leave a rating/review
Download materials
Save for later
Share

MongoDB is a document-oriented database server. It does not use the SQL syntax for queries and does not enforce a schema. For this reason, MongoDB classifies as a NoSQL database.

By design, MongoDB resembles how applications store information.

By the end of this tutorial, you’ll know how to set up MongoDB and run basic queries using MongoKitten in Server-Side Swift. You’ll also learn how to use GridFS and Aggregate Pipelines, two powerful features in MongoDB.

Getting Started

Use the Download Materials button at the top or the bottom of this tutorial to download the files you’ll need for this tutorial.

  • Xcode 11 and Swift 5.2.
  • Docker: If you don’t have Docker yet, visit Docker install for Mac.
  • Access to a MongoDB server: You can either set one up on your machine or make use of a cloud service such as MongoDB Atlas.
Note: You’ll need the following for this project:

The project in the starter folder uses the Swift Package Manager. It consists of a Vapor application with a Leaf website.

To begin, double-click Package.swift in the starter folder. Wait as Xcode opens the file and downloads all the project’s dependencies.

Next, expand the Sources/App folder to see the files you’ll modify for this project. Note that the project follows the standard Vapor hierarchy.

Setting Up MongoDB

As noted above, you’ll need to have access to a MongoDB server for this project. Once you do, open Terminal and navigate to the starter project’s directory. From within this directory, execute the following command to set up MongoDB.

docker-compose up

This command reads docker-compose.yaml and uses that configuration file to set up a Replica Set. A Replica Set is a group of servers that maintains the same data set. Each Replica Set member should be a different machine. The setup may take a few minutes.

The members of this replica set are three servers and one arbiter. The arbiter is necessary for the stability of a cluster, should one of the other members go down.

The three servers expose themselves at ports 27017, 27018 and 27019. The default port for MongoDB is 27017.

Connecting to MongoDB

Before creating a connection to a deployment, you need to create a connection string URI. Open another Terminal window, cd to your project folder and run the following commands:

cd # <Drag the 'starter' folder in here>
nano .env # This opens an editor

# Add the following line to this file:
MONGODB=mongodb://localhost:27017,localhost:27018,localhost:27019/socialbird

# save the file by pressing ctrl-o
# and exit using ctrl-x
Note: Be sure to include the leading dot in .env. Filenames with a leading dot may not be visible in Finder, but the Terminal command ls -a lists it.

You’ve created a file named .env to store your environment values and stored your connection string URI in the environment value MONGODB.

Piece by piece, here’s how you’ve constructed the URI:

  • To connect to the local cluster, you used a standard connection string. This format starts with mongodb://.
  • After this, you would put the relevant credentials formatted as <username>:<password>@. However, the cluster set up by Docker Compose does not use authentication.
  • Next, you added the hosts, separated by commas. All three servers expose themselves on localhost. By supplying all replica set hosts, MongoKitten can take advantage of high availability.
  • Finally, you added /socialbird to specify the selected database. A single deployment has many databases, each database serving a single application.
Note: There are two formats for a connection string. The Standard Format supports all deployments, unlike the DNS Seedlist format. DNS Seedlist formats start with mongodb+srv:// and connect to cloud hosted clusters.

Creating a Connection

Now that you’ve created the connection string, it’s time to connect your application.

First, close the project then reopen it by double-clicking Package.swift. Wait for Xcode to resolve all dependencies specified in Package.swift. After that, make sure that the selected scheme is SocialBird and the destination is My Mac.
Note the scheme and destination on the left side of the window

Next, Option + Click the scheme to edit it. In the Options tab, enable Use custom working directory. Click the folder icon to set this to the project folder of your project. This is necessary for Vapor to find your .env file and the Leaf templates used by this project.

Now open Sources/App/MongoKitten+Application.swift to see how the project uses MongoKitten with Vapor. The file contains the following code:

import Vapor
import MongoKitten

// 1
private struct MongoDBStorageKey: StorageKey {
  typealias Value = MongoDatabase
}

extension Application {
  // 2
  public var mongoDB: MongoDatabase {
    get {
      // Not having MongoDB would be a serious programming error
      // Without MongoDB, the application does not function
      // Therefore force unwrapping is used
      return storage[MongoDBStorageKey.self]!
    }
    set {
      storage[MongoDBStorageKey.self] = newValue
    }
  }
  
  // 3
  public func initializeMongoDB(connectionString: String) throws {
    self.mongoDB = try MongoDatabase.connect(connectionString, on: self.eventLoopGroup).wait()
  }
}

extension Request {
  // 4
  public var mongoDB: MongoDatabase {
    // 5
    return application.mongoDB.hopped(to: eventLoop)
  }
}

The code above:

  1. Defines a storage key associated with MongoDatabase.
  2. Adds a getter and setter on a Vapor Application to provide a MongoDB connection.
  3. Connects to MongoDB and stores the connection in the Application.
  4. Accesses the application’s MongoDB connection.
  5. Changes the connection handle to reply on the Request EventLoop. This is a critical step that, if omitted, will crash your application.

Configuring the Application

To finish setting up the connection, add the following code to App.swift above the return statement.

// 1
guard let connectionString = Environment.get("MONGODB") else {
  fatalError("No MongoDB connection string is available in .env")
}

// 2
try app.initializeMongoDB(connectionString: connectionString)

// 3
try createTestingUsers(inDatabase: app.mongoDB)

Here’s what this code does:

  1. Reads the connection string from the created .env file.
  2. Initializes the connection to MongoDB.
  3. Creates an initial dataset containing users and posts.

Your connection is now ready! Build and run and you’ll connect to MongoDB. You should see the following console output:

Server starting on http://127.0.0.1:8080

Visit the app in your web browser and you’ll see the login page.

Login page of the application. You can't login yet

Logging In

This application is already configured to handle password hashing and authorization using JWT. The entire application relies on Repository, in Repository.swift, for database operations.

Stop the app. Before you can enable logging in, you need to add the code for fetching users. Open User.swift and take note of the User type.

User has a static property containing the collection name for this model. This prevents you from mistyping the name.

_id holds the model’s identifier. MongoDB requires the use of this key. Unlike most databases, MongoDB does not support auto-incrementing for integers. The type of identifier used is ObjectId, which is both compact and scalable.

Next, you’ll see that MongoDB allows you to store information in the database exactly like your Swift structs. The profile and credentials fields contain grouped information.

Finally, the user stores an array of identifiers in following. Each identifier refers to another user that this user follows.