After displaying text on a web page, receiving a POST request is the “Hello, World” v2 of building a web app. Let’s walk through how to do that using the popular server side Swift web framework, Vapor.
Swift Package Manager
For this project we’re going to use Swift Package Manager to set everything up and install dependencies. Make sure you have Swift 4.0 or greater installed before moving on. You can check your Swift version by running the following in your terminal:
swift --version
Vapor has a convenient command line tool which can be used to generate templates for projects. We won’t be using that functionality in this post, as our needs are more simple, but installing this will also install other necessary dependencies.
You can install it using Homebrew with the following command:
brew tap vapor/homebrew-tap brew install vapor
Now initiate a new Swift project with the following terminal command in the directory where you want your code to live:
swift package init --type executable
This will generate a directory structure for your Swift project. By default it will name your project after the directory you ran the previous command in. My project was named Hello
, so you may need to replace that in any of the commands or code further in this post that refer to your Swift project.
Next let’s get our dependencies set up. Open the file Package.swift
and add the Vapor package to the code Swift Package Manager generated. My Package.swift
looks like this:
// swift-tools-version:4.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Hello", dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0-rc") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "Hello", dependencies: ["Vapor"]), ] )
Getting started with Vapor
We’re now ready to move on to writing some Swift code. From the base directory of your project, open the file Sources/Hello/main.swift
and add the following code to it:
import Vapor let app = try Application() let router = try app.make(Router.self) router.get("hello") { req in return "Hello, world." } try app.run()
This is the bare bones “Hello World” Vapor application. It has one route that takes a GET
request and responds with a simple string. With your Package.swift
configured and your Vapor code written, run the following commands to build and run the project:
swift build ./.build/debug/Hello
This will install Vapor and all of its dependencies and will run your web application. Once it is running, visit http://localhost:8080/hello to see a basic “Hello World” webpage.
Parsing a POST request body
The next step is to start handling POST
requests. There are many ways to get data out of the body of a POST
request, but the preferred way with Vapor is to create a Codable
struct to map the data from the request body to.
As a quick example, let’s treat the incoming request similarly to one you would receive using the Twilio SMS API for when an incoming text message is sent to your Twilio number. Let’s create a content struct to represent the data of a text message. Add the following code to main.swift
after the import Vapor
statement:
struct MessageRequest: Content { var to: String var from: String var body: String }
Now that we have our struct, all that’s left to do is create a new route to receive POST
requests. In this route, we’ll decode the request body using our new MessageRequest
struct. Add the following code to main.swift
after the “hello” route:
router.post("sms") { req -> Future<HTTPStatus> in return try req.content.decode(MessageRequest.self).map(to: HTTPStatus.self) { messageRequest in print("To: (messageRequest.to)") print("From: (messageRequest.from)") print("Body: (messageRequest.body)") return .ok } }
Let’s use the curl
command to see if this route works. Build and run your code again, and run the following command in another terminal window:
curl -d "to= 19999999999&from= 18888888888&body=Hello" -X POST http://localhost:8080/sms
In the terminal window running your Vapor app, you should see something like this printed to the console when the request is received:
Opening your web app to the internet
Now you can receive and parse POST
requests, but this isn’t much use if your web application is just running on localhost
! You could host it somewhere, but if you’re just testing stuff for development we can use a tool called ngrok. Ngrok opens a tunnel from a port on your machine to a publicly accessible URL. This is a life saver if you need to test some code for a webhook.
You can download ngrok here. Once it’s installed you can create an http tunnel on port 8080 with the following command:
ngrok http 8080
If you’ve just installed ngrok and that previous command didn’t work, you might have to run it like ./ngrok http 8080
from the directory that the ngrok executable is in. Once ngrok is running your terminal should look something like this:
Try visiting your ngrok URL in the browser and appending “/hello” to it to see the “hello” route on your web app. You can also run the previous curl command while replacing the “localhost:8080” part with your ngrok URL and everything should work the same. Your server side Swift app is now accessible on the internet…until you stop ngrok or close your laptop!
Wrapping up
Now you’re equipped to handle incoming POST
requests in your server side Swift applications. This can be useful whether you’re developing backend APIs for your iOS apps, dealing with webhooks for services like Twilio or submitting form data from a client side web application.
For more in depth projects with Vapor, you could also use the Vapor command line tool to generate project templates and handle the Swift Package Manager stuff for you.
If you have any other cool ideas, feel free to reach out and let me know or ask any questions:
- Email: Sagnew@twilio.com
- Twitter: @Sagnewshreds
- Github: Sagnew
- Twitch (streaming live code): Sagnewshreds