Skip to content

Latest commit

 

History

History
263 lines (204 loc) · 6.98 KB

Vapor.md

File metadata and controls

263 lines (204 loc) · 6.98 KB

Vapor Learning Notes

  1. Getting started
  2. Model example
  3. Controller example
  4. Parent-children relationship
  5. Sibling relationship
  6. Useful links

Getting started

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

Model Example

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)

Controller Example

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)

Parent - Children (One-to-many) Relationships Example

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

  1. Add a creator id on the model
var creatorID: User.ID
  1. Add creatorID in the initialiser as well

  2. 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

  1. 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

  1. 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)
        }
    }
  1. Register the route in the boot function
blogPostsRoute.get(BlogPost.parameter, "creator", use: getCreatorHandler)

In the UsersController

  1. 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()
        }
    }
  1. Register the route in the boot function
usersRoute.get(User.parameter, use: getBlogPostsHandler)

Sibling Relationships (Many-to-Many) Example

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.

  1. 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
    }
}
  1. In configure.swift add
migrations.add(model: BlogPostCategoryPivot.self, database: .sqlite)
  1. In BlogPost add a computed property
var categories: Siblings<BlogPost, Category, BlogPostCategoryPivot> {
    return siblings()
}
  1. In Category add a computer property
var blogPosts: Siblings<Category, BlogPost, BlogPostCategoryPivot> {
    return siblings()
}
  1. 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)
  1. 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)
  1. 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)

Useful links