DEV Community

Flavien Normand
Flavien Normand

Posted on

6 1

Apollo on Angular, almost a love story

Apollo

Apollo is a (if not the) graphQL client for frontend applications.

It is available for React, Angular, Vue and many more, with great docs available in order to get started easily and understand how to use its power.

Apollo-angular

Apollo-angular is the Angular wrapper for the apollo client. It fits particularly well with Angular because all its API was designed to use Observable and reactive paradigm. If you use Angular daily you should know how much it uses Observables and how it can be annoying when an external API doesn't. Apollo-angular will help you to develop efficiently with Apollo and Angular, by providing everything you need the way you need it.

Getting started

When getting started on apollo-angular, you can simply follow the example shown in the docs, which will allow you to install the library, then configure its module and finally provide a factory for Apollo.

import { HttpClientModule } from "@angular/common/http";
import { ApolloModule, APOLLO_OPTIONS } from "apollo-angular";
import { HttpLinkModule, HttpLink } from "apollo-angular-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
ApolloModule,
HttpLinkModule
],
providers: [{
provide: APOLLO_OPTIONS,
useFactory: (httpLink: HttpLink) => {
return {
cache: new InMemoryCache(),
link: httpLink.create({
uri: "https://o5x5jzoo7z.sse.codesandbox.io/graphql"
})
}
},
deps: [HttpLink]
}],
})
export class AppModule {}
view raw app.module.ts hosted with ❤ by GitHub

But then, it's done?

Even if this example is perfect in order to get you started with the library, it is far from being good for production use, because of how you have to setup authorization header.

The main reason being that if your application needs to change something on the client's configuration, you won't be able to create a new Apollo instance, instead you will get an error saying apollo has been already created. This example is only good if your token has no expiration date, or if it's shorter than your application's life (if it's not meant to be opened for this long).

But why?

The reason for this is that Apollo client has to be single instance, it is made to be singleton and once you created one, especially in an Angular context, where everything is a service, it's hard to delete it.

The solution

Context

In my case, I'm using Firebase for the authentication part, in order to query my graphQL API, created using Hasura. To secure the application, it requires a classic JWT authentication to match a role in the IAM, unauthenticated access is forbidden, in order to avoid unwanted spam of requests from people wanting to just hammer the API.

The problem

When the application is running, as it's a SPA, it can live longer than the JWT, meaning that I will have to refresh it, but because of that, I would have to create a new Apollo instance using the method linked above, and we know it, it's not going to work.

The solution

When you think about it, graphQL requests are just POST requests to the graphQL endoint, which means they use the http service from Angular. And this means that we can use interceptors !

import { Injectable } from '@angular/core';
import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { filter, switchMap } from 'rxjs/operators';
@Injectable()
export class ApolloInterceptor implements HttpInterceptor {
constructor(private myAuthService: AuthService) {
}
intercept(req: HttpRequest<any>, next: HttpHandler) {
// Filter your endpoint in order to only edit the graphql-related requests
if (req.url.indexOf('/graphql') > -1) {
// Get your jwt from your usual source, can be a facade, a service, or even sync data like localStorage
return this.myAuthService.jwt$
.pipe(
// Make sure the request won't replay when your token gets updated
first(),
switchMap(idToken => {
// Simply add the Authorization header to the request
const clone = req.clone({
setHeaders: {
Authorization: `Bearer ${idToken.token}`
}
});
// And you're done !
return next.handle(clone);
})
);
}
// If it's not a graphql request, just give it to the next handler.
return next.handle(req);
}
}

This allows the token to be reloaded anytime, without having to worry about the Apollo instance, as it can safely be kept as a singleton, with a proper authorization header that can be reloaded !

You can now enjoy your graphQL API with the same application instance during several hours ! 🎉

If you enjoyed this article do not hesitate to share it on twitter. If you have any questions or feedback please don't hesitate to tweet me @Supamiu_

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (2)

Collapse
 
dylanesque profile image
Michael Caveney

Thank you for writing this up, this was a huge life-saver for me!

Collapse
 
nerdophile profile image
Venkat Poojari

Will this work for subscription?

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay