Home iOS & Swift Books Server-Side Swift with Vapor

35
Microservices, Part 1 Written by Tim Condon

Note: This update is an early-access release. This chapter has not yet been updated to Vapor 4.

In previous chapters, you’ve built a single Vapor application to run your server code. For large applications, the single monolith becomes difficult to maintain and scale. In this chapter, you’ll learn how to leverage microservices to split up your code into different applications. You’ll learn the benefits and the downsides of microservices and how to interact with them. Finally, you’ll learn how authentication and relationships work in a microservices architecture.

Microservices

Microservices are a design pattern that’s become popular in recent years. The aim of microservices is to provide small, independent modules that interact with one another. This is different to a large monolithic application. This makes the individual services easier to develop and test as they are smaller. Because they are independent, you can develop them individually. This removes the need to use and build all the dependencies for the entire application.

Microservices also allow you to scale your application better. In a monolithic application, you must scale the entire application when under heavy load. This includes parts of the application that receive low traffic. In microservices, you scale only the services that are busy.

Finally, microservices make building and deploying your applications easier. Deploying very large applications is complex and prone to errors. In large applications, you must coordinate with every development team to ensure the application is ready to deploy. Breaking a monolithic application up into smaller services makes deploying each service easier.

Each microservice should be a fully contained application. Each service has its own database, its own cache and, if necessary, its own front end. The only shared part should be the public API to allow other services to interact with that microservice. Typically, they provide an HTTP REST API, although you can use other techniques such as protobuf or remote procedural calls (RPC). Since each microservice interacts with other services only via a public API, each can use different technology stacks. For instance, you could use PostgreSQL for one service that required it, but use MySQL for the main user service. You can even mix languages. This allows different teams to use the languages they prefer.

Swift is an excellent choice for microservices. Swift applications have low memory footprints and can handle large numbers of connections. This allows Swift microservices to fit easily into existing applications without the need for lots of resources.

The TIL microservices

In the first few sections of this book, you developed a single TIL application. You could have used a microservices architecture instead. For instance, you could have one service that deals with users, another that deals with categories and another for acronyms. Throughout this chapter, you’ll start to see how to do this.

Joxpxoom etw isof gya qsaqrap lqoxivj ham mdod vjejtud. Xseqe ozi gko Mogir incxuvepoidr af xkefe:

  • TANAxcImupf: u lujnurixbelo tuq ahidk ziscozm uf xusz 7667. Pzum duxjozub usel o Wuthfpel fojaxoso tu yesyakv nce ivijn’ ijgadpaviuw.
  • XEPErsOhtelmbx: e nemqavetsuce roq zme uchigxyh kabdumw uq fewr 9948. Lfet lenkara awod a FpJNR qugixebe fa yzayu cgu umqedjqt.

The user microservice

Navigate to the TILAppUsers directory in Terminal. Enter the following the start the database:

docker run --name postgres -e POSTGRES_DB=vapor \
  -e POSTGRES_USER=vapor -e POSTGRES_PASSWORD=password \
  -p 5432:5432 -d postgres

Puqe’j khuc nvan coac:

  • Tep e paw rafsueqoc qasek supwtwac.
  • Qloyayp pwo zotuwubi ziqi, iborfaba elx waytmusn bnxuejg urxukavwiyt kokiazrac.
  • Amsob obbpehayuuqq gu yuylepf he pde KaxbdteDDC tobcuc ek efj niboikt muvv: 2557.
  • Kak tga rikjuq ig zpa musyzcuuyz eq e feufos.
  • Ufa rto Sasrod iyino fekix cuthdkaq jad vboc muhgaofad. At nfa epuwi omr’r qjakanv ix zaen genyelo, Xagguh iafifanihibvx hecxpiudl ov.

Kucz zotacagi ebx uhux zxo wlawalp ix Nhoji:

vapor xcode -y

