Skip to content

@service(GeocoderProvider) Fails with "No Binding Found" in LoopBack Component #11062

@Tyagi-Sunny

Description

@Tyagi-Sunny

Describe the bug

Problem Description
@service(GeocoderProvider) Fails with "No Binding Found" in LoopBack Component after overriding the same class via Service booter in application.

Problem Statement
You created a custom LoopBack Component named DummyComponent, which declares and registers models, repositories, controllers, and a service (GeocoderProvider):

export class DummyComponent implements Component {
  ...
  services?: ServiceOrProviderClass[];
  ...
  constructor(
    @inject(CoreBindings.APPLICATION_INSTANCE)
    private application: Application,
  ) {
    this.services = [GeocoderProvider]; // <-- Intended to register the service
  }
}

and used in a controller as

export class TodoController {
  constructor(
    @repository(TodoRepository)
    public todoRepository: TodoRepository,
    
    @service(GeocoderProvider) // <-- This fails
    protected geoService: Geocoder,
  ) {}
}

i added DummyComponent in application.ts as

this.component(DummyComponent);

this works fine. but when i override the GeocoderProvider via Service Booter.
this gives me error

500 Error: No binding found for GeocoderProvider. 
Make sure a service binding is created in context RequestContext-xxxx with serviceInterface (GeocoderProvider).

Steps to reproduce:

Reason for this error->
@service decorator filters the binding based on a functions

export function filterByServiceInterface(
  serviceInterface: ServiceInterface,
): BindingFilter {
  return binding =>
    binding.valueConstructor === serviceInterface ||
    binding.tagMap[CoreTags.SERVICE_INTERFACE] === serviceInterface;
}

and if we explicitly does not provide interface then it uses class itself as interface and used it for comparison. and we end up with two different services that causes comparison to fail and returning no binding found.
As we don't have any options to provide this interface(serviceOptions) via service booter. either we need to Manually bind services after the boot process (defeating the purpose of convention-based discovery)

Solution:
I have created a decorator @ServiceOptions decorator that allows you to annotate service classes with configuration options metadata. It uses the factory pattern to create decorators with a specific options metadata key.
Then createServiceBinding function is the core registration mechanism that:

  • Extracts metadata: Retrieves decorator-defined options from the class
  • Merges configurations: Combines decorator metadata with manually passed options (manual options take precedence)
  • and rest will be same

Logs

Additional information

No response

Reproduction

https://github.com/Tyagi-Sunny/loopback-next/tree/bug-no-binding-found

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions