Getting Started with Server-Side Swift with Vapor 4

Get started quickly with Server-side Swift using Vapor and build your first Vapor web app! By Tim Condon.

Leave a rating/review
Download materials
Save for later
Share

The dream of using the same language on the server that you use in your iOS apps is calling out to you! But maybe you’ve never done server-side work before? And you already know Swift? Good, you are in the right place :]

Beginning a project in a new domain and using a new technology can be daunting, but Vapor makes it easy to get started with server-side Swift. Vapor provides a handy command line tool to create a starter project for you.

In this tutorial, you’ll start by installing the Vapor Toolbox, then use it to build and run your first project. You’ll finish by learning about routing, accepting data and returning JSON.

Getting Started

The first step in beginning Vapor development is to install the Vapor Toolbox.

The Vapor Toolbox is a command line interface (CLI) tool you use when developing Vapor apps. It helps you create a new Vapor project from a template and allows to add dependencies as needed.

Before installing the toolbox, make ensure your system has Swift installed. On macOS, simply install Xcode from the Mac App Store. On Linux, download it from https://www.swift.org and install as described below.

Note: Vapor 4 requires Swift 5.2, both in Xcode and from the command line. Xcode 11.4 and 11.5 both provide Swift 5.2.

Installing on macOS

Vapor uses Homebrew to install the Toolbox.

Note: If you don’t have Homebrew 2 installed, visit https://brew.sh and run the installation command.

In Terminal run the following commands:

brew install vapor

To make sure Vapor is correctly installed run

vapor

and you should the following output:

> vapor
Usage: vapor <command>

Vapor Toolbox (Server-side Swift web framework)

Commands:
       build Builds an app in the console.
       clean Cleans temporary files.
      heroku Commands for working with Heroku
         new Generates a new app.
         run Runs an app from the console.
  supervisor Commands for working with Supervisord
       xcode Opens an app in Xcode.

Use `vapor <command> [--help,-h]` for more information on a command.
Error: Error: Missing command
>

That means Vapor is ready to rock :]

Installing on Linux

Everything you build with Vapor will work on versions of Linux that Swift supports. The Vapor Toolbox works in exactly the same way, with the exception that you can’t use Xcode on Linux.

Installing Swift

To install Swift on Linux, go to https://swift.org/download/ and download the toolchain for your operating system. Follow the installation to install the toolchain on your machine. When complete, enter the following at a shell prompt:

swift --version

You should get the correct version of Swift returned:

Swift version 5.2.4 (swift-5.2.4-RELEASE)
Target: x86_64-unknown-linux-gnu

Installing Vapor

In your console, run the following commands:

# 1
git clone https://github.com/vapor/toolbox.git
# 2
cd toolbox
# 3
git checkout 18.0.0
# 4
swift build -c release --disable-sandbox --enable-test-discovery
# 5
mv .build/release/vapor /usr/local/bin

Here’s what this does:

  1. Clone the toolbox from GitHub.
  2. Navigate into the toolbox directory that you cloned.
  3. Check out version 18.0.0. You can find the latest release of the toolbox on the releases page on GitHub.
  4. Build the toolbox in release mode. --disable-sandbox allows the toolbox to execute other processes.
  5. Move the toolbox into your local path so you can call it from anywhere. (You might need to prefix this command with sudo on some Linux distributions.)

This article uses Ubuntu 20.04 throughout when referring to Linux, but the other supported versions of Linux should work in exactly the same way.

Building Your First App

Setting up a Vapor project can seem complicated at first as there are a number of required files and directories. To help with this, the Toolbox can create a new project from a template. It can generate projects for a simple API, websites and authentication. You can even create your own templates.

First, create a new directory in your home directory or somewhere sensible to work on your Vapor projects. For example, enter the following commands in Terminal:

mkdir ~/vapor
cd ~/vapor

This creates a new directory in your home folder called vapor and navigates you there. Next, create your project with:

vapor new HelloVapor

The toolbox then asks if you’d like to use Fluent. For now, type n followed by Enter. You’ll learn about Fluent in a future tutorial. The toolbox then generates your project for you.

You should see the following:

New ~Vapor Project

To build and start your app, run:

# 1
cd HelloVapor
# 2
swift run

Here’s what this does:

  1. cd is the “Change Directory” command and takes you into the project directory.
  2. This builds and runs the app. The first time you run it, it can take some time since it must fetch all the dependencies.

First build and run

The template has a predefined route, so open your browser and visit http://localhost:8080/hello and see the response!

Hello route

Now press Control-C in Terminal to stop the running app.

Swift Package Manager

Vapor Toolbox uses Swift Package Manager, or SwiftPM, — a dependency management system similar to CocoaPods on iOS — to configure and build Vapor apps. Open your project directory and look at the structure. On macOS in Terminal, enter:

open .

Project Layout

Notice there’s no Xcode project in your template even though you’ve built and run the app. This is deliberate. In fact, the project file is explicitly excluded from source control using the .gitignore file. When using SwiftPM, Xcode creates a workspace in a hidden directory called .swiftpm.

A SwiftPM project is defined in the Package.swift manifest file. It declares targets, dependencies and how they link together. The project layout is also different from a traditional Xcode project. There is a Tests directory for tests. There is a Sources directory for source files. Each module defined in your manifest has its own directory inside Sources. Your sample app has an App module and a Run module, so Sources contains an App directory and a Run directory.

Inside the Run directory, there’s a single file: main.swift. This is the entry point required by all Swift apps.

Note: On iOS, this is usually synthesized with a @UIApplicationMain attribute on the AppDelegate.

The template contains everything you need to set up your app and you shouldn’t need to change main.swift or the Run module. Your code lives in App or any other modules you define.

Creating Your Own Routes

Note: This section uses Xcode. If you’re developing on Linux, use your favorite editor, then use swift run to build and run your app.

Now that you’ve made your first app, it’s time to see how easy it is to add new routes with Vapor. If the Vapor app is still running, stop it by pressing Control-C in Terminal. Next enter:

open Package.swift

This opens the project in Xcode as a SwiftPM workspace. It will take a couple of minutes for Xcode to download the dependencies. When it’s finished, open routes.swift in Sources/App. You’ll see the route you visited above. To create another route, add the following after the app.get("hello") closure:

app.get("hello", "vapor") { req -> String in
  return "Hello Vapor!"
}

Here’s what this does:

  • Add a new route to handle a GET request. Each parameter to app.get is a path component in the URL. This route is invoked when a user enters http://localhost:8080/hello/vapor as the URL.
  • Supply a closure to run when this route is invoked. The closure receives a Request object.
  • Return a string as the result for this route.

In the Xcode toolbar, select the HelloVapor scheme and choose My Mac as the device.

Xcode scheme

Build and run by clicking the Run button or pressing Command-R. You may be prompted by Xcode to enter your password on this first run, so go ahead and do so. Once you see the “Server starting on http://127.0.0.1:8080” message in the console, visit http://localhost:8080/hello/vapor in your browser.

Hello Vapor route

What if you want to say hello to anyone who visits your app? Adding every name in the world would be quite impractical! There must be a better way. There is, and Vapor makes it easy.

Add a new route that says hello to whomever visits. For example, if your name is Tim, you’ll visit the app using the URL http://localhost:8080/hello/Tim and it says “Hello, Tim!”. Add the following after the code you just entered:

// 1
app.get("hello", ":name") { req -> String in
  //2
  guard let name = req.parameters.get("name") else {
    throw Abort(.internalServerError)
  }
  // 3
  return "Hello, \(name)!"
}

Here’s the play-by-play:

  1. Use :name to designate a dynamic parameter.
  2. Extract the user’s name, which is passed in the Request object. If Vapor can’t find a parameter called name, throw an error.
  3. Use the name to return your greeting.

Build and run. In your browser, visit http://localhost:8080/hello/Tim. Try replacing Tim with some other values.

Hello Name!

Accepting Data

Most web apps must accept data. A common example is user login. To do this, a client sends a POST request with a JSON body, which the app must decode and process.

Vapor makes decoding data easy thanks to its strong integration with Swift’s Codable protocol. You give Vapor a Codable struct that matches your expected data, and Vapor does the rest. Create a POST request to see how this works.

Note: This tutorial uses the RESTed app, available as a free download from the Mac App Store. If you like, you may use another REST client to test your APIs.

Set up the request as follows:

  • URL: http://localhost:8080/info
  • Method: POST
  • Add a single parameter called name. Use your name as the value.
  • Select JSON-encoded as the request type. This ensures that the data is sent as JSON and that the Content-Type header is set to application/json. If you are using a different client you may need to set this manually.

