Custom request SDK

Modernjs's BFF is isomorphic in CSR and SSR.In the browser side rely on the Fetch API,On the server relies on the node-fetch.However, in many business scenarios we need to do some additional processing of the request or response, for example:

  • Write auth information in the request header
  • Uniform processing of response data or errors
  • The browser's native fetch function is not available for a particular platform, and other methods of sending requests are required

For the above scenario, Modern.js provides the configure function,the customization capabilities range from low to high and can be used to configure ssr pass-through request headers, interceptors, and request SDKs..

CAUTION

The configure function call needs to be called before all BFF requests are sent, to ensure that the default request configuration is overridden.

App.tsx
import { configure } from '@modern-js/runtime/bff';

configure({
  request: customRequest
})

Configure ssr pass-through request headers

In scenarios where both Modern.js SSR and BFF are used, it is often necessary to pass some request headers on SSR page requests to the BFF service.

For example, the project has a page address https://website.com, which is an SSR page, and the API interface https://website.com/api/info will be called in the component, which requires the user's cookie information for authentication. When the page requests this API interface, it needs to pass the cookie requested by the SSR page to BFF.

Currently the following request headers are automatically passed through in Modern.js:

  • cookie
  • x-tt-logid
  • user-agent
  • x-tt-stress

The request header can be configured via configure. For example, in the following example, Modern.js will automatically pass the cookie information of the SSR page request to the BFF service:

App.tsx
import { configure } from '@modern-js/runtime/bff';

configure({
  allowedHeaders: ['x-uid']
})

Configuring Interceptors

In certain business scenarios, there is a requirement for unified processing of requests and responses, and interceptors can be configured to fulfill these requirements in such scenarios.

App.tsx
configure({
  // The request here is the default request sdk for bff, and the interceptor function needs to return a new request.
  // The return value of the new request must be the result of the parse body
  interceptor(request){
    return async(url, params) => {
      const res = await request(url, params);
      return res.json();
    };
  };
});

Configure custom request SDK

If the requirements cannot be met by configuring interceptors alone and need to further customize the request SDK, you can configure the custom request SDK by using the configure function:

CAUTION

Send a request to the BFF service when the server side renders, Modern.js will find the BFF service intranet IP via service discovery and send requests via IP to improve performance. This optimization is lost if a custom request SDK is used.

App.tsx
import nodeFetch from 'node-fetch';

const customFetch = (input: RequestInfo | URL, init: RequestInit) => {
  const curFetch = process.env.MODERN_TARGET !== 'node' ? fetch : nodeFetch as unknown as typeof fetch;
  return curFetch(input, init).then(async res => {
    const data = await res.json();
    data.hello = 'hello custom sdk';
    return data;
  });
};

configure({
  request: customFetch,
});

The configuration custom request SDK has the following conventions:

  • The configure function allows you to configure a request function whose input is the same as the Fetch or node-fetch in the browser, and all BFF functions will send requests through this function
  • The return value of the request function must be the actual data returned by the interface, not a Promise, otherwise the BFF function will not return data properly.
  • In the case of SSR projects, request must support both browser-side and server-side sending of requests.

Example of custom request SDK using axios:

App.tsx
import { configure } from '@modern-js/runtime/bff';
import type { Method, AxiosRequestHeaders as Headers } from 'axios';

configure({
  async request(...config: Parameters<typeof fetch>) {
    const [url, params] = config;
    const res = await axios({
      url: url as string,  // Here, because of some incompatibility between fetch and axios types, you need to use `as`
      method: params?.method as Method,
      data: params?.body,
      headers: params?.headers as Headers,
    });
    return res.data;
  },
});