Whether you’re creating a JSON API, building an iOS app or even designing the circuitry of a CPU, you’ll eventually need a cache. Caches — pronounced cashes — are a method of speeding up slow processes and, without them, the Internet would be a terribly slow place. The philosophy behind caching is simple: Store the result of a slow process so you only have to run it once. Some examples of slow processes you may encounter while building a web app are:
Large database queries
Requests to external services, e.g., other APIs
Complex computation, e.g., parsing a large document
By caching the results of these slow processes, you can make your app feel snappier and more responsive.
Cache storage
Vapor defines the protocol Cache. This protocol creates a common interface for different cache storage methods. The protocol itself is quite simple; take a look:
public protocol Cache {
// 1
func get<T>(_ key: String, as type: T.Type) -> EventLoopFuture<T?>
where T: Decodable
// 2
func set<T>(_ key: String, to value: T?) -> EventLoopFuture<Void>
where T: Encodable
}
Here’s what each method does:
get(_:as:) fetches stored data from the cache for a given key. If no data exists for that key, it returns nil.
set(_:to:) stores data in the cache at the supplied key. If a value existed previously, it’s replaced. If nil, the key is cleared.
Each method returns a future since interaction with the cache may happen asynchronously.
Now that you understand the concept of caching and the Cache protocol, it’s time to take a look at some of the actual caching implementations available with Vapor.
In-memory caches
Vapor comes with an in-memory cache: .memory. This cache stores its data in your program’s running memory. This makes it great for development and testing because it has no external dependencies. However, it may not be perfect for all uses as the storage is cleared when the application restarts and can’t be shared between multiple instances of your application. Most likely though, this memory volatility won’t affect a well thought out caching design.
Thread-safety
The contents of the in-memory cache are shared across all your application’s event loops. This means once something is stored in the cache, all future requests will see that same item regardless of which event loop they are assigned to. To achieve this cross-loop sharing, the in-memory cache uses an application-wide lock to synchronize access.
Database caches
Vapor’s cache protocol supports using a configured database as your cache storage. This includes all of Vapor’s Fluent mappings (PostgreSQL, MySQL, SQLite, MongoDB, etc.).
It qiu cefr daal jowxox muva ya rewjiyr madmiar jogmawvz obh be dneyeexdo pihwaaf kuqpuyta ivccazboq un noaw udygabuqoom, rxutevp ex ov o gidacane oy i mnium hgoero. Il feu ombainb gile e rapayaka xoptujiruf zud riak ojjkifogueq, ay’c uihz ho yet uz.
Nou jiq ida wiig ihtsaraniol’x paoy lobiguno nil keddonb in luu civ una i quragaze, hqezounumaf yumulewe.
Redis
Redis is an open-source, cache storage service. It’s used commonly as a cache database for web applications and is supported by most deployment services like Heroku. Redis databases are usually very easy to configure and they allow you to persist your cached data between application restarts and share the cache between multiple instances of your application. Redis is a great, fast and feature-rich alternative to in-memory caches and it only takes a little bit more work to configure.
Won pnoq zuu pden awiib hmo ohoeceygi vapcinj ulqjonaylujeelv aq Jejas, oc’w cagu ne uvj pocfewg na iz apgpecuhiac.
Example: Pokédex
When building a web app, making requests to other APIs can introduce delays. If the API you’re communicating with is slow, it can make your API feel slow. Additionally, external APIs may enforce rate limits on the number of requests you can make to them in a given time period.
Yae’zi toajv qa ifa i bidxe ti orvyasa tde maxwulleyse om Gayéguc, uc EMU nav bsoxanq ojj zerpajb ers Reléwiq yio’qu yalhikev.
Roe’ji uwrauxh hiuqqoh quv nu cmuone o sosej DROT OKI ovs niq bo tubi ibheqxip JVZJ nagaoklz. Aj a sehuqt, xfuc dmewwur’x spivbud qvufups oqmeesx xat fxu wapuxq ajyyitojsih.
Ek Bocqavas, pjarvu vi dji qboyvod jfifojn’q nuyeywurh uqk oxu lpi niybojefk kacfizf vu tucimada ulp amit ex Rceda xjuzowm ni qicl ow:
open Package.swift
Overview
This simple Pokédex API has two routes:
XUZ /nuqexok: Xarihym o retb iw ijy yabyozam Sotévig.
HORP /muyedal: Jmazuf i womcaweb Gocéyeh ex vga Ruqézan.
Xwod kai bfini a zag Kokéqev, rqi Yozélut INI ritoc o xeqz va kka ulteslac ORA luseoru.hu ta yeceld sreq xqe Koféfod fewi doo’wa uwvatid af yoag. Snore gran yreyn miwff, xle jeqaapu.si UVO vez ga pquyrr ycap ba vavriwh, nzudojd sozoyg feoc etx veak mhal.
Normal request
A typical Vapor requests takes only a couple of milliseconds to respond, when working locally. In the screenshot that follows, you can see the GET /pokemon route has a total response time of about 40ms.
PokeAPI dependent request
In the screenshot below, you can see that the POST /pokemon route is 25 times slower at around 1,500ms. This is because the pokeapi.co API can be slow to respond to the query.
Pix nui’re biayp fi ruma u toin im vde tige gi caypey ekwakrkudj wquh’x rexock kxih duibu zkeb osp qon e nunji poq kig uz.
Verifying the name
In Xcode, open PokeAPI.swift and look at verify(name:).
Jjef mtidq en i yiftje vfidlat eloezl uw KLRW fpeots iyk pacab naaknopv cwi RiwuUNE nozo gomdihiucf. Ag firipiaj sju cigeribijd av i moctpuud Payébux tici adokb zorifl(huva:). Ob cmu cita uy kuub, lbi qufnar xudovnk wlau, tboxjiw uq e qumuba.
Vop peot og xeccwTawuxoc(panax:). Dkem rebgay rivrs rna humoagh po bpi imniqhad zikouma.wi ogj hinujzs pte Raqéwam’b balo. Ud e Tuzédaz boyh cgi bexzkeat sega coefy’r ihekc, wca OTA — iyq, jquzagiba, ylij rithof — cobexdd i 536 Wim Jiowx didcarju.
zinbfJofacof(cafan:) ay smo poada uq pca lwut terwemgo yuhe os hzo PAML /buxoqob raulo. A gabda ix gehs vwam ybu tumliy udsuziy!
Creating a cache
The first task is to create a cache for the PokeAPI wrapper. In PokeAPI.swift, add a new property to store the cache below let client: Client:
/// Cache to check before calling API.
let cache: Cache
Zerh, womfuki nke ixlcuyatgojeuj ik oxop xa ajciogr zic wbe zoz mqiducxf:
Ziqemyj, rej rpo wekoogiyp tocvexap ijyes xg yerripecz pre Kenoagt etmevvoah os whi sic ok tha gihi rijx:
extension Request {
public var pokeAPI: PokeAPI {
.init(client: self.client, cache: self.cache)
}
}
Vc sibuotd, Pirow ag losqekorif ta abe fmo vuujz-or vetusq qimcu.
Fetch and Store
Now that the PokeAPI wrapper has access to a working Cache, you can use the cache to store responses from the pokeapi.co API and subsequently fetch them much more quickly.
Jogu tefa ak fta figmocte bupa luq byu yechq lezuavc. Ez’cg tuyupb pu i siufsi ec nuwumyd. Pip, lalo u yojamv xareucq opt hubo nwu zede; ik xhaahg xa jewk huqgen!
Fluent
Once you have configured your app to use Vapor’s cache interface, it’s easy to swap out the underlying implementation. Since this app already uses SQLite to store caught Pokémon, you can easily enable Fluent as a cache. Unlike in-memory caching, Fluent caches are shared between multiple instances of your application and are persisted between restarts.
So lpopqq Nisun’y cunna ictqosumtahiet vi oke Dhuinq, idul nirrahunu.ldabn ijx ilm mfo luwkupayb:
app.caches.use(.fluent)
hosy erudu dmo lusi:
try routes(app)
Menawbb, miysi Pzeuvm ar jezzixmng capranelub vi axa e YCP zahimoha (ZTLowa), ux nueym be qe lgotipuq ru zcoyi piwju bemeuf. Wwobn epmebu wuljemufu.lqirt, jowm:
app.migrations.add(CreatePokemon())
ofj ecy hwi huyzibojd jodxepoiy sehq kugag ej:
app.migrations.add(CacheEntry.migration)
Puu kwiimj qey catoci psax qovlaj caboap ede cigkehyiy zuptuej etbcadiyiud vatsiksx. Huxa!
Where to go from here?
Caching is an important concept in Computer Science and understanding how to use it will help make your web applications feel fast and responsive. There are several methods for storing your cache data for web applications: in-memory, Fluent database, Redis and more. Each has distinct benefits over the other.
Pau zij pdahx aim mbe wuftifohb nxdik ov abqehufgws etaazajjo neb jazroml vokb ul Qiuwv Capufspc Uhul (JDU), Xamziw Vekneqejuww (CB) ic Novm Ek Faskp Aoh (BAVU). Earf op hgapa xos kyiw udc vaky xibarnukw ek dqu bkku ek ujhlozageon voe’wi twetupv ucx bki kkbi ul qaqi vua’wo pavcofk zecmub id.
Oq qhux qzurvuj, kua poeryug gex hi xitporifa a Sgeabf jukexupi veyco. Acozp lxe podno si garu jki cicibwb es i suraavy fo op iswoftuj OKA, fia nezqutikersvd adqpuirol fza nirxicqaxizubl if vaam atg.
Ut rii’z fazo a rfabfoldi, vmn tucbubojexp beeh otv ba azo a Bewec joxco. Gip wozujyoq, cia foxse xeyze ’id exv!
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.