@loopback/docs
Version:
Documentation files rendered at [https://loopback.io](https://loopback.io)
189 lines (165 loc) • 5.46 kB
Markdown
---
lang: en
title: 'Migrate from Legacy Sequence'
keywords:
LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI, Migrate Legacy
Sequence
sidebar: lb4_sidebar
permalink: /doc/en/lb4/Migrate-from-legacy-sequence.html
---
The `Sequence` generated before `/rest@6.0.0` comes with hard-coded
actions as follows:
```ts
import {inject} from '@loopback/core';
import {
FindRoute,
InvokeMethod,
InvokeMiddleware,
ParseParams,
Reject,
RequestContext,
RestBindings,
Send,
SequenceHandler,
} from '/rest';
const SequenceActions = RestBindings.SequenceActions;
export class MySequence implements SequenceHandler {
/**
* Optional invoker for registered middleware in a chain.
* To be injected via SequenceActions.INVOKE_MIDDLEWARE.
*/
(SequenceActions.INVOKE_MIDDLEWARE, {optional: true})
protected invokeMiddleware: InvokeMiddleware = () => false;
constructor(
(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,
(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,
(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,
(SequenceActions.SEND) public send: Send,
(SequenceActions.REJECT) public reject: Reject,
) {}
async handle(context: RequestContext) {
try {
const {request, response} = context;
const finished = await this.invokeMiddleware(context);
if (finished) return;
const route = this.findRoute(request);
const args = await this.parseParams(request, route);
const result = await this.invoke(route, args);
this.send(response, result);
} catch (err) {
this.reject(context, err);
}
}
}
```
The legacy `Sequence` is now deprecated but it should continue to work without
any changes.
If you have never customized `src/sequence.ts` generated by `lb4 app`, or you
have only modified the code by adding
[`authenticate`](Authentication-component-action.md) action per our docs, you
can simply migrate to the middleware based sequence by replacing the content of
`src/sequence.ts` with the code below.
```ts
import {MiddlewareSequence} from '@loopback/rest';
export class MySequence extends MiddlewareSequence {}
```
If you have other actions in your sequence, e.g., complex error handling per
end-point, you'll have to write a Middleware to wrap your action and register it
with the middleware chain for the middleware-based sequence.
This can be achieved by creating a Middleware in
`src/middlewares/error-handler.middleware.ts` with the code below:
```ts
import {inject, injectable, Next, Provider} from '@loopback/core';
import {
asMiddleware,
HttpErrors,
LogError,
Middleware,
Response,
MiddlewareContext,
RestBindings,
RestMiddlewareGroups,
} from '/rest';
(
asMiddleware({
group: 'validationError',
upstreamGroups: RestMiddlewareGroups.SEND_RESPONSE,
downstreamGroups: RestMiddlewareGroups.CORS,
}),
)
export class ErrorHandlerMiddlewareProvider implements Provider<Middleware> {
constructor(
(RestBindings.SequenceActions.LOG_ERROR)
protected logError: LogError,
) {}
async value() {
const middleware: Middleware = async (
ctx: MiddlewareContext,
next: Next,
) => {
try {
return await next();
} catch (err) {
// Any error handling goes here
return this.handleError(ctx, err);
}
};
return middleware;
}
handleError(context: MiddlewareContext, err: HttpErrors.HttpError): Response {
// We simply log the error although more complex scenarios can be performed
// such as customizing errors for a specific endpoint
this.logError(err, err.statusCode, context.request);
throw err;
}
}
```
then bond to the application `src/application.ts` by:
```ts
import {ErrorHandlerMiddlewareProvider} from './middlewares';
//...
constructor(options: ApplicationConfig = {}) {
...
this.add(createBindingFromClass(ErrorHandlerMiddlewareProvider));
}
// ...
```
As part of the legacy action-based sequence, the `invoke` function takes `route`
and `args` as input parameters and returns `result`.
```ts
const route = this.findRoute(request);
const args = await this.parseParams(request, route);
const result = await this.invoke(route, args);
this.send(response, result);
```
The corresponding middleware for `invokeMethod` looks like the following. It now
uses the `context` to retrieve `route` and `params` instead. The return value is
also bound to `RestBindings.Operation.RETURN_VALUE` to expose to other
middleware in the chain, as illustrated below.
```ts
...
export class ErrorHandlerMiddlewareProvider implements Provider<Middleware> {
...
async value() {
const middleware: Middleware = async (
ctx: MiddlewareContext,
next: Next,
) => {
try {
// Locate or inject input parameters from the request context
const route: RouteEntry = await ctx.get(RestBindings.Operation.ROUTE);
const params: OperationArgs = await ctx.get(RestBindings.Operation.PARAMS);
const retVal = await this.invokeMethod(route, params);
// Bind the return value to the request context
ctx.bind(RestBindings.Operation.RETURN_VALUE).to(retVal);
return await next();
} catch (err) {
// Any error handling goes here
....
}
};
return middleware;
}
....
}
```