Chapters

Hide chapters

Server-Side Swift with Vapor

Third Edition - Early Acess 1 · iOS 13 · Swift 5.2 - Vapor 4 Framework · Xcode 11.4

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section I: Creating a Simple Web API

Section 1: 13 chapters
Show chapters Hide chapters

7. CRUD Database Operations
Written by Tim Condon

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

Chapter 5, “Fluent and Persisting Models”, explained the concept of models and how to store them in a database using Fluent. This chapter concentrates on how to interact with models in the database. You’ll learn about CRUD operations and how they relate to REST APIs. You’ll also see how to leverage Fluent to perform complex queries on your models.

Note: This chapter requires you to use PostgreSQL. Follow the steps in Chapter 5, “Fluent and Persisting Models”, to set up PostgreSQL in Docker and configure your Vapor application.

CRUD and REST

CRUD operations — Create, Retrieve, Update, Delete — form the four basic functions of persistent storage. With these, you can perform most actions required for your application. You actually implemented the first function, create, in Chapter 5.

RESTful APIs provide a way for clients to call the CRUD functions in your application. Typically you have a resource URL for your models. For the TIL application, this is the acronym resource: http://localhost:8080/api/acronyms. You then define routes on this resource, paired with appropriate HTTP request methods, to perform the CRUD operations. For example:

Create

In Chapter 5, “Fluent and Persisting Models”, you implemented the create route for an Acronym. You can either continue with your project or open the TILApp in the starter folder for this chapter. To recap, you created a new route handler in routes.swift:

// 1
app.post("api", "acronyms") { 
  req -> EventLoopFuture<Acronym> in
  // 2
  let acronym = try req.content.decode(Acronym.self)
  // 3
  return acronym.save(on: req.db).map { acronym }
}

Retrieve

For TILApp, retrieve consists of two separate operations: retrieve all the acronyms and retrieve a single, specific acronym. Fluent makes both of these tasks easy.

Retrieve all acronyms

To retrieve all acronyms, create a route handler for GET requests to /api/acronyms/. Open routes.swift and add the following at the end of routes(_:):

// 1
app.get("api", "acronyms") { 
  req -> EventLoopFuture<[Acronym]> in
  // 2
  Acronym.query(on: req.db).all()
}

Retrieve a single acronym

Vapor’s parameters integrate with Fluent’s querying functions to make it easy to get acronyms by IDs. To get a single acronym, you need a new route handler. Open routes.swift and add the following at the end of routes(_:):

// 1
app.get("api", "acronyms", ":acronymID") { 
  req -> EventLoopFuture<Acronym> in
  // 2
  Acronym.find(req.parameters.get("acronymID"), on: req.db)
    // 3
    .unwrap(or: Abort(.notFound))
}

Update

In RESTful APIs, updates to single resources use a PUT request with the request data containing the new information.

// 1
app.put("api", "acronyms", ":acronymID") { 
  req -> EventLoopFuture<Acronym> in
  // 2
  let updatedAcronym = try req.content.decode(Acronym.self)
  return Acronym.find(
  	req.parameters.get("acronymID"),
  	on: req.db)
    .unwrap(or: Abort(.notFound)).flatMap { acronym in
      acronym.short = updatedAcronym.short
      acronym.long = updatedAcronym.long
      return acronym.save(on: req.db).map {
        acronym
      }
  }
}

Delete

To delete a model in a RESTful API, you send a DELETE request to the resource. Add the following to the end of routes(_:) to create a new route handler:

// 1
app.delete("api", "acronyms", ":acronymID") { 
  req -> EventLoopFuture<HTTPStatus> in
  // 2
  Acronym.find(req.parameters.get("acronymID"), on: req.db)
    .unwrap(or: Abort(.notFound))
    // 3
    .flatMap { acronym in
      // 4
      acronym.delete(on: req.db)
        // 5
        .transform(to: .noContent)
  }
}

Fluent queries

You’ve seen how easy Fluent makes basic CRUD operations. It can perform more powerful queries just as easily.

Filter

Search functionality is a common feature in applications. If you want to search all the acronyms in the database, Fluent makes this easy. Ensure the following line of code is at the top of routes.swift:

import Fluent
// 1
app.get("api", "acronyms", "search") { 
  req -> EventLoopFuture<[Acronym]> in
  // 2
  guard let searchTerm = 
    req.query[String.self, at: "term"] else {
    throw Abort(.badRequest)
  }
  // 3
  return Acronym.query(on: req.db)
    .filter(\.$short == searchTerm)
    .all()
}

return Acronym.query(on: req.db)
  .filter(\.$short == searchTerm)
  .all()
// 1
return Acronym.query(on: req.db).group(.or) { or in
  // 2
  or.filter(\.$short == searchTerm)
  // 3
  or.filter(\.$long == searchTerm)
// 4
}.all()

First result

Sometimes an application needs only the first result of a query. Creating a specific handler for this ensures the database only returns one result rather than loading all results into memory. Create a new route handler to return the first acronym at the end of routes(_:):

// 1
app.get("api", "acronyms", "first") { 
  req -> EventLoopFuture<Acronym> in
  // 2
  Acronym.query(on: req.db)
    .first()
    .unwrap(or: Abort(.notFound))
}

Sorting results

Apps commonly need to sort the results of queries before returning them. For this reason, Fluent provides a sort function.

// 1
app.get("api", "acronyms", "sorted") { 
  req -> EventLoopFuture<[Acronym]> in
  // 2
  Acronym.query(on: req.db)
    .sort(\.$short, .ascending)
    .all()
}

Where to go from here?

You now know how to use Fluent to perform the different CRUD operations and advanced queries. At this stage, routes.swift is getting cluttered with all the code from this chapter. The next chapter looks at how to better organize your code using controllers.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now