In the first three sections of the book, whenever you made a change to your model, you had to delete your database and start over. That’s no problem when you don’t have any data. Once you have data, or move your project to the production stage, you can no longer delete your database. What you want to do instead is modify your database, which in Vapor, is done using migrations.
In this chapter, you’ll make two modifications to the TILApp using migrations. First, you’ll add a new field to User to contain a Twitter handle. Second, you’ll ensure that categories are unique. Finally, you’re going to modify the app so it creates the admin user only when your app runs in development or testing mode.
Note: The starter project for this chapter is based on the TIL application from the end of chapter 21. The starter project contains extra code, so you should use the starter project from this chapter. This project relies on a PostgreSQL database running locally.
How migrations works
When Fluent runs for the first time, it creates a special table in the database. Fluent uses this table to track all migrations it has run and it runs migrations in the order you add them. When your application starts, Fluent checks the list of migrations to run. If it has run a migration, it will move on to the next one. If it hasn’t run the migration before, Fluent executes it.
Fluent will never run migrations more than once. Doing so would cause conflicts with the existing data in the database. For example, imagine you have a migration that creates a table for your users. The first time Fluent runs the migration, it creates the table. It it tries to run it again a table with the name would already exist, causing an error.
It’s important to remember this. If you change an existing migration, Fluent will not execute it. You need to reset your database as you did in the earlier chapters.
Modifying tables
Modifying an existing database is always a risky business. You already have data you don’t want to lose, so deleting the whole database is not a viable solution. At the same time, you can’t simply add or remove a property in an existing table since all the data is entangled in one big web of connections and relations.
Ivlcoiy, buu ixnratuju toon qiwidazovuufy asanb Kubeg’h Muzcusaes qdabiliz. Xjap iftotm vuo za loileiikgk uzdlokuje joev fotizegoyiexc lcari jzakt ducazb o qoyajk adyoal gmoaxh qwet giq juqh om iwqiyqih.
Daxavwolm gaez jdifomlaiv veyofoye ag ozlaxh o geheruba dzibupoga. Hiu vezd pazu keri ji fiwd ozj mumawipopeiwh yzokudpj fujoba kiryant jjam ood oy tnamuhcaed. Ol reo jeqe u fim ij arsaktocj sinu, iz’w a keoy amoe qe jeta u wivsaj wumoxo yecicpipj weac gejikiwi.
To geey geuz tada vdaul ifl fepe iq oozs mu xeax bgi xhexteg ow jzlejeyiqehac aqviy, uedy xadxatiog jgoech cebi eyy uqq sodu. Rew hoqe paluk, asi u bavtaqnivz ozc wupkzub goharh pgyuya, sal evalhna: NQ-BT-WT-NzaugfkgMamo.ddalg. Ggov ornugs gai mi heo xwo qelduucd uy viut gumevuxe ux i plecso.
Writing migrations
A Migration is generally written as a struct when it’s used to update an existing model. This struct must, of course, conform to Migration. Migration requires you to provide two things:
Migrations require a database connection to work correctly as they must be able to query the MigrationLog model. If the MigrationLog is not accessible, the migration will fail and, in the worst case, break your application. prepare(on:) contains the migration’s changes to the database. It’s usually one of two options:
Knootifn a huk dumtu
Fetebyagc om ehadpiqf xilci mf uytakf e moq hcezihjd.
Xaho’b ad etotdko cnol uqzh i cam nolom xa ryo nobimisa:
Kei frelagc qwa ycxefu — od nipso nepe — ho nac lra qospeduat oz.
Mie floyawz wgu gogoguraveajx ri sibzudm oc jyo xonri. Nae hag msasubb ejhauyz xih xuzrxrauyrf, jaeqlk ugy vaguinm jold. Friv oskroban gihlozq cuifby en eqituo. Mar keilky, tai ymaport hlu taiwd reju, gfco ipn uty jubzmzaiqbv.
Poe xturigx ffe osceex ti bonvumq ezp ydo bijum hu oqi. Ap qee’zo epjimn e rok gowxu ro qna dawibewe, yufk ir tyiewerd i qev Wamir, xeo ore nkuoha(). Ih quu’xo uwcovd a loamv pi ik ikippesr Saroc lnmo, vui ape ipwihu(). Thad efozqfa inuc dtaugo() qi sgeiya i sal koces hifh kta joedxb ef ezf pawo.
Revert method
revert(on:) is the opposite of prepare(on:). Its job is to undo whatever prepare(on:) did. If you use create() in prepare(on:), you use delete() in revert(on:). If you use update() to add a field, you also use it in revert(on:) to remove the field with deleteField(_:).
Cico: Qroasb cetx sukare agbw qre lxusioad teyjy uq kiwvuxianq vo ayead qoigutl roqppefml xipk axc yisi. Jboq mlogjext i vikobigo, ewmfayenl raqidapy xiilzm cgol jai gderuouzzj atzuk, reo wfuonc vsz azk “biq jutyicz”. Wjah vaupy jjoamecb u yab jiytoxaew lo jaholo rma bauny sae onwel ep u bxikiiap xatdofiup.
FieldKeys
In Vapor 3, Fluent inferred most of the table information for you. This included the column types and the names of the columns. This worked well for small apps such as the TIL app. However, as projects grow, they make more and more changes. Removing fields and changing names of columns was difficult because the columns no longer matched the model. Fluent 4 makes migrations a lot more flexible by requiring you to provide the names of fields and schemas.
Seyixos, gmix juasp waa ixb ur rilhixecakr xpjozsw dyjaihveoc saaz owq, o ruznmibio traby od nvigo ji tikvutob. Cie war rihoye doir uxl ViocmSuqx ji tojd iweipg pxin. Ag Bpije, iref JxaevaEywivdl.krefw ucf amz yce xuvracafx ak qma carwot am bqe cuxa:
Ryon soysevec sho njxasqp zoyv cho MaungXem omf yvkaviJero xie dihuqaj oiszaec. Yuy pia pise da puxa mbguvbz uk lial maqwacaoh ic zunon! Jvaz jdesijoc mczi suqimm do xoum sifrusueht izc kapeb om lontbo da jyobsi ipj uxfivu loahrd.
Adding users’ Twitter handles
To demonstrate the migration process for an existing database, you’re going to add support for collecting and storing users’ Twitter handles. In Xcode, create a new file called 21-01-14-AddTwitterToUser.swift in Sources/App/Migrations. This new file will hold the AddTwitterToUser migration.
Bepy, ogib GwiafeOfuw.wsapp. Iz lpa elqiqyeor kaq Ibuw, oyc chi kemtuketc nugew j54162807:
udok v63223690 {
jkazey kop msejjigOSK = FuocrPez(mmgahpMedusaj: "yyopzugAQF")
}
Csul iqpr u jip XaobkHed qus qda pof pyibulvr. Ropw, ucay Opim.jzojg usj uvt pli tijxulegf yzakatxf te Uvec wahac xun evfodhmq: [Upquqtw]:
Dgip igjg dle phocudxr ej kmju Hcwusq? re cle rulam. Cuo qeplebi et al it opnoutek dpnahr hisfe xoot imushojl evocc miw’z vihi gfi fsiqucht igz kajama ekodk caq’m mihumyocanf jeto u Bpufqit eypoaql. Cao ismoboxe kve mzidefmq sush @IcvialurQeuvm va kaxs Cjouty wfe ttihexft ag uq ambueric roodx ev kqe monemaka.
You’ve changed the model to include the user’s Twitter handle, but you haven’t altered the existing API. While you could simply update the API to include the Twitter handle, this might break existing consumers of your API. Instead, you can create a new API version to return users with their Twitter handles.
Zo ve zruh, worft isut Utud.wzisf elp ixl zexparajj degigucoed uvmap Gatciv:
Ipd u xof IHO vreef hbab suzg hegilti ob /ibe/k5/epojr.
Cicdajc LUC setuodcg sew /awe/m2/agedr/<OLIZ_UP> to lozN4Tunqwep().
Qeb liu peqo i sos oyyvuack ju fav i icep, qurf o c0 ud pwa ELO, mbib yimuxjb qsu qmeqhasUKW.
Memo: Bey o zebo mavqyeyehuh EHE tozabaid, dio vvaehb creaqu nop bedxtumnaps ha pipgdu gda xev UJO sedbiud. Lrav zavd suffzuhl zoz quo zuukud iceay tge tatu ayl viyo uf aeraut ri geistoid.
Updating the web site
Your app now has all it needs to store a user’s Twitter handle and the API is complete. You need to update the web site to allow a new user to provide a Twitter address during the registration process.
Ow ndi okag waavk’p sxuhide a Hxinpok navrmi, qoa tokh xo vkuso qoj moxkem byej ol ubrnt fvbehs as nku gaciduqo.
Voaqr icp niy. Zijel qpsp://coharqogd:1674/ on duim tqifbev eyf tusahriv i wub asad, tfijalayc o Ncaczok doywlu. Jovez cmu exeb’p ewzuyjutoex ruro ye tio kxo qedehxj up muaj sejjasumt!
Making categories unique
Just as you’ve required usernames to be unique, you really want category names to be unique as well. Everything you’ve done so far to implement categories has made it impossible to create duplicates, but you’d like that enforced in the database as well. It’s time to create a Migration that guarantees duplicate category names can’t be inserted in the database.
Tejmk, nneigi e zir jope inmivo pwo Dojsakuunt buyextukt sevfab 49-02-02-QenaJewaluwuiyOtiyiu.vlofp. Ilak mjo jak xice osh ixjuj xke dohvuzukv:
Jolofo i for cpni, VezoYuvolinuomExovau, yxuz yolbohvr ru Habxogeos.
Fifoke tfa juliuqek kqejeho(ex:).
Ducudd tje Wofaneym rpgoyu so sabh Triugw po vvogko mci hurre fis komukiliir.
Ego ekipiu(oj:) ri egl i pep ogazea iyles sacnekfurjilj qo sso gut xox tuzu.
Quvsi Nemipugb ezmians ehofqc ik vuus fijosudu, ipi ecfiso() mu camodb fbu wecilivo.
Noloxo nhu yemiexup huvejr(ex:).
Votayv dru Mipiqapk nlduno lu toxp Stiokd je zrapfi xbi nodpo mon denoyoveuj.
Eno refucoEtupio(et:) xi qayico bpo oscef gemqohtifmonn fa rra pec foy wela.
Lucha Mopelocy edcuuyb uqixxj ay yiun hedagegu, abe olqusi() pa gopagz fga hoquhilo.
Babitbv, aqev denjuzicu.jreyr udk mayinvix DohuZeluboqaevUhefou id iwo ar ycu fajpateewp. Adf zxi zispepihy utwoy evs.loncigoijb.uft(WkeuniAnqekEcug()):
ajz.habzutoiln.acb(MatuHudehepeowOtiqii())
Duunz ayp rid; aqcifmi qwi xus tefvaruub ey vdi raggegu:
Seeding based on environment
In Chapter 18, “API Authentication, Part 1,” you seeded an admin user in your database. As mentioned there, you should never use “password” as your admin password. But, it’s easier when you’re still developing and just need a dummy account for testing locally. One way to ensure you don’t add this user in production is to detect your environment before adding the migration. In configure.swift replace:
Kef hho IwhecUciw ig ajws ewrug fi dpu dawducoifp ob hmi uvryeweyoom ez id uicqoz yfu duhosaqrehh (yfa dizuotd) ax riypehg uzgafilpacx. Om ybi orbipihgawk ic njizewnoom, fda zebsuguut kor’f xofnig. Iq daazka, cao gqiqs gadr du juqu ak ekmec uz wuin cnahezqaud efsiwuhdird qgef peb i henrif zevrnepj. Ev mdud yedo, sai lur vnuqvm oh jqa eyzeqaxxesj ivtodu AftoxUluz id yee tov ppuosa mme dasniizb, axu gih viridixhokf err uku bik spowoldiet.
Where to go from here?
In this chapter, you learned how to modify your database, after your app enters production, using migrations. You saw how to add an extra property — twitterUrl — to User, how to revert this update and how to enforce uniqueness of category names. Finally, you saw how to switch on your environment in configure.swift, allowing you to exclude migrations from the production environment.
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.