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 correspondingModel
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 anid
property. Each property of your Model
will correspond to a table column with the same name, converted to snake_case
.
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 statictableName: String
property.
Custom Key Mappings
As mentioned, by default allModel
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 asString
, 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 asenum
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 beCodable
, 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 customJSONDecoder
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.
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 aModel
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.
.unwrapFirstModel(or error: Error)
.
Quick Lookups
There are also two functions for quickly looking up aModel
.
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 deletingModel
s.
Get All
Fetch all records of aModel
with the all()
function.
Save
Save aModel
to the database, either inserting it or updating it depending on if it has a nil id.
Delete
Delete an existingModel
from the database with delete()
.
Sync
Fetch an up to date copy of thisModel
.
Bulk Operations
You can also do bulk inserts or deletes on[Model]
.