Caching HTTP Requests in Angular using Service Workers

Angular&NodeEnthusiast
The Startup
Published in
5 min readJan 15, 2021
service workers

This story is written assuming that you have basic knowledge on what service workers are and what they do.

Angular provides the ngsw-config.json file to control the caching behavior of the application. When you create a new project and add service workers to it, the file looks like this.

{
“$schema”: “./node_modules/@angular/service-worker/config/schema.json”,
“index”: “/index.html”,
“assetGroups”: [{
“name”: “app”,
“installMode”: “prefetch”,
“resources”: {
“files”: [
“/favicon.ico”,
“/index.html”,
“/*.css”,
“/*.js”
]
}
}, {
“name”: “assets”,
“installMode”: “lazy”,
“updateMode”: “prefetch”,
“resources”: {
“files”: [
“/assets/**”,
“/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)”
]}}
]
}

This json currently has no configuration for caching http requests. It needs to be noted that only non-mutating http requests like GET and HEAD can be cached.

Lets say I want to make a GET request to the Node server that returns back a dummy message. I want to achieve 2 kinds of caching results here:

  1. The first GET request will get the response from the server but the subsequent requests must not be pointed to the server. Instead the response must be delivered from the cache. This is the performance caching strategy.
  2. All GET requests will be pointed to the server. The exception is in case of a timeout. In this scenario, the response will be delivered from cache and not the server. This is the freshness caching strategy.

Performance caching is suitable when there is not much change in the resources. Freshness caching is suitable when the resources change frequently and the responses cannot be delivered from a cache unless there is a timeout.

Lets see how we can achieve both these results. I have a very simple component and a service in the project to demonstrate this.

<button (click)=”testPerform()”>Test Perform GET request</button><button (click)=”testFreshness()”>Test Freshness GET request</button>

The component template has 2 buttons to test the 2 strategies. Below are the definitions for the methods when the buttons are clicked.

testPerform(){
this.service.testPerform().subscribe(x=>console.log(x));
}
testFreshness(){
this.service.testFreshness().subscribe(x=>console.log(x));
}

environment.prod.ts

export const environment = {
production: true,
baseUrl:’/app/’
};

Service:

testPerform(){
return this.http.get(environment.baseUrl+’test’).pipe(map(res=>res));
}
testFreshness(){
return this.http.get(environment.baseUrl+’test2').pipe(map(res=>res));
}

testPerform() is making a GET request to /app/test. Performance caching will be applied to this request.

testFreshness() is making a GET request to /app/test2. Freshness caching will be applied to this request.

Now lets edit the ngsw-config.json to add the caching strategy to these 2 requests.The bold text are the new changes added to the existing json.

{
“$schema”: “./node_modules/@angular/service-worker/config/schema.json”,
“index”: “/index.html”,
“assetGroups”: [{
“name”: “app”,
“installMode”: “prefetch”,
“resources”: {
“files”: [
“/favicon.ico”,
“/index.html”,
“/*.css”,
“/*.js”
]}},
{
“name”: “assets”,
“installMode”: “lazy”,
“updateMode”: “prefetch”,
“resources”: {
“files”: [
“/assets/**”,
“/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)”
]}}
],
“dataGroups”: [
{
“name”:”api-perform”,
“urls”:[“/app/test”],
“cacheConfig”: {
“strategy”: “performance”,
“maxSize”: 100,
“maxAge”: “3d”,
“timeout”: “10s”
}},
{
“name”:”api-fresh”,
“urls”:[“/app/test2”],
“cacheConfig”: {
“strategy”: “freshness”,
“maxSize”: 100,
“maxAge”: “3d”,
“timeout”: “10s”
}}
]

}

dataGroups is a field that contains an array of data groups, each of which defines a set of data resources and the policy by which they are cached.

So I have defined 2 groups for the 2 GET requests. Lets check the properties.

name: Any name that uniquely identifies the group

urls: A list of URL patterns. URLs that match these patterns are cached according to this data group’s policy. Only non-mutating requests (GET and HEAD) are cached.

maxSize:The maximum number of entries, or responses, in the cache. eg:100

maxAge: How long responses are allowed to remain in the cache before being considered invalid and evicted. eg: 3d stands for 3 days

timeout:How long the Angular service worker will wait for the network to respond before using a cached response, if configured to do so. eg: 10s stands for 10 secs.

strategy:performance or freshness.

My Node server is running on localhost:8081. I have installed the http-server package to the run the angular app.

Below is the scripts section of the package.json.

“scripts”: {
“ng”: “ng”,
“start”: “ng serve”,
“build”: “ng build — prod”,
“test”: “ng test”,
“server”:”http-server -p 8080 -c-1 dist/serviceWorkers — proxy http://localhost:8081/",
“lint”: “ng lint”,
“e2e”: “ng e2e”
}

Build the project using npm run build. Next, start the http-server using npm run server to run the app.

Component

On clicking Test Perform GET request, we are sending first GET request to server.

app/test response from server

Below is the request received at Node server.

Node server app/test

Now lets click the same button for the 2nd time.

app/test response from cache

There is no request received at the Node server and the response is delivered from the cache.

On clicking the Test Freshness GET request,

app/test2 response from server

A request made to Node server.

Node app/test2

When i click on the button for the 2nd or any more number of times, the request will be made to the server and responses delivered from the server.

This pattern will stop only in case of a timeout. So let me stop the Node server now and click the button again.

This time response will deliver from the cache.

app/test2 response from cache

--

--

Angular&NodeEnthusiast
The Startup

Loves Angular and Node. I wish to target issues that front end developers struggle with the most.