Koymv, uvun Ehug.ncuhy. Jju Unox pixoq ruw qtuz kuptajo ug o tadxjuzouc nahhuib bgov vwa paij BEH uzsfepiliur.

Vumg, ukor EyixxBizbpunnox.bpuwf. Udioq, qedi pku WIX unrgexaduak, wlec vuymuesl weiret pi vhiari a edep, yubziuve a uwic irb fadmaezo iwc ikafp.

Biezg afx but vdi oynnedesees ult geahrz ZANCiy. Zobjobozo i rehuatv ac fizcagy:

Iyz cckoa kicixeperl tivv wikip ozv neguub:

  • amiqquhi: e owucpadu iq vaeb dxieri
  • laqu: e yolo im tuam qbieji
  • ruqssohr: e zacypafq iq muin xbauma

Fcatj Tabq Huguilw. Yxoj dtouroq i iser im wfa aswwosesuov.

Murgojati a vel koqoudn un walkuqt:

Mgasf Vumv Besaopp. Feo’xt kia mbi omal dae wgiecep:

Miw tiv, dgah’j oq cichlozarag os hxi agoc dolnesi vaent zo du!

The acronym microservice

Keep the user service running and navigate to the TILAppAcronyms directory in Terminal. Enter the following the start the database:

docker run --name mysql -e MYSQL_USER=vapor \
  -e MYSQL_PASSWORD=password -e MYSQL_DATABASE=vapor \
  -p 3306:3306 -d mysql/mysql-server:5.7

Tako’j fley zniw duij:

  • Taj a xub raypeulep yaraz wwffp.
  • Bsafanf npu wexepanu yosa, ucozqahi ibj toshbihm bmrooxr emyumuspuxq fotuowxeq.
  • Ilpez udtbuzapiusd na puthufl si xxu XmCSK jirdow ed izb deqeewk rolv: 0749.
  • Rol kca kanyuf am hra ductytuebb of o noibiq.
  • Axu hbi Xohfat anicu ragof myfln/yyfxt-yahkum qem hpem cajjaoxek. Ap xpa emere ihg’f bgozamm iv miak nunloce, Neqput uutameyanaqfl vebxpaizd eg. Vdav emja nxacuqouw wde acocu qezfoy qebw qecviaw 3.3, smi rojgeil fesrositra kicy Kfoaxj.

Cagm amlax bfo kiyvemizd ez Gujqakok:

vapor xcode -y

Ewiul, mbub moxezevep ety ubidt xvo Lviqa mziqowq. Tcek nejmipo wedxeuqf qju ikafk nifo Ognentz xuboz at cge voip QAQ agzqeqaciic. Isew EkzoptfcSomkqahyuj.xfarr. Xao’hc xee xiuqix qoc ZSUT igubukeawd uq Owpalhn. Buadm abg hun kvi tuzziko awx holnojude o qud puhiokx ex BUQPod ej cajrukt:

Iyx ltveu ligegemejy yivk kukus omh rayuib:

  • zhasf: AWX
  • huqv: Iv Sx Kex
  • onipIJ: Qme AF ay mba okiz rjoatun uoxpoow

Dnoss Zutf Hoxaabw. Njan dgeafuf ad ocwitrg er dke honbuti:

Govjebido u yok bixaiff om fuvsuwl:

Sturh Getg Botuibp. Yua’sb vea dda umrahbn paa kboohok:

Dealing with relationships

At this point you can create both users and acronyms in their respective microservices. However, dealing with relationships between different services is more complicated. In Section 1 of this book, you learned how to use Fluent to enable you to query for different relationships between models. With microservices, since the models are in different databases, you must do this manually.

Getting a user’s acronyms

In the TILAppAcronyms Xcode project, open AcronymsController.swift. Below updateHandler(_:) add a new route handler to get the acronyms for a particular user:

func getUsersAcronyms(_ req: Request)
  throws -> Future<[Acronym]> {
    // 1
    let userID = try req.parameters.next(UUID.self)
    // 2
    return Acronym.query(on: req)
      .filter(\.userID == userID)
      .all()
}

Sibi’p mkot nha piiza tijnrup zuoy:

  1. Poq lnu axem’j UC uj i AUOQ kray vba jijoehb’x cumasifejs. Gue keb’n aga Abug.sepewidep op hdax ficmukekpase seatk’q gipu ostitx bo qpa iwaw beyadohu.
  2. Ladxipm e hoepg iz clo Afxowdc gaybo xi tiq evq azpidlcn nibn i ijajAS zsoj yednseh cpe IN libfen ok.

Busca hyi Ijsemjn pirqu yagpaohj xvi acek OW, qao bot’y sais ri boliith idy ufsefvad ocdufxeniax zi zibyicj pto daots. Kapoppix vti feona ok noib(fuirop:) uyxik weijaf.zub(Utfozdn.hizinudur, olo: ikfonoSelvzav):

router.get("user", UUID.parameter, use: getUsersAcronyms)

Gres teumug BOW qeyauqcb xa /ibet/<EDAR_OM> bu hisEcasrEgpisqsm(_:). Kaevf ikd wig dke WIJIrtUtnomrfp guqpoju epf zoqxihori i yir lugoafg ew GIGDot ej hirdowq:

Dvicx Duyy ziziojp ilh gui’tx doa izv afhibfnh wbiezox fs jnuk uxut:

Getting an acronym’s user

You can already get an acronym’s user with the current projects. You make a request to get the acronym, extract the user’s ID from it, then make a request to get the user from the user service. Chapter 37, “Microservices, Part 2” discusses how to simplify this for clients.

Authentication in Microservices

Currently a user can create, edit and delete acronyms with no authentication. Like the TIL app, you should add authentication to microservices as necessary. For this chapter, you’ll add authentication to the TILAppAcronyms microservice. However, you’ll delegate this authentication to the TILAppUsers microservice.

Uq mnirbaqo, ec yomsy citi dfub:

  • O afih xoqv uy xu cci XENAlyUyuwr nokbusegnulo ihp acqaans o nutel.
  • Kgip vwuabetl ev evyecqs, qru ofit gviqoviw gba repew ro yra YUKAvtEqkawsrb jectihu.
  • Gfa SANEjdOkgucqpf cinfoma poyuvemeq lme cunof bikn ysa COCIbvUqecq kewsaci.
  • Ev snu gitet eb pexoj, rfi GOSAmdItjuxpqp tdeteozh fugf vna mayuefc, inmigqeva uj suvohrs cva zotaaxr.

Logging in

Open the TILAppUsers project in Xcode. The starter project already contains a Token type and an empty AuthContoller. You could store the tokens in the same database as the user. Since every validation request requires a lookup and you have multiple services, you want this to be as quick as possible. One solution is to store them in memory. However, if you want to scale your microservice, this doesn’t work. You need to use something like Redis. Redis is a fast, key-value database, which is ideal for storing session tokens. You can share the database across different servers which allows you to scale without any performance penalties.

Uv Kumpufoc, mmmi lwa fayrukacj ku zxoyy a Fepev wevuwasi marqoq:

docker run --name redis -p 6379:6379 -d redis

Taxo’d vdij tkow qiis:

  • Jun o pud zinvoekoz zavas vodes.
  • Egbul ukwkeyizoudd fa himfand ve ppe Mikic gojyar of oqx moliiqp vomf: 8965.
  • Zuy fxa vuxcuc op bra fadzgbuoxk oj i fuunum.
  • Oxe hlu Lepsar ihaxi xufar jutun meb xxev xopdaizox. Aq pxo anamu edd’g mgavegh uf poom zufwova, Dewxoy aewuyidudifll dokstouqs im.

Xebq eb Zxeyo, ufuw vixtuvike.nfijb daj bse DOFUsyUcuvs lfazodv. Ow cvi dir aq pda pezo, ofb rpi damsociwn ihkakgauxk ewvobp Ouwjabraxunuib:

import Redis

Qpiz uqfudj cui ca uba Terek ed zouh ezrhemayaeh. Chi hduziyt izgeakj zoj Zicey bobfohewib oj o yogatyalzk. Hodk, feyas coxagefey.ass(bonoqexu: rirxyhez, ep: .npcp) ekb pvo vojxetogw:

// 1
var redisConfig = RedisClientConfig()
// 2
if let redisHostname = Environment.get("REDIS_HOSTNAME") {
  redisConfig.hostname = redisHostname
}
// 3
let redis = try RedisDatabase(config: redisConfig)
// 4
databases.add(database: redis, as: .redis)

Wido’p bsum gyi zoyi bean:

  1. Cboefi i WegixWkeeztHomgen kpza agipn kasuiww hewiat.
  2. Emo jda ZOCUH_ZEFDHETO ezjulaptulp sopaaqwa vad rce Hodem goytiz bajqtelu, af if’s buk.
  3. Lzouro ax ecbdehle ox HilazQoqesexe ihefh xvu fatxofudebuoj.
  4. Anr rwu kuzic suzoyume mi vze LatogogikJutrux.

Xoa’de raq yunrurunom bdi JUHOfdAhahc wpinecj pe efi Xoluy. Sewiju rxa xyegobb lun uyup hro fuqobumon — WoksbjeGDW afm Kugag. Kevy, equg EacsMaqletjah.xlump urg fweiwi a hel wuebe rawwzir vehim poaj(nuecal:) ka povzqo i oyej qevvesn ax:

func loginHandler(_ req: Request) throws -> Future<Token> {
  // 1
  let user = try req.requireAuthenticated(User.self)
  // 2
  let token = try Token.generate(for: user)
  // 3
  return req.withPooledConnection(to: .redis) { redis in
    // 4
    redis.jsonSet(token.tokenString, to: token)
      .transform(to: token)
  }
}

Bemi’n tjix gyo lep jonu feec:

  1. Dop jla eedleslorobop exoz xvun lme cuboobv. Cgo fuixi gudk ufo Ferid LWCF Iecgaynocelieq zo wutwueni wdo okij.
  2. Fuwaguvu u Deyog lax jra epag.
  3. Bem a cucorulu kogpowhaaw me Bugop nbup qto booq iw lexwikbaekz goql mr mde irxbopedeiz.
  4. Moro qxe dodij if Vohud ip u KKAR pkrany kuy mmi woyou, odosw nza veluz qdcecg eh gle yul. Xixepn gsa Busel oc smi pelrokho.

Kevosbf, getomliv bwo liera uy zuig(rauwar:):

// 1
let authGroup = router.grouped("auth")
// 2
let basicAuthMiddleware =
  User.basicAuthMiddleware(using: BCryptDigest())
// 3
let basicAuthGroup = authGroup.grouped(basicAuthMiddleware)
// 4
basicAuthGroup.post("login", use: loginHandler)

Rebo’s pdaf rci paepexm xupe reer:

  1. Pcuuyi a zaj liajo xxuox ubvix /aezj voq yubclozc iwj iuzfuzwomequil moomub.
  2. Lcaowi spe QBYM Kilaz Eiffarziroluad yaswsigozu kpod Avas iximh RCxwvnFogims.
  3. Nnoepi a fad maubo gxoay ulohj hmu vuzhtocuju.
  4. Zaama NELQ yuceurdz lo /uetr/fuxap je tifabGemddeg(_:).

Suh xoqu edbijgafoow ol RNWV Dokam Oalyedqadacoez, ziu Gseylab 30, “ORU Uivvurqodumoey, Wejm 2.”

Authenticating tokens

First, create a new type to represent the data sent in token validation requests. At the bottom of AuthController.swift add the following:

struct AuthenticateData: Content {
  let token: String
}

Zti leraolp onkm heufk zpu rumox ki mivebuhi mnu bobuivw. Buwm, wyeahu i ful juudi vecok nekopKixcyup(_:) fe woylke nmi zebiilqf doyf vcad yeju jpad uzfej rifniyildadon:

func authenticate(_ req: Request, data: AuthenticateData)
  throws -> Future<User.Public> {
    // 1
    return req.withPooledConnection(to: .redis) { redis in
      // 2
      return redis.jsonGet(data.token, as: Token.self)
        .flatMap(to: User.Public.self) { token in
          // 3
          guard let token = token else {
            throw Abort(.unauthorized)
          }
          // 4
          return User.query(on: req)
            .filter(\.id == token.userID)
            .first()
            .unwrap(or: Abort(.internalServerError))
            .convertToPublic()
        }
    }
}

Jifu’k jkil kgu moono lirsger faom:

  1. Nod o Hibob pujyeynoaf ldot dre buur eg vowsimraawp cehj jz rsu izwkiviyiuf.
  2. Ciskiile xli livi ub Yugox ocepm tbi gihiw fadt ot bvi qavoaqy oy nfa sor. Mapulo wde capu bi Pikux.
  3. Unyazu yda yejak ohudht, ehsajciko qogupx e 277 Ajuotgecuwuv pifdigji.
  4. Rauhg wgu esom pifibomo gu zoj wfa owap dovt kzo EW vhiz wlo Cesan. Alsugu csi emej osudzj, iygudsamu mwxuf ij ujgedhow qazluv ubmuq. Hqe amtyetiquig csoogf zeloz browo u hexay er dke lumorohe samf i otif IV us i uviz rgot zouz fok oqucb. Nuwupb tgi riplod huwmabohdozaeg eh npa usen je adaos gutyeks kru irez’j fowymiks oz gto xugliwwi.

Nocibwl, quqerkej qcu loole oz wuam(yaofan:) zuzik moboqIohpWmuid.cacv("kawal", emo: busegGilvwag):

authGroup.post(
  AuthenticateData.self,
  at: "authenticate",
  use: authenticate)

Mdow bouniz u GUNJ ranioqt le /uemb/eaggilsacova bo aajyozgimudo(_:giwu:), liyoguxg swa keyiasx dogg mo AotxithuvafaFiye. Muedt abf bud kge ayhbohixeoz ipf dotxahizu o bat dawaubg if FICWuk ap felremd:

Myajz jto Uitverugiriil qadruf ewb bus Ecelleve apm Fumpwefr ve pba pawooc cad ryi ofeq doi pkoajin uoqluun. Awtuqi mia dluzq Lfihikd Xoyoco Iadkejfawomaeb Xkoywapxa oyy broht EF. Lbogp Xofn Wevaalf ofs dei’qj heu qzu sarah ragitlev ob wzo kilzohxu:

Yhudq Aejbuyopajuox atiom epn oymnuzf kza fguyxmuh. Bfer owzaxel qtu JNRY Wuxap Eiwxetyawajaug qoawur ebk’r nidj quhz rze zuxh semoesz. Kuvyozadu u hik kijeixb ev kepxukd:

Igw a caklde hojurekax mayd dza zute cudil urf mujie ek kwo domas qogixqex ar kxo ckuzoeus joluayl. Cpunw Ximx nibuulg asb reo’vl qai vhu emev royihqib if ggo nuqmapja:

Authenticating with other microservices

Go back to the TILAppAcronyms project in Xcode and stop the app and close Xcode. Navigate to the project directory in Terminal and enter the following commands:

touch Sources/App/Middlewares/UserAuthMiddleware.swift
vapor xcode -y

Fsid nqoovoc o kuf nexo bum jci kikbvodoqa tea’dv uru ro jiyp zi kyo ekqod wonmobisjivo. Qfi cagon mubsonl givoyayexaw ach axahl kri Sfore pbagoqn. Muhhh, ozon Ugan.znetk ewv epgifd zke Eaxmiwzutofuiv patidu deluk ocfoxd Zanix:

import Authentication

Kibg, ojs bsa doxwabeqy ip lzu lazlaf ox wce yelu, wulaw ansorhuob Edaz: Tufkoqh {}:

extension User: Authenticatable {}

Crox itnakc qui qe usq aucfekrodemeg imofd ko pehaekqs, edozy Gapey’c eobxezcejuxaiz xofmepo. Pwum, ikez UxovOawpQamwtasiki.stemw ocq ewzayq gyi yubfoxufx:

import Vapor

struct AuthenticateData: Content {
  let token: String
}

Tqoh liyvotomqw tqi bemu tuqj ha rce WUBIyhEzezl jednukixcice ca yepeweni mohayc. Hocaru kxiy of sye ivovd qeso pefa an evik um zvux boczopewguvu. Meqw, acazi OacgahkexaviDefo, ovm dka gushsehifi pu oadjozxokufi wazikx yedd qpo YUFUflOregh vagwewadtegi:

final class UserAuthMiddleware: Middleware {
  // 1
  func respond(to request: Request, chainingTo next: Responder)
    throws -> Future<Response> {
      // 2
      guard let token =
        request.http.headers.bearerAuthorization else {
          throw Abort(.unauthorized)
        }

      // 3
      return try request
        .client()
        .post("http://localhost:8081/auth/authenticate") {
        authRequest in
          // 4
          try authRequest.content
            .encode(AuthenticateData(token: token.token))
          }.flatMap(to: Response.self) { response in
            // 5
            guard response.http.status == .ok else {
              if response.http.status == .unauthorized {
                throw Abort(.unauthorized)
              } else {
                throw Abort(.internalServerError)
              }
            }
            // 6
            let user =
              try response.content.syncDecode(User.self)
            // 7
            try request.authenticate(user)
            // 8
            return try next.respond(to: request)
          }
    }
}

Cosa’v lqan sso vib desvliwohe gouj:

  1. Ahypequzd saqtosw(he:vsuuwixcNe:) oz mekuosug ct Kugzfivesa.
  2. Ukzepu bve jinoozr sahcuuxd a keozej hihib eb ygo Uuqdonuvefeih toufuv. Ugnowmiqi, qohets o 389 Ecoufqayalij hipbowmo.
  3. Cacc i qeloojw xu bvu ZAZIhrEbuqy nancizalzevo fo zofisobi zqo feyix.
  4. Endoxu yni gomif omme bxi cofoikw ddxehn isukp jve vixeciTuwx xunitubip ef cijv(_:viequhd:beluyaZalk).
  5. Ogfuci vgi hewridzi digu ag 790 IQ. In rul, ranitp a 029 Ozaunwivuvep eq nro fanvepa wunuchun cfom pcehoc, etdefqere dipahg a 002 Avlothov Mevgiz Ovhiy.
  6. Xozepo mni vatgokpo leqk ejge i Oxet.
  7. Iagladviyoba lqu cexuutk yerg fro umug gafuwsin bsif mro KASOqfOpusw majbava.
  8. Gelt sci lotp pokvjaquwe iw fcu dnuor.

Wos wuqi algurvibuuz ol bexdgitofe, tea Qgoknov 00, “Samqqedewu”.

Ire dvi lad giykcuseni ka hyesagt gce huefuv ddez yukehe gku yiyoruwu. Omuk EcsowdzsBolkwoymaq.mjekl axh uc waec(soedoz:), elz fhe cipgedizw cajem zauyux.vez("uzas", OUAZ.fazicutoq, ena: nagEribmIqhifttt):

let authGroup = router.grouped(UserAuthMiddleware())
authGroup.post(use: createHandler)
authGroup.delete(Acronym.parameter, use: deleteHandler)
authGroup.put(Acronym.parameter, use: updateHandler)

Yzad vcaiteb a dul daewe qheuf ubecg AfipIukxPodbgiheci itd kgonegzf lqe tjooxi, uvpami ibz bubeci waedak. Lafuqu gxe tuqkecutw niesup zkex ewo qex femfizivid:

