Skip to content

Querying Models

Once you have a model (and optionally a migration) you can start querying your database to create, read, update, and delete data.

Connection

The first thing you need to query your database, is a connection to it. Luckily, they are easy to get.

You can use either the application or an incoming request to create a database connection. You just need access to the database identifier.

Request

The preferred method for getting access to a database connection is via an incoming request.

router.get(...) { req in
    return req.withConnection(to: .foo) { db in
        // use the db here
    }
}

The first parameter is the database's identifier. The second parameter is a closure that accepts a connection to that database.

Tip

Although the closure to .withConnection(to: ...) accepts a database connection, we often use just db for short.

The closure is expected to return a Future<Void>. When this future is completed, the connection will be released back into Fluent's connection pool. This is usually acheived by simply returning the query as we will soon see.

Application

You can also create a database connection using the application. This is useful for cases where you must access the database from outside a request/response event.

let res = app.withConnection(to: .foo) { db in
    // use the db here
}
print(res) // Future<T>

This is usually done in the boot section of your application.

Warning

Do not use database connections created by the application in a route closure (when responding to a request). Always use the incoming request to create a connection to avoid threading issues.

Create

To create (save) a model to the database, first initialize an instance of your model, then call .save(on: ).

router.post(...) { req in
    return req.withConnection(to: .foo) { db -> Future<User> in
        let user = User(name: "Vapor", age: 3)
        return user.save(on: db).transform(to: user) // Future<User>
    }
}

Response

.save(on: ) returns a Future<Void> that completes when the user has finished saving. In this example, we then map that Future<Void> to a Future<User> by calling .map and passing in the recently-saved user.

You can also use .map to return a simple success response.

router.post(...) { req in
    return req.withConnection(to: .foo) { db -> Future<HTTPResponse> in
        let user = User(name: "Vapor", age: 3)
        return user.save(on: db).map(to: HTTPResponse.self) {
            return HTTPResponse(status: .created)
        }
    }
}

Multiple

If you have multiple instances to save, do so using an array. Arrays containing only futures behave like futures.

router.post(...) { req in
    return req.withConnection(to: .foo) { db -> Future<HTTPResponse> in
        let marie = User(name: "Marie Curie", age: 66)
        let charles = User(name: "Charles Darwin", age: 73)
        return [
            marie.save(on: db),
            charles.save(on: db)
        ].map(to: HTTPResponse.self) {
            return HTTPResponse(status: .created)
        }
    }
}

Read

To read models from the database, use .query() on the database connection to create a QueryBuilder.

All

Fetch all instances of a model from the database using .all().

router.get(...) { req in
    return req.withConnection(to: .foo) { db -> Future<[User]> in
        return db.query(User.self).all()
    }
}

Filter

Use .filter(...) to apply filters to your query.

router.get(...) { req in
    return req.withConnection(to: .foo) { db -> Future<[User]> in
        return try db.query(User.self).filter(\User.age > 50).all()
    }
}

First

You can also use .first() to just get the first result.

router.get(...) { req in
    return req.withConnection(to: .foo) { db -> Future<User> in
        return try db.query(User.self).filter(\User.name == "Vapor").first().map(to: User.self) { user in
            guard let user = user else {
                throw Abort(.notFound, reason: "Could not find user.")
            }

            return user
        }
    }
}

Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional user, or we throw an error.

Update

router.put(...) { req in
    return req.withConnection(to: .foo) { db -> Future<User> in
        return db.query(User.self).first().map(to: User.self) { user in
            guard let user = $0 else {
                throw Abort(.notFound, reason: "Could not find user.")
            }

            return user
        }.flatMap(to: User.self) { user in
            user.age += 1
            return user.update(on: db).map(to: User.self) { user }
        }
    }
}

Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional user, or we throw an error.

Delete

router.delete(...) { req in
    return req.withConnection(to: .foo) { db -> Future<User> in
        return db.query(User.self).first().map(to: User.self) { user in
            guard let user = $0 else {
                throw Abort(.notFound, reason: "Could not find user.")
            }
            return user
        }.flatMap(to: User.self) { user in
            return user.delete(on: db).transfom(to: user)
        }
    }
}

Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional user, or we throw an error.