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 https://www.raywenderlich.com/4493-server-side-swift-with-vapor/lessons/9
Learn how to create controllers to organize routes and how to save and retrieve models using Fluent.
Flawless so far. Good job.
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.
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:
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
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.
JT
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
or
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ā¦
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.
Edited:
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:
router.post(InfoData.self, 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
func createHandler(_ req: Request) throws -> Future<Acronym> {
let acronym = try.req.content.decode(Acronym.self).await(on: req)
return acronym.save(on: 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!
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
@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 @0xtim .