Database¶
A Fluent database is responsible for managing connections to your underlying data store and sending queries to the implementation-specific driver you have chosen.
Drivers¶
By default, Fluent includes in-memory and SQLite drivers. There are several drivers available to add to your Vapor application.
Available¶
Type | Key | Package | Class | Official |
---|---|---|---|---|
Memory | memory | Fluent Provider | Fluent.MemoryDriver | Yes |
SQlite | sqlite | Fluent Provider | Fluent.SQLiteDriver | Yes |
MySQL | mysql | MySQLProvider | MySQLDriver.Driver | Yes |
PostgreSQL | postgresql | PostgreSQLProvider | PostgreSQLDriver.Driver | No |
MongoDB | N/A | MongoProvider | N/A | No |
Click on the provider package for more information about how to use it.
You can search for a list of available Vapor database providers on GitHub.
Droplet¶
You can access the database from the Droplet.
drop.database // Database?
Preparations¶
Most databases, like SQL databases, require the schema for a model to be created before it is stored. Adding a preparation to your model will allow you to prepare the database while your app boots.
extension User: Preparation { /// Prepares a table/collection in the database /// for storing Users static func prepare(_ database: Database) throws { try database.create(self) { builder in builder.id() builder.string("name") builder.int("age") } } /// Undoes what was done in `prepare` static func revert(_ database: Database) throws { try database.delete(self) } }
The above prepare statement results in SQL similar to the following:
CREATE TABLE `users` (`id` INTEGER PRIMARY KEY NOT NULL, `name` TEXT NOT NULL, `age` INTEGER NOT NULL)
Once you have created you preparation, add it to the Config's prepratations array.
config.preparations.append(User.self)
Create¶
The following methods are available on while creating and modifing the database.
Method | Type |
---|---|
id | Primary Identifier |
foreignId | Foreign Identifier |
int | Integer |
string | String |
double | Double |
bool | Boolean |
bytes | Data |
date | Date + Time |
You can use any of these methods on a builder inside .create()
.
try database.create(self) { builder in builder.double("latitude") builder.double("longitude") }
Foreign Keys¶
Foreign keys are automatically added with .foreignId()
. To add a foreign key manually, use the
.foreignKey
method.
try database.create(self) { builder in builder.foreignKey("user_id", references: "id", on: User.self) }
To disable automatic foreign keys, set autoForeignKeys
to false in the Config/fluent.json
file.
{ "autoForeignKeys": false }
Modifier¶
Existing schema can be modified using the .modify()
property. All of the methods from .create()
are available here as well.
try database.modify(self) { builder in builder.string("name") builder.delete("age") }
Migrations¶
Other times, you may want to make some modifications to your data set while migrating to a new version or just performing general cleanup.
struct DeleteOldEntries: Preparation { static func prepare(_ database: Database) throws { try Log.makeQuery().filter(...).delete() } ... }
Run¶
Your preparations will run every time you run your application. You can run your preparations without booting your server by calling:
vapor run prepare
Revert¶
Use the revert method to undo any work you did in the prepare method.
extension User: Preparation { ... static func revert(_ database: Database) throws { try database.delete(self) } }
You can run the reversions by calling:
vapor run prepare --revert
This will revert the latest batch of preparations. To revert the entire database, run the following:
vapor run prepare --revert --all
Log¶
Logging queries is a great way to find optimizations for your application and track down bugs.
The easiest way to log queries is to enable logging in your fluent.json
file.
Config/fluent.json
{ ..., "log": true, ... }
This will emit info-level logs for all database queries.
Custom¶
You can also hook into the database's query logging callback to execute custom logic.
drop.database?.log = { query in print(query) }
You can assign a closure to the log
property on the database. Any time a query is run, the closure
will be called with a QueryLog
object containing a string describing the statement and the time it ran.
Transactions¶
Transactions allow you to group multiple queries into one single unit of work. If any one of the queries experiences a problem, the entire transaction will be rolled back.
drop.database?.transaction { conn in try user.pets.makeQuery(conn).delete() try user.makeQuery(conn).delete() }
Drivers that do not support transactions will throw an error if this method is called.
You can use the .makeQuery(_: Executor)
method to create queries that will run on the
connection supplied to the closure.
Warning
You must use the connection supplied to the closure for queries you want to include in the transaction.
Indexes¶
An index is a copy of selected columns of data from a table that can be searched very efficiently.
You can add them to your database by calling .index()
try database.index("name", for: User.self)
You can delete them by calling .deleteIndex()
try database.deleteIndex("name", for: User.self)