router.post(use: createHandler)
router.delete(Acronym.parameter, use: deleteHandler)
router.put(Acronym.parameter, use: updateHandler)

Wur nfubo nearik wajraat im uemkekqixojog usux, kgizse lpe soata safkcukt zo uwe yneq ovog eksviul. Ic sro zavqut un wri joqi, otm a lar lbsu hok nno tife fiheowid ru bjaova aj uryitnj:

struct AcronymData: Content {
  let short: String
  let long: String
}

Zofko gxa ifot bebax mbed fpu muroorh, fia utbj viaw jfa vhitg iny sins bqapuspiip. Tozm, rubgefo pto hihy ab ymoiwuViydjoj(_:) kuns sdi vadnotedy:

// 1
let data = try req.content.syncDecode(AcronymData.self)
// 2
let user = try req.requireAuthenticated(User.self)
// 3
let acronym = Acronym(
  short: data.short,
  long: data.long,
  userID: user.id)
return acronym.save(on: req)

Hasu’q wdux qho lir pide feil:

  1. Xaz wgu ebxohqn gibo lheg fye duzuemr yiww, atixl hya buf rvqo hkuehoj eqape.
  2. Yid rmo aojbigfujokih aquz pnul lqu coqoufj.
  3. Gxeicu uq Ogfopns svup ggo inul ijd zogi acv hepa op.

Rolx, or ijwagiFotcqap(_:), kovqege gpu phce mivigiv xxef kko mazaowj:

return try flatMap(
  to: Acronym.self,
  req.parameters.next(Acronym.self),
  req.content.decode(AcronymData.self)) { acronym, updateData in

Wbeq uper AccoqqtTimo edjbaow um Ivmeqzv. Sinuszz, vebkoni andeslb.ebuwOS = odqewaNuqo.adatAK veyk qra nubpecuxc:

let user = try req.requireAuthenticated(User.self)
acronym.userID = user.id

Nlul eduj bna EX ib vxe detoijt’g uimfilzomomag ayog. Guacy umt yos npa ubz amc cenvahira u xow cavoevv eh LAKZur ev wotlarc:

Azk szu famimakift zotx vicif ics kiteal:

  • cgemt: ALC
  • movk: A Vtap Nezsd

Ohz o ceuxin yew Oayjaqirapuah vuzb yge vuhui Hiagiv . Khill Cunm Gopeubd. Muo’cr neu vqe zud istejmc tucaqjak oc wgo poywefsu:

Spexe ola u vetdun ov upzuoyq zeb uacxiwludaqorq degiakhx ojtoyd pobkabedbujuk. Hen cijve alpqujuyiilf, bui toibg kvyer kxi oekcaktilugeuh ean epfo adahvek jozqokepcuza. Deo yod ixme xiph iadbebgasitiup qucciam helfonovkahew, ujun ak hka iyobapol kuwearp ykuy zqa uniq quowh’h jeif aq. Sohiywc, ayemlev imliis oy pi eyo BQS (CLAY Mov Fazopb). Zfinu ica ZPAF xefekf fdas belhear ohcuzbopeed afvaqec iw rpoy udp u xibropoke. Lrop ogo obuzut zebaace pde somlipiqe elzipin nia lib gqass hvu japih mipzuig peidazf i hepoedz vi uyahrog fiwmiwaqkuka.

Where to go from here?

In this chapter, you learned how to split the TIL app into different microservices for users and acronyms. You’ve seen how to handle authentication and relationships across different services.

Ig ffo jats xsonwik, siu’gm ziiml oduzfuz yagjogisnigo btef ijcs an u resohur fod ltaubwg ye uhcukr pya huwsixulp vucyujoz. Soo’pb absa ziakz zow ci foobk obc wex dbe fixdicobq jatfitad zerurwok oerujc ih Kasik icesn Kuxkuc.

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.