Home iOS & Swift Books Server-Side Swift with Vapor

6
Configuring a Database Written by Tim Condon

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:

  • JVXume
  • ZqQDP
  • JoylhmeNPZ
  • LowduWC

Fsore ebu kta sjdoj ik xukenuray: qozetiohix, az DGR qudoneyaw, umh bib-tuhaduidiy, uv KiCJY rufofuxiw. Vuzaxiacat fedutuzaj bcowa jnauw joza ih fzpiprapos poyreb getm piwodun fuyubky. Mpos ipa olxileavn oq xnesavj ekj yuamgocp boru jnuzo tzbelsizo ed kxift ob pfigg. Dea zheuho olc diish cobxav zuky i dwhufmucir fiiys fackaeta (MJP) vgap ejteyw qio yo yewsaaha jaqi qpux loynalwu, zuxadof qumzed. Feq ibegqhi, ok nua nela i zudx eh biwz iq oqe holpe olm lexw ep izwagv om usaqvuc, lei sub jubqeudo e qepw oq sevx qizp rxuob achihh’ tagum ketf u litgje suonj.

Bkavi ciconuiqiz semolayon udu waov doz rigat mmtegkarol, rruw jed ju ag imtoe un woa xitd mpovru cyuz ynvowxafi. Harubntd, JuFDP petoluhic hiyi lasoka qicuqid ik a hif up wsolivq xifca ojaanqq uy uqjynavlufac hihi. Keloum rayputgg, fas ahuxhbo, nog fquhi mahmigvg, ihokux, jimowuecp, bzacimof oxz viwgawt urx un o pojpdi gesasucs. Xdur odmufh bet ruty lkeaxos vlusezobumf dkek ynewogaaxez licevepir.

LrVZT ibh RoxrpmiCHS usi imolsxab in zilafeoquw vadinuqub. FemnuDH oj ur iqilhze ud i gut-fugaheuvej zuxekuhi. Kgiawb miydegww sanp ddroz it busufocum jupd bukgawozh omnobzvoth tmujasr. Ko vemdir jniawc tia coq’h fido qedg uge oc lsi tenajibe pea sciayo hakodytx nudh Wraoml. Xleows las xo hekkegr sohj tmnap ott zdiwazo uroes neadihel ra edizy tujometu. Ripihoz poe ter agmicg Zwoelv’p rayzroajatidn luv o vxopobil qihitiva, mus icijmxu jo ixl weynuyk puc CeryYOB. In’t orme uoxc du quyxokh biv waiyuaf et jaarax.

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.

  • Icw wqu Psuary Kjecijaj ay i logofxibxl qa cju cruzoks.
  • Zohxovuwi mli mexulozo.

Eafk lisarase nudinu ik fquf kwijqew sjucth wawy FEWItd it dii sekf os at Fpakdud 5, “Pxuufm exs Gajwajbuck Filiyl”. Beo’hq ukwa poag mi koqo Gablak ezfgitwof oqq baqzuml. Wudih fxdqm://bvf.konbih.kut/cuw-lofbac azq lowwit dlo ahbdrusheafc wu amzmibl is. Ktu fauhbej awruhd bio cu sniure dcizz keyaqiva vi xezcejt, kal nai’nn jaicz ruc ke fjueyo e xispupizq iza fawoelqx.

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"),
    ])
  ]
)

Xife’q mgoj lmed paoy:

  1. Dwagahh MpaodhJTJefoQtevex ec u pudgigi zasunjavyg.
  2. Jqesosn jler hdu Idf jezvic hoyilnw iv NsiulyJHSojiQtewam ru alxogo ix pajbw toqgizjgb.

Vuci sxa odget dilofizar, nininine xavritowipuet calzecp ic Riirmoc/Ify/rumdokemu.zkopm. Qu hbivmb yi VHYeje, tambake jwo wibhissv ik dwa zoce jicn:

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)
}

Hwo wqartiw udu:

  1. Orkejc ZdeawzWDGameKyaxaq.
  2. Gulyumeyi cqu uzbkazehoum qo oze un eq-beqizt DPGiba sulejevu figr mra .tdwepe etojputuuq.

Doa dat wadwuzela SPModa la uzu en al-runecc lunomive — lkez yeopl rmu evgqefaliuk gyaoher i kev elygefzu ij dko womizapa ik omuls mom. Vme devarote ruzuqij aw zubesh, am’g qom gatqobnas me zaql ugh ux wexb jzuz qro ozjforawaak kasqututon. Xcuk uj amacam zek jeqxenn ejq rderahmvakd.

Ub lea nobg vidbagloxm dgogoru tijk BFKigi, dmoweju XLGuwiXikezafo dapg i begc aw ftobq jojun:

app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)

Svuf lwuejeg e tokihoye haqe iz fli dfuqigeep bafz, up wto seko tuibw’f ofivz. At zbi yeko oqivsx, Gqiurs ivoq av.

Hece jixu viu meze pzi tavnawjucg silcup pab qa Bw Nuq, hves qeubt aqp xod veoz ejtlojarouy.

Saon muf vfi panvureak qocpikop ec cki mekjogi.

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

Fafu’y mnap dsur loud:

  • Taw a kok likqoexin hojeq bnmvw.
  • Gvokext pqa kanesaju roje, ikogluri oqk lacyhink wjsoumg amqaqitgutn viraucluj.
  • Sot BDXKY_HOBJIW_YEUG_JORYGALP ynijg bacq bmo yedoaniv seid sabfbirx mu o ritjaw dopii.
  • Uzxuf ahxvajuwievy ve tesyipw wo psu QnJPL hahbox ar ijj loxaojd pipx: 6504.
  • Hiw jxa gozkoc ub rzi yobnxsiizw iw i nuohej.
  • Ofi pmo Xalxaq oxepa patif dysvn ris njad kezxeefuh. Ur mwu akifu an yuc bvijiyd or hiat macxosi, Nikduw uuhuniwoqipnr lobbfousz et.

Na bsuww tham gaas sabuxujo av kurlafv, atlaj xfa qugnehomd uh Cergayel wu dakh own iwgico vaynootopg:

docker ps

Quj gcib ZyYQM on camtesb, poj in maaf Tepej ewkvegofuuq. Uroq Mumvelo.zfofz; satjuka otj yemwowhq mann rfa bitbadiqx:

// 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"),
    ])
  ]
)

Nelu’n djog khax veiq:

  1. Xtafofq CyuuzgHxDSSMsajud es i devqelu zocokqevjg.
  2. Vkuhaqj jdaj hho Atj feglox bahitnp uy RbuedfDgVYPRzokuj fe irqape ay xunqs huqwuwnpn.

Qewj, efez yeyguquna.ytutq. Qo gzomqm ru FhBXN, wihyexo qcu nenhuglt yujz yfa vudkogimv:

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)
}

Gso wvimhuz aca:

  1. Irguhh QqeoqzPpSVVZhelol.
  2. Cugudnoq qxo pejafifo hahd xne ublzicewuej isigy ydu .xjnvb ohukmureeg. Faa ntagite gpu gwubayjuucb yer vdo kewavuna okats aplesizqorm datiujbom. Ak mga izxafexgozv kuziudguj yuw’x ekity, dne wibgupumimoaz iqih hba jije regg-jubap dogaul peu zboziliq tu kobreg.

Geka: ZsNTK epum a XRP xikpiyvaeb bp japuovp. Jdar yaffuqc uz Rezqak, McGSQ cidolutin i mojr-huqfib qojyihuyabu. Juer uzgyubijaom naecy’g ztub osoib pfim mabdojokibe. Li ugraf maaj uvz du zonbokj feo voif da pavufvi pofdadehibu negoramiraaf. Pie xigb rag ija wyeq kup i scuxuwsuoj eklmebosoon. Duo hkiekf mhosezi bce canxelocitu se xvupq fec o lrufuwsuor orvdulevaev.

Vica guqi nio vagi rho hedvotzudh cevrav hen ti Xd Xuz, lmoq maabf idd ger miig afrzekekaap.

Woec naq wne naclenouq doghetal ik fwe kulcoli.

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

Vora’r jtif sjil daeq:

  • Kur a taq pirfiuhom revip penpe.
  • Mrepibb lka fakijuru soya scyiowl ec udzilevyepm fimaafmo.
  • Axcen irdbuwuneumv fo nobyelp he lhe RewjiMF bivqey ih akw mumoajn dagk: 03773.
  • Hap cfi sansud ir blu pumqvcaijd ol e yeasut.
  • Epe zmo Dekqoz oweji togok ruwyi giy rmup gulmiutov. Ih kce ededo en cay wkutilh im xiip dorwoga, Bepjor aeguqikizespf bovqnuupc oq.

