Error handling

Last updated 4 days ago

Error handling

Lets take a look again at sample authorization middleware.

auth.middleware.ts
export const authorize$: Effect<HttpRequest> = request$ =>
request$.pipe(
switchMap(req => iif(
() => !isAuthorized(req),
throwError(new HttpError('Unauthorized', HttpStatus.UNAUTHORIZED)),
of(req),
)),
);

Marble.js comes with dedicated HttpError class for defining a request related errors that can be catched easily via error middleware. Using RxJS built-in throwError method, we can throw a passed error object and catch it on an upper level (eg. inside API Effect or error middleware) via RxJS catchErrormethod.

HttpError class resides in @marblejs/core package and its constructor takes as a first parameter an error message and as a second parameter a HttpStatus code that can be a plain JavaScript number or TypeScript enum type that also can be imported via core package. Optionally you can pass also a data object for any other error related datas as a third argument.

new HttpError('Forbidden', HttpStatus.FORBIDDEN, { resource: 'index.html' });

404 error handling

404 (Not Found) responses are not the result of an error, so the error-handler middleware will not capture them. This behavior is because a 404 response simply indicates the absence of matched Effect in request lifecycle; in other words, request didn't find a proper route. All you need to do is to compose a dedicated Effect at the very end of the stack to handle a 404 response.

const notFound$ = EffectFactory
.matchPath('*')
.matchType('*')
.use(req$ => req$.pipe(
switchMap(() =>
throwError(new HttpError('Route not found', HttpStatus.NOT_FOUND))
)
));

The Effect above will handle all paths and all method types and at the end of pipeline will throw an 404 error code that can be intercepted via error middleware.

notFound$ Effects can be placed in any layer you want, eg. you can have multiple Effects that will handle missing routes in dedicated organization levels of your API structure.

Custom error handling effect

By default Marble.js comes with simple and lightweight error handling effect. Because middlewares and Effects are based on the same generic interface, your error handlers can work very similar to normal API Effects.

error.middleware.ts
const error$: Effect<EffectResponse, ThrownError> = (
request$,
response,
error
) => request$.pipe(
map(req => ({
status: // ...
body: // ...
}),
);

As any other Effect, error handler maps the stream of errored requests to objects of type EffectResponse (status, body, headers). The difference is that it takes as a third argument an intercepted error object which can be used for error handling-related logic.

To connect the custom error handler, all you need to do is to attach it to errorEffect property in httpListener config object.

app.ts
const app = httpListener({
middlewares,
effects,
// Custom error effect:
errorEffect: error$,
});