|  | 
| 1 | 1 | --- | 
| 2 | 2 | layout: page | 
| 3 |  | -title: Build a Web Server | 
|  | 3 | +title: Build a Web Server with Vapor | 
| 4 | 4 | --- | 
| 5 | 5 | 
 | 
| 6 |  | -TODO | 
|  | 6 | +{% include getting-started/_installing.md %} | 
|  | 7 | + | 
|  | 8 | +## Installing Vapor | 
|  | 9 | + | 
|  | 10 | +This guide uses the [Vapor](https://vapor.codes) web framework to build a server application. First, you need to install the Vapor toolbox. If you already have Homebrew installed on macOS, run | 
|  | 11 | + | 
|  | 12 | +```bash | 
|  | 13 | +brew install vapor | 
|  | 14 | +``` | 
|  | 15 | + | 
|  | 16 | +If you're running on a different OS or want to install the toolbox from source, see [the Vapor docs](https://docs.vapor.codes/install/linux/#install-toolbox) for how to do so. | 
|  | 17 | + | 
|  | 18 | +## Creating a Project | 
|  | 19 | + | 
|  | 20 | +Then, in your terminal in a directory where you want to create the new project run: | 
|  | 21 | + | 
|  | 22 | +```bash | 
|  | 23 | +vapor new HelloVapor | 
|  | 24 | +``` | 
|  | 25 | + | 
|  | 26 | +This pulls down a template and asks you a series of questions to create a simple project with everything you need to get started. This guide will create a simple REST API that you can send and receive JSON to and from. So answer no to all other questions. You'll see the project created successfully: | 
|  | 27 | + | 
|  | 28 | + | 
|  | 29 | + | 
|  | 30 | +Navigate into the created directory and open the project in your IDE of choice. For instance, to use VSCode run: | 
|  | 31 | + | 
|  | 32 | +```bash | 
|  | 33 | +cd HelloVapor | 
|  | 34 | +code . | 
|  | 35 | +``` | 
|  | 36 | + | 
|  | 37 | +For Xcode, run: | 
|  | 38 | + | 
|  | 39 | +```bash | 
|  | 40 | +cd HelloVapor | 
|  | 41 | +open Package.swift | 
|  | 42 | +``` | 
|  | 43 | + | 
|  | 44 | +Vapor's template contains a number of files and functions already set up for you. **configure.swift** contains the code to configure your application and **routes.swift** contains route handler code. | 
|  | 45 | + | 
|  | 46 | +## Creating Routes | 
|  | 47 | + | 
|  | 48 | +First, open **routes.swift** and create a new route to say hello to anyone accessing your site by declaring a new route below `app.get("hello") { ... }`: | 
|  | 49 | + | 
|  | 50 | +```swift | 
|  | 51 | +// 1 | 
|  | 52 | +app.get("hello", ":name") { req async throws -> String in | 
|  | 53 | +    // 2 | 
|  | 54 | +    let name = try req.parameters.require("name") | 
|  | 55 | +    // 3 | 
|  | 56 | +    return "Hello, \(name.capitalized)!" | 
|  | 57 | +} | 
|  | 58 | +``` | 
|  | 59 | + | 
|  | 60 | +Here's what the code does:  | 
|  | 61 | + | 
|  | 62 | +1. Declare a new route handler registered as a **GET** request to `/hello/<NAME>`. The `:` denotes a dynamic path parameter in Vapor and will match any value and allow you to retrieve it in your route handler. `app.get(...)` takes a closure as the final parameter that can be asynchronous and must return a `Response` or something conforming to `ResponseEncodable`, such as `String`. | 
|  | 63 | +2. Get the name from the parameters. By default, this returns a `String`. If you want to extract another type, such as `Int` or `UUID` you can write `req.parameters.require("id", as: UUID.self)` and Vapor will attempt to cast it to the type and automatically throw an error if it's unable to. This throws an error if the route hasn't been registered with the correct parameter name. | 
|  | 64 | +3. Return the `Response`, in this case a `String`. Note that you don't need to set a status code, response body or any headers. Vapor handles this all for you, whilst allowing you to control the `Response` returned if needed. | 
|  | 65 | + | 
|  | 66 | +Save the file and build and run the app. Send a **GET** request to `http://localhost:8080/hello/tim`. You'll get the response back: | 
|  | 67 | + | 
|  | 68 | +```bash | 
|  | 69 | +$ curl http://localhost:8080/hello/tim | 
|  | 70 | +Hello, Tim! | 
|  | 71 | +``` | 
|  | 72 | + | 
|  | 73 | +Try it with different names to see it change automatically! | 
|  | 74 | + | 
|  | 75 | +## Returning JSON | 
|  | 76 | + | 
|  | 77 | +Vapor uses [`Codable`](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types) under the hood to make it easy to send and receive JSON, using a wrapper protocol called `Content` to add a few extra features. Next, you'll return a JSON body with the message from the Hello! route. First, create a new type at the bottom of **routes.swift**: | 
|  | 78 | + | 
|  | 79 | +```swift | 
|  | 80 | +struct UserResponse: Content { | 
|  | 81 | +    let message: String | 
|  | 82 | +} | 
|  | 83 | +``` | 
|  | 84 | + | 
|  | 85 | +This defines a new type that conforms to `Content` that matches the JSON you want to return. | 
|  | 86 | + | 
|  | 87 | +Create a new route below `app.get("hello", ":name") { ... }` to return this JSON: | 
|  | 88 | + | 
|  | 89 | +```swift | 
|  | 90 | +// 1 | 
|  | 91 | +app.get("json", ":name") { req async throws -> UserResponse in | 
|  | 92 | +    // 2 | 
|  | 93 | +    let name = try req.parameters.require("name") | 
|  | 94 | +    let message = "Hello, \(name.capitalized)!" | 
|  | 95 | +    // 3 | 
|  | 96 | +    return UserResponse(message: message) | 
|  | 97 | +} | 
|  | 98 | +``` | 
|  | 99 | + | 
|  | 100 | +Here's what this code does: | 
|  | 101 | + | 
|  | 102 | +1. Define a new route handler that handles a **GET** request to `/json`. Importantly, the return type for the closure is `UserResponse`. | 
|  | 103 | +2. Get the name as before and construct the message. | 
|  | 104 | +3. Return the `UserResponse`. | 
|  | 105 | + | 
|  | 106 | +Save and build and run the app again and send a GET request to `http://localhost:8080/json/tim`: | 
|  | 107 | + | 
|  | 108 | +```bash | 
|  | 109 | +$ curl http://localhost:8080/json/tim | 
|  | 110 | +{"message":"Hello, Tim!"} | 
|  | 111 | +``` | 
|  | 112 | + | 
|  | 113 | +This time, you get JSON back! | 
|  | 114 | + | 
|  | 115 | +## Handling JSON | 
|  | 116 | + | 
|  | 117 | +Finally, we'll cover how to receive JSON. At the bottom of **routes.swift**, create a new type to model JSON you'll send to the server app: | 
|  | 118 | + | 
|  | 119 | +```swift | 
|  | 120 | +struct UserInfo: Content { | 
|  | 121 | +    let name: String | 
|  | 122 | +    let age: Int | 
|  | 123 | +} | 
|  | 124 | +``` | 
|  | 125 | + | 
|  | 126 | +This contains two properties, a name and an age. Then, below the JSON route, create a new route to handle a POST request with this body: | 
|  | 127 | + | 
|  | 128 | +```swift | 
|  | 129 | +// 1 | 
|  | 130 | +app.post("user-info") { req async throws -> UserResponse in | 
|  | 131 | +    // 2 | 
|  | 132 | +    let userInfo = try req.content.decode(UserInfo.self) | 
|  | 133 | +    // 3 | 
|  | 134 | +    let message = "Hello, \(userInfo.name.capitalized)! You are \(userInfo.age) years old." | 
|  | 135 | +    return UserResponse(message: message) | 
|  | 136 | +} | 
|  | 137 | +``` | 
|  | 138 | + | 
|  | 139 | +The important differences in this new route handler are: | 
|  | 140 | + | 
|  | 141 | +1. Use `app.post(...)` instead of `app.get(...)` as this route handler is a **POST** request. | 
|  | 142 | +2. Decode the JSON from the request body. | 
|  | 143 | +3. Use the data from the JSON body to create a new message. | 
|  | 144 | + | 
|  | 145 | +Send a POST request with a valid JSON body and see your response: | 
|  | 146 | + | 
|  | 147 | +```bash | 
|  | 148 | +$ curl http://localhost:8080/user-info -X POST -d '{"name": "Tim", "age": 99}' -H "Content-Type: application/json" | 
|  | 149 | +{"message":"Hello, Tim! You are 99 years old."} | 
|  | 150 | +``` | 
|  | 151 | + | 
|  | 152 | +Congratulations! You've built your first web server in Swift! | 
|  | 153 | + | 
|  | 154 | +Find the source code for this guide at [https://github.com/vapor/swift-getting-started-web-server](https://github.com/vapor/swift-getting-started-web-server) | 
0 commit comments