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

6. Configuring a Database
Written by Tim Condon

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Databases allow you to persist data in your applications. In this chapter, you’ll learn how to configure your Vapor application to integrate with the database of your choice.

This chapter, and most of the book, uses Docker to host the database. Docker is a containerization technology that allows you to run independent images on your machine without the overhead of virtual machines. You can spin up different databases and not worry about installing dependencies or databases interfering with each other.

Why use a database?

Databases provide a reliable, performant means of storing and retrieving data. If your application stores information in memory, it’s lost when you stop the application. It’s good practice to decouple storage from your application as this allows you to scale your application across multiple instances, all backed by the same database. Indeed, most hosting solutions don’t have persistent file storage.

Choosing a database

Vapor has official, Swift-native drivers for:

SQLite

SQLite is a simple, file-based relational database system. It’s designed to be embedded into an application and is useful for single-process applications such as iOS applications. It relies on file locks to maintain database integrity, so it’s not suitable for write-intensive applications. This also means you can’t use it across servers. It is, however, a good database for both testing and prototyping applications.

MySQL

MySQL is another open-source, relational database made popular by the LAMP web application stack (Linux, Apache, MySQL, PHP). It’s become the most popular database due to its ease of use and support from most cloud providers and website builders.

PostgreSQL

PostgreSQL — frequently shortened to Postgres — is an open-source, relational database system focused on extensibility and standards and is designed for enterprise use. Postgres also has native support for geometric primitives, such as coordinates. Fluent supports these primitives as well as saving nested types, such as dictionaries, directly into Postgres.

MongoDB

MongoDB is a popular open-source, document-based, non-relational database designed to process large amounts of unstructured data and to be extremely scalable. It stores its data in JSON-like documents in human readable formats that do not require any particular structure.

Configuring Vapor

Configuring your Vapor application to use a database follows the same steps for all supported databases as shown below.

SQLite

Unlike the other database types, SQLite doesn’t require you to run a database server since SQLite uses a local file. Open Package.swift in your project directory. Replace the contents with the following:

// swift-tools-version:5.2

import PackageDescription

let package = Package(
  name: "TILApp",
  platforms: [
    .macOS(.v10_15)
  ],
  dependencies: [
    .package(
      url: "https://github.com/vapor/vapor.git", 
      from: "4.0.0"),
    .package(
      url: "https://github.com/vapor/fluent.git", 
      from: "4.0.0"),
    // 1
    .package(
      url: "https://github.com/vapor/fluent-sqlite-driver.git", 
      from: "4.0.0")
  ],
  targets: [
    .target(
      name: "App",
      dependencies: [
        .product(name: "Fluent", package: "fluent"),
        // 2
        .product(
          name: "FluentSQLiteDriver", 
          package: "fluent-sqlite-driver"),
        .product(name: "Vapor", package: "vapor")
      ],
      swiftSettings: [
        .unsafeFlags(
          ["-cross-module-optimization"], 
          .when(configuration: .release))
      ]
    ),
    .target(name: "Run", dependencies: [.target(name: "App")]),
    .testTarget(name: "AppTests", dependencies: [
      .target(name: "App"),
      .product(name: "XCTVapor", package: "vapor"),
    ])
  ]
)
import Fluent
// 1
import FluentSQLiteDriver
import Vapor

// configures your application
public func configure(_ app: Application) throws {  
  app.databases.use(.sqlite(.memory), as: .sqlite)
  
  app.migrations.add(CreateAcronym())
  
  app.logger.logLevel = .debug
  
  try app.autoMigrate().wait()
  
  // register routes
  try routes(app)
}
app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)

MySQL

To test with MySQL, run the MySQL server in a Docker container. Enter the following command in Terminal:

docker run --name mysql \
  -e MYSQL_USER=vapor_username \
  -e MYSQL_PASSWORD=vapor_password \
  -e MYSQL_DATABASE=vapor_database \
  -e MYSQL_RANDOM_ROOT_PASSWORD=yes \
  -p 3306:3306 -d mysql
docker ps

// swift-tools-version:5.2
import PackageDescription

let package = Package(
  name: "TILApp",
  platforms: [
    .macOS(.v10_15)
  ],
  dependencies: [
    .package(
      url: "https://github.com/vapor/vapor.git", 
      from: "4.0.0"),
    .package(
      url: "https://github.com/vapor/fluent.git", 
      from: "4.0.0"),
    // 1
    .package(
      url: "https://github.com/vapor/fluent-mysql-driver.git", 
      from: "4.0.0")
  ],
  targets: [
    .target(
      name: "App",
      dependencies: [
        .product(name: "Fluent", package: "fluent"),
        // 2
        .product(
          name: "FluentMySQLDriver", 
          package: "fluent-mysql-driver"),
        .product(name: "Vapor", package: "vapor")
      ],
      swiftSettings: [
        .unsafeFlags(
          ["-cross-module-optimization"], 
          .when(configuration: .release))
      ]
    ),
    .target(name: "Run", dependencies: [.target(name: "App")]),
    .testTarget(name: "AppTests", dependencies: [
      .target(name: "App"),
      .product(name: "XCTVapor", package: "vapor"),
    ])
  ]
)
import Fluent
// 1
import FluentMySQLDriver
import Vapor

