Skip to content

Instantly share code, notes, and snippets.

@esotrope
Last active August 10, 2017 01:15
Show Gist options
  • Save esotrope/4dd50cfa0e290d545b3308bde65dc58d to your computer and use it in GitHub Desktop.
Save esotrope/4dd50cfa0e290d545b3308bde65dc58d to your computer and use it in GitHub Desktop.
Angular / API error handling
/*
This change is centered around noticing that we had our services mapping HTTP exception states to null which forced us to
(1) have null checking in each service object mapper and
(2) have null checks in our service subscription success handlers when they really belong in the error handler.
In this new approach we don't have to check for error cases in service subscription success handlers and mappers.
We let all exceptions propagate to
(1) Error resolve which can be displayed as a not found or generic error
(2) a service exception which can be displayed as a not found or generic error
API endpoint contract:
/singleResource
found -> {}
not found -> 404
/multipleResources
found -> [{},{},...]
found but empty -> []
not found -> []
API HTTP code masking:
403 -> 404
All other HTTP codes will come back as-is and either are:
401 unauthenticated
4xx indicating developer error
5xx indicating system failure
Below are some code snippets to demostrate how to code an Angular client with this error handling approach:
*/
/*
CASE A: Not handling 404s
*/
// in service
http.get('/data')
.map(data => /* do some mapping */ )
// component consuming service
dataService.getData()
.subscribe(
data => { this.data = data; },
error => { /* handle error with generic failure message */ }
);
// in resolve (implements Resolve<Data | Error>)
dataService.getData()
.catch(error => [error]);
// component consuming resolve
// if data is null go to error state in view
public data: Data | Error
this.data = this.route.snapshot.data[ 'data' ];
<div *ngIf="data instanceof Error">
<div class="alert alert-danger">
<p>{{'error' | translate}}</p>
</div>
</div>
/*
CASE B: Handling 404s
*/
// in service
http.get('/data')
.catch(error => {
if (error.status == 404) {
throw new NotFoundError();
}
throw error;
})
.map(report => /* do some mapping */ )
// component consuming service
dataService.getData()
.subscribe(
data => { this.data = data; },
error => {
if (error instancof NotFoundError) {
// handle 404
} else {
// handle all other errors as generic system failed error
}
}
);
// in resolve (implements Resolve<Data | Error>)
dataService.getData()
.catch(error => [error]);
// component consuming resolve
// if data is null go to error state in view
public data: Data | Error
this.data = this.route.snapshot.data[ 'data' ];
<div *ngIf="data instanceof Error">
<div class="alert alert-danger">
<p *ngIf="data instanceof NotFoundError">{{'not-found' | translate}}</p>
<p *ngIf="!(data instanceof NotFoundError)">{{'error' | translate}}</p>
</div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment