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
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Suggesting Users

The homepage only shows the posts by Ray Wenderlich and yourself. That happens because you’re only following Ray Wenderlich. To discover other users, you’ll create another pipeline in findSuggestedUsers(forUser:inDatabase:).

This pipeline will fill the sidebar with users you’re not following yet. To do this, you’ll create a filter that only shows people you’re not following. You’ll use the same filter as above, but reversed. Replace the return statement in findSuggestedUsers(forUser:inDatabase:) with the following:

let users = database[User.collection] // 1
return findUser(byId: userId, inDatabase: database).flatMap { user in // 2
  
  // 3
  var feedUserIds = user.following
  feedUserIds.append(userId)
  
  // 4
  let otherUsersQuery: Document = [
    "_id": [
      "$nin": feedUserIds
    ]
  ]
  
  // Continue writing here
  
}

In the code above, you:

  1. Get users collection.
  2. Find user by userId.
  3. List the user identifiers whose profiles are not shown.
  4. Construct a filter that looks for users where their identifier is Not IN the array.

If you use a simple filter, users will always see the same suggestions. But you’ll do better than that! Instead of a simple filter, you’ll suggest users based on the people you’re following, using a Graph Lookup. This works in the same way as a lookup stage, but recursively.

While a graph lookup provides more relevant results, it doesn’t show suggestions when a user doesn’t follow anyone. Therefore, use a sample stage instead. sample(_:) creates a random set of entities from the input results.

Add this code below the Continue writing here comment:

return users.buildAggregate {
  match(otherUsersQuery) // 1
  sample(5) // 2
  sort([
    "profile.firstName": .ascending,
    "profile.lastName": .ascending
  ]) // 3
}.decode(User.self).allResults() // 4

In this code, you:

  1. Find all users excluding yourself and those you’re following.
  2. Select up to five random users.
  3. Sort them by firstName, then lastName.
  4. Decode the results as a User and return all results.

Build and run the application, reload the page and ta-da! Your sidebar now contains users that you can follow.

Sidebar has users you can follow.

Following Users

To make the follow button work, replace the return statement in followUser with the following:

return database[User.collection].updateOne( // 1
  where: "_id" == follower._id, // 2
  to: [
    "$push": [
      "following": account._id
    ]
  ] // 3
).map { _ in }

In this code, you’re:

  1. Getting the users collection.
  2. Specifying which user to update.
  3. Updating the user.

Creating Posts

Now that you’re able to see posts, it’s time to create some of your own!

To create a post, you’ll change createPost. In MongoDB, the insert operation creates new entities in a collection. MongoKitten even provides a helper for encodable types such as TimelinePost.

Replace the return statement in createPost(_:inDatabase:) with the following, to add the post to its collection:

return database[TimelinePost.collection].insertEncoded(post).map { _ in }

This line will insert the new, encoded post into the timelineposts collection.
Create a post and refresh the website. Your new post will be on the top! But you’re not done yet.

As you might have noticed, users can upload images as part of their posts. Any service should store the files on the disk. However, this can be a complex task if you want to do it right. You’ll need to take care of access control and high availability, to name a few.

MongoDB already supports high availability and replication. And the code that calls MongoDB already makes access checks. To take advantage of this, you’ll use GridFS. GridFS is a standard that can store small files into MongoDB. Preferably you’ll keep the files as small as possible. It’s great for storing images or PDF invoices.

To use GridFS, you’ll need to create a GridFSBucket. You can then upload or download files from this bucket. To allow uploading files, replace the return statement in uploadFile(_:inDatabase:) with the following:

let id = ObjectId() // 1
let gridFS = GridFSBucket(in: database) // 2
return gridFS.upload(file, id: id).map { // 3
  return id // 4
}

In the code above, you:

  1. Generate the file’s identifier.
  2. Open the GridFS bucket in the selected database.
  3. Upload the user’s file to GridFS.
  4. Return the generated identifier.

The first post!

Reading Uploaded Files

To share the file with users, the repository uses readFile(byId:inDatabase:). Like the example above, you’ll access file storage through a GridFS bucket. But instead of uploading a file, you’ll read the contents instead.

First, you must fetch the file from the GridFSBucket. The fetched GridFSFile does not contain the contents of the file data. Instead, it contains all metadata related to this file. This includes the filename, size and any custom data.

After fetching the GridFSFile, read the file’s contents using the file’s reader. Replace the return statement in readFile(byId:inDatabase:) with the following:

let gridFS = GridFSBucket(in: database) // 1

return gridFS.findFile(byId: id) // 2
  .flatMap { file in
    guard let file = file else { // 3
      return database.eventLoop.makeFailedFuture(Abort(.notFound))
    }
    
    return file.reader.readData() // 4
}

In the code above, you:

  1. Get the Bucket
  2. Find file by Id
  3. Unwrap file, else throw an error
  4. Read data and return

Build and restart the application. Then, create a new post and don’t forget to attach an image. You’ll see your new post in the timeline, including the image.

Congratulations! You’ve completed your first app with MongoDB and Server-Side Swift.

Where to Go From Here?

You can download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.

MongoDB has a lot of powerful features to offer. If you’re looking to expand upon those, I recommend reading the official documentation at mongodb.com.

If you want to learn more about developing web services in Server-Side Swift, our Server-Side Swift With Vapor book is a good starting place.

MongoDB is a powerful database. Are you excited to explore it more? Feel free to share your thoughts on MongoDB and MongoKitten in the discussion below!