Server Side Swift with Vapor 2 (Part 2)

ShengHua Wu
3 min readJul 17, 2017

--

In the previous part of this series, we have created a RESTful API server which manipulates three models — — Lesson, Teacher, and Student. In this article, I am going to build up a sibling relationship between Teacher and Lesson, and it is possible to follow the same pattern to achieve the relationship between Student and Lesson.

Introduction

Basically, we need to define the following three endpoints:

  1. /teachers/teacher_id/teaches/lesson_id: Set a sibling relationship between Teacher and Lesson via a POST request.
  2. /teachers/teacher_id/lessons: Fetch all Lesson objects corresponding to a specific Teacher object.
  3. /lessons/lesson_id/teachers: Fetch all Teacher objects corresponding to a specific Lesson object.

Implementation

First of all, in order to describe a sibling relationship, it is necessary to store the pair of models’ identifiers into a new table called Pivot. Therefore, let's move to the Config+Setup.swift file and add the following line into the setupPreparations method.

private func setupPreparations() throws {
// ...
preparations.append(Pivot<Teacher, Lesson>.self)
}

Secondly, we also need to define a new endpoint, in order to save the identifier pair into our Pivot table via a POST request. Thus, switch to TeahcerController.swift file and add the following two new methods.

func addRoutes(_ drop: Droplet) {
let teachersGroup = drop.grouped("teachers")
teachersGroup.post(Teacher.parameter, "teaches", Lesson.parameter, handler: teaches)
}
private func teaches(request: Request) throws -> ResponseRepresentable {
let teacher = try request.parameters.next(Teacher.self)
let lesson = try request.parameters.next(Lesson.self)
let pivot = try Pivot<Teacher, Lesson>(teacher, lesson)
try pivot.save()
return teacher
}

The reason why we have to create the addRoutes method is this endpoint doesn't belong to the RESTful API design diagram. In other words, we need this method to connect with the Droplet object. So, open the Routes.swift file and modify the setupRoutes method.

func setupRoutes() throws {
// ...
let teacherController = TeacherController()
resource("teachers", teacherController)
teacherController.addRoutes(self)
// ...
}

At this point, we are able to generate a sibling relationship between Teacher and Lesson. Let's continue implementing how to retrieve the relevant model objects. In TeacherController.swift file, add another new method and a new endpoint. In addition, create an extension of Teacher to get the sibling of Lesson easily.

final class TeacherController {
// ...
func addRoutes(_ drop: Droplet) {
let teachersGroup = drop.grouped("teachers")
// ...
teachersGroup.get(Teacher.parameter, "lessons", handler: lessons)
}
private func lessons(request: Request) throws -> ResponseRepresentable {
let teacher = try request.parameters.next(Teacher.self)
return try teacher.lessons().makeJSON()
}
}
// ...extension Teacher {
func lessons() throws -> [Lesson] {
let lessons: Siblings<Teacher, Lesson, Pivot<Teacher, Lesson>> = siblings()
return try lessons.all()
}
}

Next, we add the following methods and an extension of Lesson within LessonController.swift, in order to fetch the corresponding Teacher sibling.

final class LessonController {
// ...
func addRoutes(_ drop: Droplet) {
let lessonsGroup = drop.grouped("lessons")
lessonsGroup.get(Lesson.parameter, "teachers", handler: teachers)
}
private func teachers(request: Request) throws -> ResponseRepresentable {
let lesson = try request.parameters.next(Lesson.self)
return try lesson.teachers().makeJSON()
}
}
// ...extension Lesson {
func teachers() throws -> [Teacher] {
let teachers: Siblings<Lesson, Teacher, Pivot<Teacher, Lesson>> = siblings()
return try teachers.all()
}
}

Finally, remember to hook up the new endpoint with the Droplet object in Routes.swift file.

func setupRoutes() throws {
let lessonController = LessonController()
resource("lessons", lessonController)
lessonController.addRoutes(self)
// ...
}

Conclusion

The entire sample code is here.

Again, we can test our new endpoints with Postman. However, let’s talk about the pros and cons of Vapor. On one hand, the merits include well-defined documentations, useful built-in functions, and a migration tool. On the other hand, the downsides are fewer search results for Vapor 2 than 1, and Swift keeps evolving. Still, I personally think Vapor is a good opportunity to learn back-end development for a iOS developer. I’m totally open to discussion and feedback, so please share your thoughts.

--

--

ShengHua Wu

I’m an enthusiast of iOS development. Enjoy learning new things.