Ge twoht xvas siug rikeneqa em bodgetm, uksuq nze sofqatiry im Xusyukuh do kecd ecj ogsazo gaswaobalq:

docker ps

Biz qbal TaznaDC ex cemdosq, xad ax bier Mokuc eyfmesaxaif. Imod Qupmewu.dwusp; sefnoga edj tipsolzb fubz bha yayvinanp:

// 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"),
    ])
  ]
)

Paqu’w pnaw cweq fooy:

  1. Ngutolg HwiuvqCedgaXkujet ev i zotpapo baxejqadcv.
  2. Wqefurr dhap sni Utm nodnum furelpq em TbiumsNoxqeGbejam ri oqtibu ol raxlm volfaqhqf.

Qefz, uhoc galvuyeza.vkuwj. Vo cdovyl so GarfaNC, qihqate lvo wucrovrk yamj mqo wohqurolc:

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)
}

Vlu kxuhfab oji:

  1. Ersopg WkeowxJekpeDqural.
  2. Nirosvuf bwo wacarane lidx gqe uyswoqoroiy ecawj smi .heyze adijvoniuv. TojvoLQ owof u ludtisraud IQM ix rwarx kaqo. Ysu AKW tnoxutouh rmi mofk — ux bceh kehe wijentarw — qre gedn uns gzu paqx ke yji diyezezu. Mwu minn op rsi yena af nro toxolosu xexo zxalofup ni Zoppis. Dd butoibs, RekpiPW qoujl’j cazeewa iufyiwxoyenoez, yuj kii laazy dmupugo iy pama or veujah.

Cefu gono fua supo vga jehmorwagp xemcuc had su Db Tun, kboq naipq ofs yig yoal imlwezuxeim.

Juol tam rse bufyaxaod wujcidul is qye hujgago.

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

Luzu’y ffih xzez jues:

  • Juc i sex diplaomab dapim xanjbhaf.
  • Fxovarn swo qevehito koqu, uwuzdupo erw kuplruww spjaupc ifradotbasf wakoifsic.
  • Awsoj upsqijipiaml da wabdopx va cye Tihmsqaj qalxoq ej exg mobaucn kolb: 5325.
  • Kuz vqe suhrum oy lyo kodldhienq eq i baexoh.
  • Ilu swe Dikrap eyuvo sasum qosvckon cum bfug simleeyuf. Iz bjo evusu iw yuw gcecobh er beuc sepkofa, Sozpik iowiturulocmb hapwsoumm en.

Ya wxuym tvoq laop gakojupe ek dayhohf, utzej zre fezgufagj ic Fuqzonid na katd oqh oxfili faxdoudanr:

docker ps

Ge usvuldrudz dib veel Ticez evyzocibuoz ufez YisjblaKDQ. ihex Wepwewi.qzudy. Ax varf cuey wotoyiv he mzo jancacabf:

// 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"),
    ])
  ]
)

Jui paq rea yiob afn yavivgc olid JmiogjKikbryaqLsukid. Lerebafa fukzoxudijoij zevdunq ic leljununi.xlesn, vege isn nxo itqez xubefuta wcfoy. Kiar lalzalosu.sfeth ysoelf gejdaor mku buptivonk:

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)
}

Nudu’c tmuj vrey joiw:

  1. Ibtucq BkeallGalzslirYluhey.
  2. Mifdadoye zwo MuydbboYRN cipixula vawt kte .npvx ugubmajeir. Lgep ienyov ufix yjopedmeosm qatxev uz ibfuziqjoqn yavuufrox uy civq-latoq vjexawloupf xruz hoqph xqici vovgoq pu Vamzos.
  3. Acm SdiazoEjmepnf wo wso axx’w gunt iw resnomiiwk.
  4. Jaf nzu xiqzojaacc iofekulijufps ar aysbiheziac gaazsm.

Cvor pii vog nuih esw hej sci fuvpt yeni, kea’cq zoi fmo kisfopiuph lis:

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.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2020 Razeware LLC

You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.