Your API implementation often needs to interact with REST APIs, SOAP WebServices, gRPC microservices, or other forms of APIs.

To facilitate calling other APIs or web services, we introduce@loopback/service-proxy module to provide a common set of interfaces forinteracting with backend services. You can create a Service class to provide acommon set of interfaces for interacting with backend services.

There are 3 major steps:

  • Add a datasource: specify the service you’re trying to connect
  • Add a service: define how the operations/methods in the external APIswill be mapped to the service methods.
  • Add a controller: expose the REST APIs that will call the service methods

Add a datasource

Add a datasource using the Datasource generator andselect the corresponding connector.

Datasource for SOAP web service

For calling SOAP web services, you also need to know the URL of the SOAP webservice endpoint and its corresponding WSDL file.

  1. $ lb4 datasource
  2. ? Datasource name: ds
  3. ? Select the connector for ds: SOAP webservices (supported by StrongLoop)
  4. ? URL to the SOAP web service endpoint: http://calculator-webservice.mybluemix.net/calculator
  5. ? HTTP URL or local file system path to the WSDL file: http://calculator-webservice.mybluemix.net/calculator?wsdl
  6. ? Expose operations as REST APIs: Yes
  7. ? Maps WSDL binding operations to Node.js methods:

For the last option Maps WSDL binding operations to Node.js methods, specifythe JSON in the format of:

  1. servicMethodName: {
  2. "service": "<WSDL service name>",
  3. "port": "<WSDL port name>",
  4. "operation": "<WSDL operation name>"
  5. }

If you have more than one operations to map, it might be easier to edit theDataSource JSON after it’s been created. See below for the example of themapping of the WSDL binding operations and Node.js methods.

  1. {
  2. "name": "ds",
  3. "connector": "soap",
  4. "url": "http://calculator-webservice.mybluemix.net/calculator",
  5. "wsdl": "http://calculator-webservice.mybluemix.net/calculator?wsdl",
  6. "remotingEnabled": true,
  7. // ADD THIS SNIPPET
  8. "operations": {
  9. "add": {
  10. "service": "CalculatorService", //WSDL service name
  11. "port": "CalculatorPort", //WSDL port name
  12. "operation": "add" //WSDL operation name
  13. },
  14. "subtract": {
  15. "service": "CalculatorService",
  16. "port": "CalculatorPort",
  17. "operation": "subtract"
  18. }
  19. }
  20. // END OF THE SNIPPET
  21. }

For details, you can refer to the SOAP connector’s operations property:https://github.com/strongloop/loopback-connector-soap#operations-property

Datasource for REST service

When calling REST services, select REST services for connector. We’ll leavethe default for the last 3 prompts.

  1. $ lb4 datasource
  2. ? Datasource name: restds
  3. ? Select the connector for restds: REST services (supported by StrongLoop)
  4. ? Base URL for the REST service: https://swapi.co/api/
  5. ? Default options for the request:
  6. ? An array of operation templates:
  7. ? Use default CRUD mapping: No

The next step is to edit the DataSource JSON file for options andoperations.

The REST connector uses therequest module as the HTTP client. Youcan configure the same options as for the request() function. See details inthis documentation page.

The template object specifies the REST API invocation as a JSON template. Youcan find more details in theDefining a custom method using a template page.

  1. {
  2. "name": "restds",
  3. "connector": "rest",
  4. "baseURL": "https://swapi.co/api/",
  5. "crud": false,
  6. "options": {
  7. "headers": {
  8. "accept": "application/json",
  9. "content-type": "application/json"
  10. }
  11. },
  12. "operations": [
  13. {
  14. "template": {
  15. "method": "GET",
  16. "url": "https://swapi.co/api/people/{personId}"
  17. },
  18. "functions": {
  19. "getCharacter": ["personId"]
  20. }
  21. }
  22. ]
  23. }

Refer to the detailed information in [configure the options] and [templates].

Add a service

Add a service using the Service generator and specifythe DataSource that you just created.

Define the methods that map to the operations

In the Service interface, define the methods that map to the operations of yourexternal service.

To promote type safety, we recommend you to declare data types and serviceinterfaces in TypeScript and use them to access the service proxy.

  1. export interface CalculatorService {
  2. add(args: CalculatorParameters): Promise<AddResponse>;
  3. subtract(args: CalculatorParameters): Promise<SubtractResponse>;
  4. }
  5. export interface AddResponse {
  6. result: {
  7. value: number;
  8. };
  9. }
  10. export interface SubtractResponse {
  11. result: {
  12. value: number;
  13. };
  14. }
  15. export interface CalculatorParameters {
  16. intA: number;
  17. intB: number;
  18. }

Alternately, we also provide a weakly-typed generic service interface asfollows:

  1. /**
  2. * A generic service interface with any number of methods that return a promise
  3. */
  4. export interface GenericService {
  5. [methodName: string]: (...args: any[]) => Promise<any>;
  6. }

Add a Controller

Add a controller using the Controller generator withthe Empty Controller option.

Inject the Service in the constructor

  1. constructor(
  2. @inject('services.CalculatorService')
  3. protected calculatorService: CalculatorService,
  4. ) {}

Add the REST endpoints

This will be similar to how you normally add a REST endpoint in a Controller.The only difference is you’ll be calling the methods that you’ve exposed in theService interface.

  1. @get('/add/{intA}/{intB}')
  2. async add(
  3. @param.path.integer('intA') intA: number,
  4. @param.path.integer('intB') intB: number,
  5. ): Promise<AddResponse> {
  6. //Preconditions
  7. return this.calculatorService.add(<CalculatorParameters>{
  8. intA,
  9. intB,
  10. });
  11. }

More examples

Testing your application

Make service proxies easier to test

While @serviceProxy decorator makes it easy to use service proxies incontrollers, it makes it difficult to write integration tests to verify thatservice proxies are correctly configured/implemented in respect to the mostrecent API provided by the backend service implementation. To make serviceproxies easy to test, we recommend to write aProvider class that will allow bothcontrollers and integration tests to access the same service proxyimplementation.

  1. import {getService, juggler} from '@loopback/service-proxy';
  2. import {inject, Provider} from '@loopback/core';
  3. import {GeocoderDataSource} from '../datasources/geocoder.datasource';
  4. export class GeoServiceProvider implements Provider<GeoService> {
  5. constructor(
  6. @inject('datasources.geoService')
  7. protected dataSource: juggler.DataSource = new GeocoderDataSource(),
  8. ) {}
  9. value(): Promise<GeocoderService> {
  10. return getService(this.dataSource);
  11. }
  12. }

Troubleshooting

If you get the error about theapp.serviceProvider() function is needed for ServiceBooter, make sure youapplyServiceMixinto your Application class in the application.ts.

  1. export class MyLoopBackApplication extends BootMixin(
  2. ServiceMixin(RepositoryMixin(RestApplication)),
  3. )

Please refer toTesting Your Applicationfor guidelines on integration testing.