Your request should look similar to the following:

RESTed POST request

Go back to Xcode, open routes.swift and add the following to the end of the file to create a struct called InfoData to represent this request:

struct InfoData: Content {
 let name: String
}

This struct conforms to Content which is Vapor’s wrapper around Codable. Vapor uses Content to extract the request data, whether it’s the default JSON-encoded or form URL-encoded. InfoData contains the single parameter name.

Next, add a new route after the app.get("hello", "vapor") closure:

// 1
app.post("info") { req -> String in
  // 2
  let data = try req.content.decode(InfoData.self)
  // 3
  return "Hello \(data.name)!"
}

Here’s what this does:

  1. Adds a new route handler to handle a POST request for the URL http://localhost:8080/info. This route handler returns a String.
  2. Decode the request’s body using InfoData.
  3. Returns the string by pulling the name out of the data variable.

Build and run the app. Send the request from RESTed and you’ll see the response come back:

RESTed results

Note: If you are into curl here’s the corresponding call:
curl http://localhost:8080/info \
  -H "Content-Type: application/json" \
  -d '{"name":"Tim"}'

This may seem like a lot of boilerplate to extract a single parameter from JSON. However, Codable scales up and allows you to decode complex, nested JSON objects with multiple types in a single line.

Returning JSON

Vapor also makes it easy to return JSON in your route handlers. This is a common need when your app provides an API service. For example, a Vapor app that processes requests from an iOS app needs to send JSON responses. Vapor again uses Content to encode the response as JSON.

Open routes.swift and add the following struct, called InfoResponse, to the end of the file:

struct InfoResponse: Content {
  let request: InfoData
}

This struct conforms to Content and contains a property for the request.

Next, replace the previous app.post("info") with the following:

// 1
app.post("info") { req -> InfoResponse in
  let data = try req.content.decode(InfoData.self)
  // 2
  return InfoResponse(request: data)
}

Here’s what changed:

  1. The route handler now returns the new InfoResponse type.
  2. A new InfoResponse is constructed using the decoded request data.

Build and run the app. Send the same request from RESTed. You’ll see a JSON response containing your original request data:

RESTed JSON response

Troubleshooting Vapor

In this and future Vapor apps, you may encounter errors in your projects. There are a number of steps to take to troubleshoot any issues. Here’s some tips:

  1. Update dependencies
  2. Clean and rebuild
  3. Ask the community for help :]

Update your Dependencies

One scenario you may encounter is hitting a bug in Vapor or another dependency you use. Make sure you are on the latest package version of any dependencies to see if the update fixes the issue. In Xcode, choose File ▸ Swift Packages ▸ Update to Latest Package Versions. If you’re running the app in Terminal, or on Linux, type:

swift package update

This SwiftPM command pulls down any updates to your dependencies and use the latest releases you support in Package.swift. Note that while packages are in the beta or release candidate stages, there may be breaking changes between updates.

Clean and Rebuild

If you are still having issues, you can use the software equivalent of “turn it off and on again”. In Xcode, use Command-Option-Shift-K to clean the build folder.

You may also need to clear your derived data for the Xcode project as well and the workspace itself. The “nuclear” option involves:

  • Remove the .build directory to remove any build artifacts.
  • Remove your .swiftpm directory to delete the Xcode workspace and any misconfigurations.
  • Remove Package.resolved to ensure you get the latest dependencies next time you build.
  • Remove DerivedData to clear build artifacts cached by Xcode.

Vapor Discord

The steps above usually fix most issues you might encounter that aren’t caused by your code. If all else fails, head to Vapor’s Discord server. You’ll find thousands of developers discussing Vapor, its changes and helping people with issues.

Click the Join Chat button on Vapor’s web site: https://vapor.codes.

Where to Go From Here?

You can download the completed project for this tutorial using the “Download Materials” button at the top or bottom of this page.

This tutorial provided an overview of how to get started with Vapor and how to create basic routes. If you enjoyed this tutorial, why not check out our full-length book on Vapor development: Server Side Swift with Vapor?

If you’re a beginner to web development, but have worked with Swift for some time, you’ll find it’s easy to create robust, fully-featured web apps and web APIs with Vapor.

Whether you’re looking to create a backend for your iOS app, or want to create fully-featured web apps, Vapor is the perfect platform for you.

Questions or comments on this tutorial? Leave them in the comments below!