- Getting started
- Model example
- Controller example
- Parent-children relationship
- Sibling relationship
- Useful links
Check if ready for Vapor eval "$(curl -sL check.vapor.sh)"
Install brew brew install vapor/tap/vapor
Check for latest version brew upgrade vapor/tap/vapor
Make a new project vapor new <Project Name> --branch=beta
-> cd into project
Generate xCode Project vapor xcode
-> Open xCode project
-> Run -> My Mac
-> Go to http://localhost:8080/hello
To create a new file touch Sources/App/Models/<File Name>.swift
-> Regenerate xcode project vapor xcode -y
import Foundation
import FluentSQLite
import Vapor
final class User: Codable {
var id: UUID?
var name: String
var username: String
init(name: String, username: String) {
self.name = name
self.username = username
}
}
extension User: SQLiteUUIDModel {}
extension User: Content {}
extension User: Migration {}
Add it to migrations in configure.swift
migrations.add(model: User.self, database: .sqlite)
import Vapor
struct UsersController: RouteCollection {
func boot(router: Router) throws {
let usersRoute = router.grouped("api", "users")
usersRoute.get(use: getAllHandler)
usersRoute.post(use: createHandler)
usersRoute.get(User.parameter, use: getHandler)
}
func getAllHandler(_ req: Request) throws -> Future<[User]> {
return User.query(on: req).all()
}
func createHandler(_ req: Request) throws -> Future<User> {
let user = try req.content.decode(User.self)
return user.save(on: req)
}
func getHandler(_ req: Request) throws -> Future<User> {
return try req.parameter(User.self)
}
}
extension User: Parameter{}
Register it in routes.swift
let usersController = UsersController()
try router.register(collection: usersController)
User (Parent) - creator of blog post, can write multiple blog posts
Blog post (Children) - created by one and only one user
In the BlogPost model
- Add a creator id on the model
var creatorID: User.ID
-
Add creatorID in the initialiser as well
-
To be able to view the relationship, add an extension on the BlogPost
extension BlogPost {
var creator: Parent<BlogPost, User> {
return parent(\.creatorID)
}
}
In the User model
- Add an extension to be able to see all the blog posts of an user
extension User {
var blogPosts: Children<User, BlogPost> {
return children(\.creatorID)
}
}
In the BlogPostsController
- Create a getCreatorHandler that will return us the user that created the post
func getCreatorHandler(_ req: Request) throws -> Future<User> {
return try req.parameter(BlogPost.self).flatMap(to: User.self) { blogPost in
return try blogPost.creator.get(on: req)
}
}
- Register the route in the boot function
blogPostsRoute.get(BlogPost.parameter, "creator", use: getCreatorHandler)
In the UsersController
- Create a new root handler getBlogPostsHandler to see all blog posts written by a user
func getBlogPostsHandler(_ req: Request) throws -> Future<[BlogPost]> {
return try req.parameter(User.self).flatMap(to: [BlogPost].self) { user in
return try user.blogPosts.query(on: req).all()
}
}
- Register the route in the boot function
usersRoute.get(User.parameter, use: getBlogPostsHandler)
Category (Sibling) - can have many blog posts Blog Post (Sibling) - can have many categories
To represent many-to-many relationships in Vapor we need to use Pivots.
- Create a new model BlogPostCategoryPivot
import Foundation
import FluentSQLite
import Vapor
final class BlogPostCategoryPivot: SQLiteUUIDPivot {
var id: UUID?
var blogPostID: BlogPost.ID
var categoryID: Category.ID
typealias Left = BlogPost
typealias Right = Category
static let leftIDKey: LeftIDKey = \BlogPostCategoryPivot.blogPostID
static let rightIDKey: RightIDKey = \BlogPostCategoryPivot.categoryID
init(_ blogPostID: BlogPost.ID,_ categoryID: Category.ID) {
self.blogPostID = blogPostID
self.categoryID = categoryID
}
}
- In configure.swift add
migrations.add(model: BlogPostCategoryPivot.self, database: .sqlite)
- In BlogPost add a computed property
var categories: Siblings<BlogPost, Category, BlogPostCategoryPivot> {
return siblings()
}
- In Category add a computer property
var blogPosts: Siblings<Category, BlogPost, BlogPostCategoryPivot> {
return siblings()
}
- Add the route in the BlogPostsController
func getCategoriesHandler(_ req: Request) throws -> Future<[Category]> {
return try req.parameter(BlogPost.self).flatMap(to: [Category].self) { blogPost in
return try blogPost.categories.query(on: req).all()
}
}
And register it in the boot
blogPostsRoute.get(BlogPost.parameter, "categories", use: getCategoriesHandler)
- Add the route in CategoriesController
func getBlogPostsHandler(_ req: Request) throws -> Future<[BlogPost]> {
return try req.parameter(Category.self).flatMap(to: [BlogPost].self) { category in
return try category.blogPosts.query(on: req).all()
}
}
And register it in the boot
categoriesRoute.get(Category.parameter, "blogPosts", use: getBlogPostsHandler)
- Add a new POST route in the BlogPostsController
func addCategoriesHandler(_ req: Request) throws -> Future<HTTPStatus> {
return try flatMap(to: HTTPStatus.self, req.parameter(BlogPost.self),
req.parameter(Category.self)) { blogPost, category in
let pivot = try BlogPostCategoryPivot(blogPost.requireID(), category.requireID())
return pivot.save(on: req).transform(to: .ok)
}
}
And register it in the boot
blogPostsRoute.post(BlogPost.parameter, "categories", Category.parameter, use: addCategoriesHandler)
- Vapor documentation
- Vapor Slack
- RayWenderlich Server Side Swift with Vapor Course
- RESTed app - to format and make HTTP requests and view the response