Vapor — Create a GET route

Parameters, JSON instead of plain/text and throw errors

Alexandre Cools
7 min readOct 25, 2020

Before starting, if you don’t know how to create a new Vapor project, I suggest you to read this article who explain how to get started with Vapor.

What’s a route?

A route is like a name you use to access API endpoints, (by the URL). A route can have multiple endpoints associated with it. Which is used depends on the HTTP verb; GET in our case.

Generated routes

By default, Vapor create two routes for us. To see them, you can open the routes.swift file:

As you can see, there are two routes defined: / and /hello.

The first one, /, return “It works” and the second one, /hello return “Hello, world!” both as plain text.

For some basic routes, that’s great and easy to create, but what if you want a route like /hello/world?

Create a route with multiple “sub-paths”

Let me quickly explain what I mean by “sub-paths”.
In the following path: how/to/say/hello/to/the/world, there is 7 sub-paths (how, to, say, hello, to, the, world). So a “sub-path” is all texts separated by the / symbol.

There are many solution to do that, but the recommended one is to add all the sub-paths you want in the get method, separated by a coma, like this:

app.get(“hello”, “world”) { req -> String in
return “The new Hello world!”
}

Then, if you rebuild your Vapor application and go to the following address: 127.0.0.1:8080/hello/world , you should see the new message: “The new Hello world!”.

💡 Tips: if you have a 404 not found error, double check your browser address to be sure that the path is correct, and be to not enter any spaces in between quote in the get function parameters. For example app.get("hello", " world") will not work because you can’t enter an address like: 127.0.0.1:8080/hello/ world. If you do that, your browser will try to load some result from the internet and not from your server.

Routes parameters

Ok, right now, your request is not very interesting because you say hello to the world, but what if you want to say hello to a specific person ? For example, you could want to do something like this: 127.0.0.1:8080/hello/alexandre to say hello to Alexandre, or maybe 127.0.0.1:8080/hello/domenico, to say hello to Domenico, etc.

Well, you can add parameter to your route simply by adding : before a sub-path in your app get function. For example, if you want to say hello to someone, your app get function will look like:

app.get(“hello”, “:name”) { req -> String in
// Your code here
}

By writing :name, Vapor will understand you want a variable in your request URL. So, you can now call the following URLs and all of them will works:

127.0.0.1:8080/hello/alexandre
127.0.0.1:8080/hello/domenico
127.0.0.1:8080/hello/world

In fact, you can type whatever you want after /hello/ (small reminder: spaces are note supported).

Ok, you have now a parameter for your route, but if you run your Vapor application, you still have “Hello, world!” as response of your request.

To get the request parameter, you can doing the following code inside your app get function:

guard let name = req.parameters.get(“name”) else { return “Not found...”}

Note that the name here: get("name") should be the exact same text as the name here: app.get("hello", ":name"). So if you want to do the things in a secure and beautiful way, you should probably use a constant for that.

Obviously, instead of returning “Not found”, you should return an error as we will see in a minute.

So, now you have your request parameter, you can do:

return “Hello, \(name)!”

to return “Hello, “ followed by the name you entered in your URL.

Routes parameters, other than String

And what if my parameter isn’t a String but an Int for example?

The function req.parameters.get("name") return an optional String, but if your parameter is an Int for example, you can do this instead: req.parameters.get("name", as: Int.self) and you will get an optional Int.

⚠️ Warning, by doing this, you’ll ensure that your parameters is an Int and only an Int. So, if you call now the URL 127.0.0.1:8080/hello/zero, you will get “Not found” as response, because your parameters “zero” is not an Int.

It could be very useful if you know exactly the type of the variable you want.

Return an error instead of a String

Well, instead of returning “Not found” when the request have an issue, a better way is to return an error with a code, like a 404, 403, etc.

The routes(_ app:) function is marked as throws to indicate that the function can throw an error. In Swift, it means that we have to use the do catch method to catch the error if any.

In our case, we will use this to throw a specific error by doing:

throw Abort(.badRequest)

This code will return a Bad Request (code 400) to the client.

Bad request is not the only error you can throw. If you want to see all the possibilities of the return code you have, you can jump to the definition of .badRequest.

In fact, you can return a lot of different response to the client. You have noted that you can return error to the user but also different success response as 201 or 202 for example.

💡 Tips: if you don’t know which response is associated to which code, you can scroll down a bit to have more informations.

Now we know how to return an error to the client, let’s put this in pratice.

If you don’t want to allow the user to say hello to the world for example, you can update your function like this:

app.get(“hello”, “:name”) { req -> String in
guard
let name = req.parameters.get(“name”) else { throw Abort(.badRequest) }
guard name != “world” else { throw Abort(.badRequest) }
return “Hello, \(name)!”
}

In fact, if you look the code closer, you will see that’s pretty the same as before, we only replace the return "Not found..." by throw Abort(.badRequest) .

Return JSON instead of plain/text

Until now, all your request return a plain/text format to the client, but web API return generally response as JSON.

Basically, JSON is only a [String: Any] type, and Vapor help us to return automatically a JSON object when we use struct who conform to the Content protocol.

In our example, we can create a struct named HelloReponse with a name attribute, and who conform to the Content protocol:

struct HelloResponse: Content {
var name: String
}

If you want to know more about the Content protocol, you can Option + Click on it to display a bit more informations:

Ok, now we have this struct, we can adapt our hello function by replacing the String return type by our new HelloResponse type. Then, when we have validated the name received as parameter, we can create our HelloResponse and return it.

app.get(“hello”, “:name”) { req -> HelloResponse in
guard
let name = req.parameters.get(“name”) else { throw Abort(.badRequest) }
guard name != “world” else { throw Abort(.badRequest) }
return HelloResponse(name: name)
}

So now, when we call 127.0.0.1:8080/hello/alexandre, we will receive a JSON as response:

All right, we now have a Vapor application who listen for request and can respond to the hello/:name route by returning a JSON object with the name passed in parameter. The function does not have a lot of sense, but we have saw how to create our GET route, to add parameter to it, to throw error if needed and to return JSON to the client who ask for this route. That’s not so bad! 🎉

🚀 What’s next

If you missed the previous article, you can read it here: Vapor — Generated files overview.

Don’t forget to “clap” and share this article if you like it and want to see more content like this! 👏

And if you have any question, feel free to let a comment right below, I’ll answer you with pleasure!

Thanks for reading,

--

--

Alexandre Cools

iOS Developer @LunabeeStudio 👨🏻‍💻 — Sport addict 🏊‍♂️🚴‍♂️🏃‍♂️ — Travel enthusiast ✈️