// configures your application
public func configure(_ app: Application) throws {
  // 2
  app.databases.use(.mysql(
    hostname: Environment.get("DATABASE_HOST") ?? "localhost",
    username: Environment.get("DATABASE_USERNAME") 
      ?? "vapor_username",
    password: Environment.get("DATABASE_PASSWORD") 
      ?? "vapor_password",
    database: Environment.get("DATABASE_NAME") 
      ?? "vapor_database",
    tlsConfiguration: .forClient(certificateVerification: .none)
  ), as: .mysql)
  
  app.migrations.add(CreateAcronym())
  
  app.logger.logLevel = .debug
  
  try app.autoMigrate().wait()
  
  // register routes
  try routes(app)
}

MongoDB

To test with MongoDB, run the MongoDB server in a Docker container. Enter the following command in Terminal:

docker run --name mongo \
  -e MONGO_INITDB_DATABASE=vapor \
  -p 27017:27017 -d mongo
docker ps

// swift-tools-version:5.2
import PackageDescription

let package = Package(
  name: "TILApp",
  platforms: [
    .macOS(.v10_15)
  ],
  dependencies: [
    .package(
      url: "https://github.com/vapor/vapor.git", 
      from: "4.0.0"),
    .package(
      url: "https://github.com/vapor/fluent.git", 
      from: "4.0.0"),
    // 1
    .package(
      url: "https://github.com/vapor/fluent-mongo-driver.git", 
      from: "1.0.0")
  ],
  targets: [
    .target(
      name: "App",
      dependencies: [
        .product(name: "Fluent", package: "fluent"),
        // 2
        .product(
          name: "FluentMongoDriver", 
          package: "fluent-mongo-driver"),
        .product(name: "Vapor", package: "vapor")
      ],
      swiftSettings: [
        .unsafeFlags(
          ["-cross-module-optimization"], 
          .when(configuration: .release))
      ]
    ),
    .target(name: "Run", dependencies: [.target(name: "App")]),
    .testTarget(name: "AppTests", dependencies: [
      .target(name: "App"),
      .product(name: "XCTVapor", package: "vapor"),
    ])
  ]
)
import Fluent
// 1
import FluentMongoDriver
import Vapor

// configures your application
public func configure(_ app: Application) throws {
  // 2
  try app.databases.use(.mongo(
    connectionString: "mongodb://localhost:27017/vapor"), 
    as: .mongo)
  
  app.migrations.add(CreateAcronym())
  
  app.logger.logLevel = .debug
  
  try app.autoMigrate().wait()
  
  // register routes
  try routes(app)
}

PostgreSQL

The Vapor app from Chapter 5, “Persisting Models” you created already uses PostgreSQL. Remember, you created a PostgreSQL database in Docker with the following command in Terminal:

docker run --name postgres \
  -e POSTGRES_DB=vapor_database \
  -e POSTGRES_USER=vapor_username \
  -e POSTGRES_PASSWORD=vapor_password \
  -p 5432:5432 -d postgres
docker ps

// swift-tools-version:5.2
import PackageDescription

let package = Package(
  name: "TILApp",
  platforms: [
    .macOS(.v10_15)
  ],
  dependencies: [
    // 💧 A server-side Swift web framework.
    .package(
      url: "https://github.com/vapor/vapor.git", 
      from: "4.0.0"),
    .package(
      url: "https://github.com/vapor/fluent.git", 
      from: "4.0.0"),
    .package(
      url: 
        "https://github.com/vapor/fluent-postgres-driver.git",
      from: "2.0.0")
  ],
  targets: [
    .target(
      name: "App",
      dependencies: [
        .product(name: "Fluent", package: "fluent"),
        .product(
          name: "FluentPostgresDriver", 
          package: "fluent-postgres-driver"),
        .product(name: "Vapor", package: "vapor")
      ],
      swiftSettings: [
        .unsafeFlags(
          ["-cross-module-optimization"], 
          .when(configuration: .release))
      ]
    ),
    .target(name: "Run", dependencies: [.target(name: "App")]),
    .testTarget(name: "AppTests", dependencies: [
      .target(name: "App"),
      .product(name: "XCTVapor", package: "vapor"),
    ])
  ]
)
import Fluent
// 1
import FluentPostgresDriver
import Vapor

// configures your application
public func configure(_ app: Application) throws {
  // 2
  app.databases.use(.postgres(
    hostname: Environment.get("DATABASE_HOST")
      ?? "localhost",
    username: Environment.get("DATABASE_USERNAME")
      ?? "vapor_username",
    password: Environment.get("DATABASE_PASSWORD")
      ?? "vapor_password",
    database: Environment.get("DATABASE_NAME") 
      ?? "vapor_database"
  ), as: .psql)

  // 3
  app.migrations.add(CreateAcronym())

  app.logger.logLevel = .debug

  // 4
  try app.autoMigrate().wait()

  // register routes
  try routes(app)
}

Where to go from here?

In this chapter, you’ve learned how to configure a database for your application. The next chapter introduces CRUD operations so you can create, retrieve, update and delete your acronyms.

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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now