How does Alchemy compare to Vapor?
That being said, after building with Vapor I felt like there was room for a framework that focused a lot more on convention like Laravel / Rails to keep things lean and fast.
Consider ORM APIs…
In Vapor require property wrappers around each field, as well as manually entering in the table name, db column the keys map to, and needing to write an initializer.
final class Planet: Model {
// Name of the table or collection.
static let schema = "planets"
// Unique identifier for this Planet.
@ID(key: .id)
var id: UUID?
// The Planet's name.
@Field(key: "name")
var name: String
// Creates a new, empty Planet.
init() { }
// Creates a new Planet with all properties set.
init(id: UUID? = nil, name: String) {
self.id = id
self.name = name
}
}
In Alchemy the table name is inferred to be the type name pluralized, and each db column is inferred from the Swift property name (even mapping camelCase to snakeCase by default). It also supports structs, and thus the automatic initializers.And because it uses Swift.Codable doesn’t have to use expensive reflection APIs to decode, giving a decent performance boost (disclaimer: last time I checked).
struct Planet: Model {
var id: UUID?
var name: String
}
Similarly with relationships, Vapor requires somewhat verbose string / key path arguments in the property wrappers and supports 1-M M-1 & M-M relationships. final class Star: Model {
@Children(for: \.$star) // 1-M
var planets: [Planet]
@Parent(key: "star_id") // M-1
var star: Star
}
vs inferring the type and connecting keys, as well as supporting customizing the relationship behavior in Alchemy for things like HasManyThrough, BelongsToThrough, etc. All inferred keys are, of course, overridable in case your db naming convention is different. struct Star: Model {
@BelongsTo var star: Star // M-1
@HasMany var planets: [Planet] // 1-M
@HasMany var moons: [Moon] // “Has Many Through”
static func mapRelations(_ mapper: RelationshipMapper<User>) {
// infers mapping from stars.id -> plants.star_id -> planets.id -> moons.planet_id
mapper.config(\.$moons).through(“planets”)
}
}
There’s also a bunch more examples of APIs around QueryBuilder, Caching, Jobs, Queues, Redis, Commands, Authentication, Middleware etc[1]. And everything will be converted to async/await in the coming days.1: https://github.com/alchemy-swift/alchemy/tree/main/Docs#docs