Rune
Getting Started
Alchemy includes Rune, an object-relational mapper (ORM) to make it simple to interact with your database. With Rune, each database table has a corresponding Model
type that is used to interact with that table. Use this Model type for querying, inserting, updating or deleting from the table.
Creating a Model
To get started, implement the Model protocol. All it requires is an id
property. Each property of your Model
will correspond to a table column with the same name, converted to snake_case
.
Warning: Model
APIs rely heavily on Swift’s Codable
. Please avoid overriding the compiler synthesized func encode(to: Encoder)
and init(from: Decoder)
functions. You might be able to get away with it but it could cause issues under the hood. You can however, add custom CodingKeys
if you like, just be aware of the impact it will have on the keyMappingStrategy
described below.
Custom Table Names
By default, your model will correspond to a table with the name of your model type, pluralized. For custom table names, you can override the static tableName: String
property.
Custom Key Mappings
As mentioned, by default all Model
property names will be converted to snake_case
, when mapping to corresponding table columns. You may change this behavior via the keyMapping: DatabaseKeyMapping
. You could set it to .useDefaultKeys
to use the verbatim CodingKey
s of the Model
object, or .custom((String) -> String)
to provide a custom mapping closure.
Model Field Types
Basic Types
Models support most basic Swift types such as String
, Bool
, Int
, Double
, UUID
, Date
. Under the hood, these are mapped to relevant types on the concrete Database
you are using.
Advanced Types
Models also support some more advanced Swift types, such as enum
s and JSON
.
Enums
String
or Int
backed Swift enum
s are allowed as fields on a Model
, as long as they conform to ModelEnum
.
JSON
Models require all properties to be Codable
, so any property that isn’t one of the types listed above will be stored as JSON
.
Custom JSON Encoders
By default, JSON
properties are encoded using a default JSONEncoder()
and stored in the table column. You can use a custom JSONEncoder
by overriding the static Model.jsonEncoder
.
Custom JSON Decoders
Likewise, you can provide a custom JSONDecoder
for decoding data from JSON columns.
Decoding from SQLRow
Model
s may be “decoded” from a SQLRow
that was the result of a raw query or query builder query. The Model
’s properties will be mapped to their relevant columns, factoring in any custom keyMappingStrategy
. This will throw an error if there is an issue while decoding, such as a missing column.
Note: For the most part, if you are using Rune you won’t need to call SQLRow.decode(_ type:)
because the typed ORM queries described in the next section decode it for you.
Model Querying
To add some type safety to query builder queries, you can initiate a typed query off of a Model
with the static .query
function.
ModelQuery<M: Model>
is a subclass of the generic Query
, with a few functions for running and automatically decoding M
from a query.
All Models
.allModels()
returns all Model
s that matched the query.
First Model
.firstModel()
returns an EventLoopFuture<M?>
containing the first Model
that matched the query, if it exists.
If you want to throw an error if no item is found, you would .unwrapFirstModel(or error: Error)
.
Quick Lookups
There are also two functions for quickly looking up a Model
.
ensureNotExists(where:error:)
does a query to ensure that a Model
matching the provided where clause doesn’t exist. If it does, it throws the provided error.
unwrapFirstWhere(_:error:)
is essentially the opposite, finding the first Model
that matches the provided where clause or throwing an error if one doesn’t exist.
Model CRUD
There are also convenience functions around creating, fetching, and deleting Model
s.
Get All
Fetch all records of a Model
with the all()
function.
Save
Save a Model
to the database, either inserting it or updating it depending on if it has a nil id.
Delete
Delete an existing Model
from the database with delete()
.
Sync
Fetch an up to date copy of this Model
.
Bulk Operations
You can also do bulk inserts or deletes on [Model]
.