Search code examples
swiftswift4.1vapor

Correct way to load data from database and load it to the view in Vapor 3?


I have a Vapor 3 project which can upload some content String with format in html. And have function to load this content as a html page. The code looks like:

func newpost(_ reqest: Request) throws -> Future<View> {
    self.getContent(req: reqest) { (content) in
        return try reqest.view().render("newpost.leaf", content)
    }

}

func getContent(req:Request, callback: @escaping (String) -> ()) {
   let _ = BlogModel.query(on: req).first().map(to: BlogModel.self) { (blog) -> (BlogModel) in
        callback((blog?.content)!)
        return blog!
    }
}

But this code lead to error:

Invalid conversion from throwing function of type '(_) throws -> _' to non-throwing function type '(String) -> ()'

If I try return try reqest.view().render("newpost.leaf", content) out site the block then I cannot get the content. Please help me for the right way to load it.


Solution

  • You should have a look at the Async section in the docs (Promises, etc.). There's no need to use callbacks.

    This is could be one way to obtain the data from the DB and render it using Leaf (it's the same idea as your code, but replacing callbacks with Promises and cleaning up unnecessary code):

    enum APIError: AbortError {
        case dataNotFound
    }
    
    /// Render the HTML string using Leaf
    func newPost(_ req: Request) throws -> Future<View> {
        return getContent(req)
            .flatMap(to: View.self) { model in
                // By default, Leaf will assume all templates have the "leaf" extension
                // There's no need to specify it
                return req.view().render("newpost", model)
            }
    }
    
    /// Retrieve X content from the DB
    private func getContent(_ req: Request) throws -> Future<BlogModel> {
        return BlogModel.query(on: req)
            .first() // can be nil
            .unwrap(or: APIError.dataNotFound)
            // returns an unwrapped value or throws if none
    }
    

    If you don't want to throw when no data is found, you could just use nil-coalescing to convert nil to an empty String for example.