DEV Community

Krzysztof Platis
Krzysztof Platis

Posted on • Updated on

Don’t use global static objects - avoid race condition in SSR Angular 🏎

Instead of using a global static object, you should create an injectable service. Otherwise in SSR server two apps bootstrapped for two concurrent http requests can change and read that object at the same time. It’s prone to race condition.

Sometimes it’s not so obvious we’re using a static global object, especially when we import it from a 3rd party library.

3rd party library i18next

In an Angular project I imported the translations engine from the 3rd party vanilla JS library: import i18next from ‘i18next’. Then in an APP_INITIALIZER I set the active language i18next.changeLanguage(urlLang) based on the URL param, for example or Then this language was used behind the scenes to translate labels in the components, i.e. i18next.t(‘translation.key’).

Race condition

When the SSR server received 2 http requests in a row and handled them concurrently in one NodeJS process - and - the global current language was set first to English, and then immediately overwritten to Japanese. The components of both apps were rendered a little later, so both were using Japanese for translating labels. Therefore a user requesting an URL could get a response in Japanese, which was a bug.


To fix it I needed to create a fresh i18next instance i18next.createInstance() for each bootstrapped app. So I wrapped it in an injection token provided in the app’s root injector:

import i18next from i18next;

export const I18NEXT_INSTANCE = new InjectionToken('I18NEXT_INSTANCE', {
  providedIn: 'root',
  factory: () => i18next.createInstance(),
Enter fullscreen mode Exit fullscreen mode

Then this instance could be accessed only by the injectable classes (components, services, …) inside the same bootstrapped app:

  /* ... */
  @Inject(I18NEXT_INSTANCE) protected i18next
) {}
Enter fullscreen mode Exit fullscreen mode

Discussion (0)