I'm trying to understand how to create CRUD functions with Vapor. It appears first()
is optional, but cannot be unwrapped as an optional type
func first() -> EventLoopFuture<Token?>
Here's my Token.swift
import Foundation
import Fluent
import Vapor
final class Token: Model {
// 1
static let schema = "tokens"
// 2
@ID(key: .id)
var id: UUID?
// 3
@Field(key: "token")
var token: String
@Field(key: "debug")
var debug: Bool
// 4
init() { }
init(token: String, debug: Bool) {
self.token = token
self.debug = debug
}
}
Here's my TokenController.swift
import Foundation
import Fluent
import Vapor
struct TokenController {
func create(req: Request) async throws -> HTTPStatus {
let token = try req.content.decode(Token.self)
try await token.create(on: req.db)
return .created
}
func delete(req: Request) async throws -> HTTPStatus {
guard let token = req.parameters.get("token") else {
return .badRequest
}
try await Token.query(on: req.db)
.filter(\.$token == token)
.first()
try await delete(req: req.db)
return .noContent
}
}
@available(macOS 12, *)
extension TokenController: RouteCollection {
func boot(routes: RoutesBuilder) throws {
let tokens = routes.grouped("token")
tokens.post(use: create)
tokens.delete(":token", use: delete)
}
}
The return token might not be there, so it should be optional, but I get the familiar error
Initializer for conditional binding must have Optional type, not 'EventLoopFuture<Token?>'
I'm using async-await obviously and not EventLoopFuture<>
except maybe under the hood, if I understand the documentation correctly.
Based on a tutorial I'm following, this was suggested as a solution to delete rows by getting the first row in the table
func delete(req: Request) async throws -> HTTPStatus {
guard let token = req.parameters.get("token") else {
return .badRequest
}
guard let row = try await Token.query(on: req.db)
.filter(\.$token == token)
.first()
else {
return .notFound
}
try await row.delete(on: req.db)
return .noContent
}
But this gives me the same error for token, including another error for the row.delete(on:_)
method
Value of type 'EventLoopFuture<Token?>' has no member 'delete'
What am I missing?
• Pouring over the documentation • Retracing my steps from the tutorial • Holding my breath until the computer worked
You probably need to give the compiler a hand and force it to use the async version of .first()
:
guard let row: Token = try await Token.query(on: req.db)
.filter(\.$token == token)
.first()
Because the functions have the same signature it can occasionally pick the future version, especially if there are errors elsewhere