Server Side Swift with Vapor - Part 9: Controllers | Ray Wenderlich

Learn how to create controllers to organize routes and how to save and retrieve models using Fluent.

This is a companion discussion topic for the original entry at

Flawless so far. Good job.

1 Like

just a tool recommendation - postman is a more full-featured http test client, it’s main value is in retaining a history of requests. also free.

1 Like

you use terminal to create new swift files. Is it mandatory, or can you use xcode to create them too ?

@toinewx you can use Xcode you just have to be really careful about:

  • what target membership you select - Xcode doesn’t have great integration with SPM yet to be able to make this easy, it just picks the first target it knows about which is usually a dependency
  • where you save the file. If you save it in the wrong place then it will work up until you regenerate your project then you are going to get errors

See Server Side Swift with Vapor - Part 8: Models And | Ray Wenderlich - #7 by simonqq as an example for errors caused by this.

So in short it is certainly possible (and I do it half the time) but it can cause issues which is why I suggest to do it through terminal, especially while people get used to SPM :slight_smile:

1 Like

I’m currently on this part of the tutorial and am stuck with Xcode generating the following error when trying to compile the Acronyms controller:

Incorrect argument label in call (have ‘collection:’, expected ‘route:’)

The error is in routes.swift on the following line:
try router.register(collection: AcronymsController)

I’ve double checked my code with what I see in the video and it looks the same. What am I doing wrong? Everything else up until this point has compiled fine.


Hi @jtrimble! Does AcronymsController conform to RouteCollection? Secondly does your code have a capital A in the thing you are passing a parameter? i.e. are you passing it the type rather than an instance. You either need to do:

let acronymsController = AcronymsController()
try router.register(collection: acronymsController) // Note the lower case a


try router.register(collection: AcronymsController())

Hope that helps!

OMG, I can’t believe I missed a problem with case! Thank you!

Guess it’s too early in the morning… :slight_smile:

1 Like

@jtrimble :coffee: :wink:

Hey, can’t make post requests. I get the same error every time, “wait() must not be called when on the EventLoop”. Thanks in advance for your help.

Hey, so I figured out if you remove the wait() call then the issue goes away.

@trouge ah yeah that’s because save() actually works on a future! The other option is to use the function I posted in the other forum, that looks like:, at: "info") { req, data -> InfoResponse in
  return InfoResponse(request: data)

Hi Tim,

Could you give the specific update for this function (rather than the generalized one from the other video pasted above)? I’ve taken 1 beginner Swift course before this so I’m still putting the pieces of abstraction together :slight_smile:

func createHandler(_ req: Request) throws -> Future<Acronym> {
    let acronym = try.req.content.decode(Acronym.self).await(on: req)
    return req)

@amyerson that’s good timing, I’m currently going through and noting all the updates before I rerecord what’s needed. You can see the current changes in this gist, which I’ll keep updating as I go through all the changes. Any problems or questions just ask!

It looks like a lot of changes, thanks for going thru and making that reference!

1 Like

Just a quick question @0xtim.

func getAllHandler(_ req: Request) throws -> Future<[Acronym]> {
    return Acronym.query(on: req).all()

I am getting bit confused as to why we don’t use flatmap for this return above, I dived into the code for the .all() function and noticed that it uses flatmap, so there is no point calling twice if it already is an asynchronous request?

@0xtim Can you please help with this when you get a chance? Thank you - much appreciated! :]

@izzywizz so the answer is either easy or complicated depending on how you look at it!

The Acronym.query(on: req).all() line performs a database query to get all of the Acronyms. The will return an array of acronyms at some point in the future, so the return type is Future<[Acronym]>. Given that is all you want to return you can just return that directly (the router can wait for the response and return it when the future completes). If you wanted to actually manipulate the array of users before returning it, that would be different - then you would just a flatMap or map to unwrap the future, and manipulate the data.

Does that make sense?

@0xtim yeah that makes sense, so it just returns it when the future completes asynchronously for .all() but if you wanted to change something then you have to safely unwrap it using flatmap, manipulate whichever way you want then return the manipulated data when it finishes in the future.

Huzzah! Thanks a bunch, that was diving me crazy. Explains why we use it for delete/ update now

1 Like

@izzywizz so without wanting to confuse you too much…

For the update yes you need to upwrap it as you need to be able to change the values. For the delete you can actually call delete on a Future<Model>, but that is just a convenience that has been added to Fluent to make it easier for you!

That’s well cool. I just tried the following code you advised on the delete handler we previously created on the video without the flatmap closure.

    let acronym = try req.parameter(Acronym.self)
    return acronym.delete(on: req).transform(to: .noContent)

And the convenience method works brilliantly, they really have thought of everything and when you dive into vapor code it pretty much looks like ours but using generics types. though if you cmd-click further down the vapor convenience code for delete, you go down one hell of weird rabbit hole involving Async but that is debate for another time hahaha

You are awesome :sunglasses: @0xtim .

1 Like