I've got @ngrx/store and effects working fine, however, I just realized there will be a a lot of API calls (in effects), and if any of those returns a 401 error I should redirect the user to the login page. My problem is: I don't want to check that in every single effect, that would be a ton extra code for the same thing. Let's say for example I have a code like this:
Sample effect
@Effect() getMe$ = this.actions$
.ofType(GET_ME)
.map(action => action.payload)
.switchMap(payload => this.userService.me()
.map(res => ({ type: GET_ME_SUCCESS, payload: res }))
.catch(() => Observable.of({ type: GET_ME_FAILURE }))
);
userService.me()
me(): Observable<User> {
return this.apiService.get(`/auth/me`);
}
apiService.get()
get(endpoint: string): Observable<any> {
return this.http.get(`${this.base}${endpoint}`, this.options())
.map(res => res.json());
}
This works perfectly fine, but I'm not sure how to handle the case when the API return 401. Where should I redirect the user globally in that case? Should I create an action for that case? Where should I dispatch that action then? Or am I doing it completely wrong?
Any help in the right direction would be appreciated!
You need to run this command npm install @ngrx/effects — save to install required dependencies. You have to define the actual services to make the API calls. Finally, you need to register all the effects with the EffectsModules and import this module into your main module or feature module.
When should you not use NgRx? Never use NgRx if your application is a small one with just a couple of domains or if you want to deliver something quickly. It comes with a lot of boilerplate code, so in some scenarios it will make your coding more difficult.
This is trivial within an application that is using the global NgRx Store, as the global Store is just a cache object.
'ofType' filters an Observable of Actions into an observable of the actions whose type strings are passed to it.
The errors that are emitted from Http
will contain a status
property (set to the HTTP status code) if they are errors that were received from the server.
If you include the error status in your HTTP-based service failure actions:
@Effect() getMe$ = this.actions$
.ofType(GET_ME)
.map(action => action.payload)
.switchMap(payload => this.userService.me()
.map(res => ({ type: GET_ME_SUCCESS, payload: res }))
.catch(error => Observable.of({
type: GET_ME_FAILURE,
payload: { errorStatus: error.status }
}))
);
You could then write a general-purpose effect that looks at all actions and redirects if they contain a 401 error:
@Effect() errorStatus401$ = this.actions$
.map(action => action.payload)
.filter(payload => payload && payload.errorStatus === 401)
.switchMap(payload => {
this.router.navigate(['/login']);
return Observable.empty();
});
Or, if you use the @ngrx/router-store
:
import { go } from '@ngrx/router-store';
...
@Effect() errorStatus401$ = this.actions$
.map(action => action.payload)
.filter(payload => payload && payload.errorStatus === 401)
.map(payload => go(['/login']));
If there are additional actions you wish to perform before navigating, you can emit multiple actions using concat
:
@Effect() errorStatus401$ = this.actions$
.map(action => action.payload)
.filter(payload => payload && payload.errorStatus === 401)
.switchMap(payload => Observable.concat({ type: 'CLEAR_TOKEN' }, go(['/login'])));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With