Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loopback 4 Middleware not registered

I'm following this example here for a simple logging middleware setup. But I can't seem to get it to log my request information. I get the standard response when I hit /ping, but nothing is logged out to the console.

I try to register the middleware in two different locations. index.ts and application.ts.

// src/middleware/log.middleware.ts

import { Next } from '@loopback/core'
import { Middleware, MiddlewareContext } from '@loopback/rest'

export const testLoggingMiddleware: Middleware = async (
  middlewareCtx: MiddlewareContext,
  next: Next,
) => {
  const { request } = middlewareCtx
  console.log('Request: %s %s', request.method, request.originalUrl)
  try {
    // Proceed with next middleware
    await next()
    // Process response
    console.log('Response received for %s %s', request.method, request.originalUrl)
  } catch (err) {
    // Catch errors from downstream middleware
    console.error('Error received for %s %s', request.method, request.originalUrl)
    throw err
  }
}
// src/index.ts

import { ApplicationConfig, RemoteConfigurationsApplication } from './application'
import { testLoggingMiddleware } from './middleware/log.middleware'
import { NodeEnvironmentService } from './services'

export * from './application'

export async function main(
  options: ApplicationConfig = {},
): Promise<RemoteConfigurationsApplication> {
  const app = new RemoteConfigurationsApplication(options)

  app.middleware(testLoggingMiddleware) // trying to add the middleware here as well

  await app.boot()
  await app.start()

  const url = app.restServer.url
  console.log(`Server is running at ${url}`)
  console.log(`Try ${url}/ping`)

  return app
}

if (require.main === module) {
  // Run the application
  const config = {
    rest: {
      port: +(process.env.PORT ?? 8081),
      host: process.env.HOST,
      // The `gracePeriodForClose` provides a graceful close for http/https
      // servers with keep-alive clients. The default value is `Infinity`
      // (don't force-close). If you want to immediately destroy all sockets
      // upon stop, set its value to `0`.
      // See https://www.npmjs.com/package/stoppable
      gracePeriodForClose: 5000, // 5 seconds
      openApiSpec: {
        // useful when used with OpenAPI-to-GraphQL to locate your application
        setServersFromRequest: true,
      },
      basePath: `/`,
      cors: {
        origin: '*',
        methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
        preflightContinue: false,
        optionsSuccessStatus: 204,
        credentials: true,
      },
    },
  }
  main(config).catch((err) => {
    console.error('Cannot start the application.', err)
    process.exit(1)
  })
}
// src/application.ts

require('dotenv').config()

import { BootMixin } from '@loopback/boot'
import { ApplicationConfig } from '@loopback/core'
import { HealthComponent } from '@loopback/extension-health'
import { RestExplorerBindings, RestExplorerComponent } from '@loopback/rest-explorer'
import { RepositoryMixin } from '@loopback/repository'
import { RestApplication } from '@loopback/rest'
import { ServiceMixin } from '@loopback/service-proxy'
import * as path from 'path'
import { testLoggingMiddleware } from './middleware/log.middleware'
import { MySequence } from './sequence'

export { ApplicationConfig }

export class RemoteConfigurationsApplication extends BootMixin(
  ServiceMixin(RepositoryMixin(RestApplication)),
) {
  constructor(options: ApplicationConfig = {}) {
    super(options)

    // Set up the custom sequence
    this.sequence(MySequence)

    // Set up default home page
    this.static('/', path.join(__dirname, '../public'))

    // Customize @loopback/http-explorer configuration here
    this.configure(RestExplorerBindings.COMPONENT).to({
      path: '/explorer',
    })
    this.component(RestExplorerComponent)

    this.component(HealthComponent)

    this.middleware(testLoggingMiddleware) // adding middleware in application

    this.projectRoot = __dirname
    // Customize @loopback/boot Booter Conventions here
    this.bootOptions = {
      controllers: {
        // Customize ControllerBooter Conventions here
        dirs: ['controllers'],
        extensions: ['.controller.js'],
        nested: true,
      },
    }
  }
}
like image 572
Clement Avatar asked Oct 27 '25 18:10

Clement


2 Answers

If anyone is still experiencing this problem, this is what worked for me:

  1. Update your sequence.ts so it looks like this:
    import { MiddlewareSequence } from '@loopback/rest';
    
    export class MySequence extends MiddlewareSequence {}
  1. Create your middleware on /src/middleware/example.middleware.ts

import { Next } from '@loopback/core'
import { Middleware, MiddlewareContext } from '@loopback/rest'

export const exampleMiddleware: Middleware = async (
  middlewareCtx: MiddlewareContext,
  next: Next,
) => {
  const {request} = middlewareCtx
  console.log('Request: %s %s', request.headers, request.method, request.originalUrl)
  try {
    // Proceed with next middleware
    const result = await next()
    // Process response
    console.log('Response received for %s %s', request.method, request.originalUrl)
    return result
  } catch (err) {
    // Catch errors from downstream middleware
    console.error('Error received for %s %s', request.method, request.originalUrl)
    throw err
  }
}
  1. Register your middleware in application.ts
    import {ApplicationConfig} from '@loopback/core';
    import {RestApplication} from '@loopback/rest';
    import {exampleMiddleware} from './middleware/example.middleware';

    export class MyApplication extends RestApplication {
      constructor(config: ApplicationConfig) {
        // register your middleware
        this.middleware(exampleMiddleware);
      }
    }

  1. Run the app and check your console.

Reference doc

like image 199
Fernando Rojas Avatar answered Oct 30 '25 08:10

Fernando Rojas


As mentioned by @Rifa, you need to update MySequence class

Earlier It had the following content-

import { inject } from '@loopback/context';
import {
  FindRoute,
  InvokeMethod,
  ParseParams,
  Reject,
  RequestContext,
  RestBindings,
  Send,
  SequenceHandler,
  InvokeMiddleware
} from '@loopback/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.
   */
  @inject(SequenceActions.INVOKE_MIDDLEWARE, { optional: true })
  protected invokeMiddleware: InvokeMiddleware = () => false;

  constructor(
    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,
    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,
    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,
    @inject(SequenceActions.SEND) public send: Send,
    @inject(SequenceActions.REJECT) public reject: Reject
  ) {}

  async handle(context: RequestContext) {
    try {
      const { request, response } = context;
      // Invoke registered Express middleware
      const finished = await this.invokeMiddleware(context);
      if (finished) {
        // The response been produced by the middleware chain
        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);
    }
  }
}

Replace it with-

import { MiddlewareSequence } from '@loopback/rest';

export class MySequence extends MiddlewareSequence {}
like image 27
Varun Kumar Avatar answered Oct 30 '25 07:10

Varun Kumar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!