---
title: "Tutorial: Building a WebApp using Swift Part 2"
description: In Part 2 of this Swift language Tutorial series, we'll create a data Model and develop basic data operations.
image: https://blog.avenuecode.com/hubfs/Images/Blog/Swift-2.jpg
---

[AvenueCode.com](https://avenuecode.com) [News](https://avenuecode.com/news) [Contact](https://avenuecode.com/contact)

[![Avenue Code Snippets Logo](https://blog.avenuecode.com/hubfs/Avenue%20Code%20New%20Logos%20-%202023/AC-Snippets---Black.png)](https://blog.avenuecode.com/?hsLang=en-us) *menu*

- Technology
  
  [Cloud](https://blog.avenuecode.com/blog/topic/cloud?hsLang=en-us) [Delivery Infrastructure](https://blog.avenuecode.com/blog/topic/delivery-infrastructure?hsLang=en-us) [Web Experience](https://blog.avenuecode.com/blog/topic/web-experience?hsLang=en-us) [Agile Mindset](https://blog.avenuecode.com/blog/topic/agile-mindset?hsLang=en-us) [Quality First](https://blog.avenuecode.com/blog/topic/quality-first?hsLang=en-us)
  
  [Design](https://blog.avenuecode.com/blog/topic/design?hsLang=en-us) [Solution Architecture](https://blog.avenuecode.com/blog/topic/solution-architecture?hsLang=en-us) [Data & ML](https://blog.avenuecode.com/blog/topic/data-and-machine-learning?hsLang=en-us) [Mobile Experience](https://blog.avenuecode.com/blog/topic/mobile-experience?hsLang=en-us)
- [Whitepapers](https://blog.avenuecode.com/blog/topic/whitepapers?hsLang=en-us)
- [Spotlight](https://blog.avenuecode.com/blog/topic/spotlight?hsLang=en-us)
- [Extraordinary Women in Tech](https://blog.avenuecode.com/blog/topic/extraordinary-women-in-tech?hsLang=en-us)
- [Avenue Code Culture](https://blog.avenuecode.com/blog/topic/avenue-code-culture?hsLang=en-us)

- [Cloud](https://blog.avenuecode.com/blog/topic/cloud?hsLang=en-us)
- [Design](https://blog.avenuecode.com/blog/topic/design?hsLang=en-us)
- [Delivery Infrastructure](https://blog.avenuecode.com/blog/topic/delivery-infrastructure?hsLang=en-us)
- [Solution Architecture](https://blog.avenuecode.com/blog/topic/solution-architecture?hsLang=en-us)
- [Web Experience](https://blog.avenuecode.com/blog/topic/web-experience?hsLang=en-us)
- [Data & ML](https://blog.avenuecode.com/blog/topic/data-and-machine-learning?hsLang=en-us)
- [Agile Mindset](https://blog.avenuecode.com/blog/topic/agile-mindset?hsLang=en-us)
- [Mobile Experience](https://blog.avenuecode.com/blog/topic/mobile-experience?hsLang=en-us)
- [Quality First](https://blog.avenuecode.com/blog/topic/quality-first?hsLang=en-us)
- [Whitepapers](https://blog.avenuecode.com/blog/topic/whitepapers?hsLang=en-us)
- [Spotlight](https://blog.avenuecode.com/blog/topic/spotlight?hsLang=en-us)
- [Extraordinary Women in Tech](https://blog.avenuecode.com/blog/topic/extraordinary-women-in-tech?hsLang=en-us)
- [Avenue Code Culture](https://blog.avenuecode.com/blog/topic/avenue-code-culture?hsLang=en-us)

# [Tutorial: Building a WebApp using Swift Part 2](https://blog.avenuecode.com/tutorial-building-a-webapp-using-swift-part-2-2/)

- [Tweet](https://twitter.com/share)

*perm\_identity* [Guilherme Teixeira](https://blog.avenuecode.com/tutorial-building-a-webapp-using-swift-part-2-2//author/guilherme-teixeira?hsLang=en-us)

*schedule* 1/25/17 4:55 PM

In the first part of our Tutorial, we learned how to setup and run our Vapor environment, as well as how to configure MySQL for use. If you missed Part 1, you can read it [here](https://blog.avenuecode.com/tutorial-building-a-webapp-using-swift-part-1/?hsLang=en-us)! Now, in Part 2, we will create our data Model and learn how to develop the necessary basic data operations that will be used on our Pet App. So, let's get started!

#### MODEL

> Model is the base protocol for any of your application's models, especially those you want to persist.

Using Model subclasses, we can get objects that represent your information, and all the complexity involved in create, delete, update and fetch commands will be resolved! All we have to do is specify the fields, make it conform to ResponseRepresentable protocol, and create preparations.

First of all, let's create a new file named *Pet.swift* inside Models folder.   
The first step is to open this file and import:   
`import Vapor import Foundation`

Now, we declare our class as Model protocol, like this:   
`final class Pet: Model { }`

Let's create a few properties that will be the persisted columns on our database, not forgetting to id properties that are required when using MySQL:

`var id: Node? var name: String var age: String var owner: String var lastVisit:String var specie:String var picture: String var exists: Bool = false`

Now we implement the 3 needed methods:

`init(name: String, age:String, owner:String, lastVisit:String, specie:String, picture:String) { self.id = nil self.name = name self.age = age self.owner = owner self.lastVisit = lastVisit self.specie = specie self.picture = picture }`

`init(node: Node, in context: Context) throws { id = try node.extract("id") name = try node.extract("name") age = try node.extract("age") owner = try node.extract("owner") lastVisit = try node.extract("lastVisit") specie = try node.extract("specie") picture = try node.extract("picture") }`

`func makeNode(context: Context) throws -> Node { return try Node(node: [ "id": id, "name": name, "age" : age, "owner": owner, "lastVisit": lastVisit, "specie" : specie, "picture" : picture ]) }`

 

Code will be available on GitHub - don't worry about the code for now.

 

The first initializer is the one we will use to create our new persisted information. The second and the third methods are necessary to conform to ResponseRepresentable. With that, we have the information in Node object format, which is used by Fluent to store and retrieve information, and as an added bonus we get facilities like the ability to transform objects in JSON format.

Lastly, to complete our class, we have to implement two more methods that will handle the database tables. The first is the *prepare* method, responsible for creating new tables when they are not yet extent, and the second method is *revert*, used to delete the entire table:

`static func prepare(_ database: Database) throws { try database.create("pets") { pet in pet.id() pet.string("name") pet.string("age") pet.string("owner") pet.string("lastVisit") pet.string("specie") pet.string("picture") } }`

`static func revert(_ database: Database) throws { try database.delete("pets") }`

 

Don't be scared: revert method will not be called within your code. When you want to drop your tables, you can invoke this function via Terminal command `vapor run prepare --revert`.

 

Now, our Model class is done. We only need to declare our preparations together with our Droplet creation on *Main.swift*, like this:

`let drop = Droplet( preparations:[Pet.self], providers:[VaporMySQL.Provider.self] )`

#### CRUD OPERATIONS

Now that our Model is done, we can start our database operations. In computer programming, CRUD is the Acronym for the four basic persistent storage functions: **Create**, **Read**, **Update** and **Delete**.   
For now, our server CRUD requests will be answered with JSON response, and User Interface will be the theme of Part 3 of this Tutorial. Let's open *Main.swift*, register new routes, and practice what we'll need to do to get the CRUD operations for our Pet App:

- **Creating a new Pet:**

`drop.post("pet") { request in guard let name:String = request.data["name"]?.string, let age:String = request.data["age"]?.string, let owner:String = request.data["owner"]?.string, let nextVisit:String = request.data["nextVisit"]?.string, let specie:String = request.data["specie"]?.string, let picture:String = request.data["picture"]?.string else { throw Abort.badRequest } var myPet:Pet = Pet(name: name, age: age, owner: owner, lastVisit: nextVisit, specie: specie, picture: picture) try myPet.save() return try JSON(node: Pet.all().makeNode()) }`

In this first snippet, we register a *POST* method for an endpoint *"pet"*, and safely store the needed parameters using Swift's `guard let`. If some of these parameters are missing, then we throw a Bad Request message. Then, with all parameters correct, we create our Pet instance and save through *myPet.save()*. Guess what??? Our Pet is saved!!!

![giphy (4).gif](https://blog.avenuecode.com/hs-fs/hubfs/Images/Blog/giphy%20(4).gif?width=458&height=301&name=giphy%20(4).gif)

And to finish, we simply return all the entries on our Pet table in JSON format.

 

You can test your inserts doing a POST to a url like this: [http://localhost:8080/pet?name=Bob&age=2&owner=John&lastVisit=14-May-2016&specie=Cat&picture=https://img.url](http://localhost:8080/pet?name=Bob&age=2&owner=John&lastVisit=14-May-2016&specie=Cat&picture=https://img.url) . You can use[Postman](https://www.getpostman.com/) to make your life easier

 

- **Update an existent Pet:**

`drop.put("pet") { request in guard let identifier:String = request.data["id"]?.string, let name:String = request.data["name"]?.string, let age:String = request.data["age"]?.string, let owner:String = request.data["owner"]?.string, let lastVisit:String = request.data["lastVisit"]?.string, let specie:String = request.data["specie"]?.string, let picture:String = request.data["picture"]?.string else { throw Abort.badRequest } guard var myPet:Pet = try Pet.find(identifier) else { throw Abort.notFound } myPet.name = name myPet.age = age myPet.owner = owner myPet.lastVisit = lastVisit myPet.specie = specie myPet.picture = picture try myPet.save() return try JSON(node: Pet.all().makeNode()) }`

The first half of the Update route is pretty similar to Insert. The primary difference is that for updates, we use PUT method instead, and also send the *id* to identify which entry we will update. After checking all parameters, we'll try to fetch an existent Pet. If we do not find it, then we respond throwing Not Found. With the Pet object loaded, we simply need to update with new information, and again, invoke the save method. Finally, we respond again with all Pet entries.

 

You can test your updates doing a PUT to an url like this: [http://localhost:8080/pet?id=1&name=George&age=3&owner=Steven&lastVisit=14-May-2016&specie=Parrot&picture=https://img.url](http://localhost:8080/pet?id=1&name=George&age=3&owner=Steven&lastVisit=14-May-2016&specie=Parrot&picture=https://img.url), where *id* is the Pet to update.

 

- **Deleting an existing Pet:**

`drop.delete("pet", String.self) { request , identifier in guard var myPet:Pet = try Pet.find(identifier) else { throw Abort.notFound } try myPet.delete() return try JSON(node: Pet.all().makeNode()) }`

Our deletion route also uses the same endpoint, but with DELETE method.   
In this route we explore a new possibility. Instead of passing *id* as a parameter, we use a type safe routing parameter *String.self*, accessible via the second closure parameter - in this case, *identifier*. It makes our code cleaner, safer and avoids some validations, once this handler alone will be executed when a valid string arrives.   
To proceed with our deletion logic, we fetch the Pet with specified id using *Pet.find(identifier)*. With the object in hand, we only need to call *delete()* and it'll be gone!

 

You can test your inserts doing a DELETE to a url like this: [http://localhost:8080/pet/1](http://localhost:8080/pet/1), where the last number is the *id*for the Pet to delete.

 

- **Reading Pets:**

Our Pet app will demand two reading methods: One that will fetch all Pets, and other that will fetch specific Pets:

`drop.get("pet") { request in return try JSON(node: Pet.all().makeNode()) }`

`drop.get("pet", String.self) { request , identifier in guard var myPet:Pet = try Pet.find(identifier) else { throw Abort.notFound } return try JSON(node: myPet.makeNode()) }`

The first route is a GET method that simply points to *pet* endpoint. As in previously presented examples, it will return load all Pets in Node format through *Pet.all().makeNode()* and return as JSON using the *JSON(...) call*.

 

You can fetch all entries simple doing a GET to [http://localhost:8080/pet](http://localhost:8080/pet)

 

The second route, as the delete route, has a type safe routing parameter that will be accessed through the second closure parameter *identifier*. It tries to fetch the specific Pet through *Pet.find(identifier)*. If nothing is found, it throws Not Found. With the loaded Pet, we return its JSON representation in the end of the method.

 

You can test your single entry fetch doing a GET to an url like this: [http://localhost:8080/pet/1](http://localhost:8080/pet/1), where the last number represents the *id* of the Pet to retrieve.

## ** WHAT'S NEXT?**

I can't believe it! Part 2 is ending!!!

![source (1).gif](https://blog.avenuecode.com/hs-fs/hubfs/Images/Blog/source%20(1).gif?width=500&height=200&name=source%20(1).gif)

Don't be sad! Part 3 is coming!

In this part of the Tutorial, we learned how to create our Model, do the basic operations over it, and also create simple and beautiful routes to the same endpoint that do operations according to the HTTP methods and parameters. It opens a myriad of options to be explored!

In the next part we will learn how to build the page design to allow user interaction through the browser, using *Leaf* for templating and Skeleton to make page more beautiful. We also will modify our two GET routes to return pages.

Thank you for reading and see you in Part 3!

---

[![LinkedIn Icon](https://blog.avenuecode.com/hubfs/Images/Blog/linkedin.png)](https://www.linkedin.com/in/gmteixeira/?locale=en_US)

### Author

# Guilherme Teixeira

 Guilherme Teixeira is a System Engineer Consultant at Avenue Code.

---

### Related Posts

### Building Accessible Web Applications

[READ MORE](https://blog.avenuecode.com/building-accessible-web-applications?hsLang=en-us)

### Create a Tunnel from Anypoint Platform to GCP Using an HA VPN

[READ MORE](https://blog.avenuecode.com/create-a-tunnel-from-anypoint-platform-to-gcp-using-ha-vpn?hsLang=en-us)

### Create a Classic Tunnel from GCP to Anypoint Platform

[READ MORE](https://blog.avenuecode.com/create-a-classic-tunnel-from-gcp-to-anypoint-platform?hsLang=en-us)

### How to Make an Android Splash Screen

[READ MORE](https://blog.avenuecode.com/how-to-make-an-android-splash-screen?hsLang=en-us)

### Leave a Comment!

### Avenue Code Social

[![Facebook Icon](https://blog.avenuecode.com/hubfs/Images/Blog/facebook.png?t=1486470796564)](https://www.facebook.com/avenuecode)

[![Twitter Icon](https://blog.avenuecode.com/hubfs/Images/Blog/twitter.png?t=1486470796842)](https://twitter.com/AvenueCode)

[![LinkedIn Icon](https://blog.avenuecode.com/hubfs/Images/Blog/linkedin.png?t=1486470796556)](https://www.linkedin.com/company/avenue-code)

### Newsletter

Want to stay on top of all tips and news from Avenue Code?

### Popular Snippets

![ac-logo-inverted.svg](https://blog.avenuecode.com/hubfs/Images/Logos/ac-logo-inverted.svg "ac-logo-inverted.svg")

### About Us

- [Who We Are](https://www.avenuecode.com/who-we-are)
- [What We Do](https://www.avenuecode.com/what-we-do)
- [Portfolio](https://www.avenuecode.com/portfolio)
- [Partners](https://www.avenuecode.com/partners)
- [News](https://www.avenuecode.com/news)
- [Events](https://www.avenuecode.com/events)
- [Blog](https://blog.avenuecode.com/)
- [Contact](https://www.avenuecode.com/contact)

### Our Offices

San Francisco

[+1 415 766 4178](tel:+553125161448) [ac.inquiries@avenuecode.com](mailto:brazil.info@avenuecode.com)

Belo Horizonte

[+55 31 2516 1448](tel:+553125161448) [brazil.info@avenuecode.com](mailto:brazil.info@avenuecode.com)

São Paulo

[+55 11 3205 3232](tel:+553125161448) [brazil.info@avenuecode.com](mailto:brazil.info@avenuecode.com)

### We're Hiring!

- [Belo Horizonte](https://www.avenuecode.com/who-we-are)
- [New York](https://www.avenuecode.com/what-we-do)
- [San Francisco](https://www.avenuecode.com/portfolio)
- [São Paulo](https://www.avenuecode.com/partners)

---

©2015 - 2017 Avenue Code

[![Facebook Icon](https://blog.avenuecode.com/hubfs/Images/Icons/facebook-2.png)](https://www.facebook.com/avenuecode) [![Twitter Icon](https://blog.avenuecode.com/hubfs/Images/Icons/twitter-2.png)](https://twitter.com/AvenueCode) [![LinkedIn Icon](https://blog.avenuecode.com/hubfs/Images/Icons/linkedin-2.png)](https://www.linkedin.com/company/avenue-code) [![Glassdoor Icon](https://blog.avenuecode.com/hubfs/Images/Icons/glassdoor-icon-1.png)](https://www.glassdoor.com/Overview/Working-at-Avenue-Code-EI_IE456173.11,22.htm) [![YouTube Icon](https://blog.avenuecode.com/hubfs/Images/Icons/youtube-2.png)](https://www.youtube.com/user/AvenueCodePlay)

/\*\* DISQUS SCRIPTS \*\*/ Please enable JavaScript to view the [comments powered by Disqus.](http://disqus.com/?ref_noscript)

© 2026 Avenue Code

```json
{
  "@context" : "https://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Guilherme Teixeira",
    "url" : "https://blog.avenuecode.com/author/guilherme-teixeira"
  },
  "datePublished" : "2017-01-25T18:55:00.000Z",
  "headline" : "Tutorial: Building a WebApp using Swift Part 2",
  "image" : [ "https://blog.avenuecode.com/hubfs/Images/Blog/Swift-2.jpg" ],
  "mainEntityOfPage" : {
    "@id" : "https://blog.avenuecode.com/tutorial-building-a-webapp-using-swift-part-2-2/",
    "@type" : "WebPage"
  },
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "url" : "https://blog.avenuecode.com/hubfs/Avenue%20Code%20New%20Logos%20-%202023/Avenue%20Code-primary%20versions_LOGO%20HORIZONTAL%20group%201-7.png"
    },
    "name" : "Avenue Code"
  }
}
```