ethers.js icon indicating copy to clipboard operation
ethers.js copied to clipboard

Add Documentation for NetworkPlugin

Open seibelj opened this issue 6 months ago • 2 comments

Suggestion

I need to add the ability for a custom HTTP header to every request to the provider, as our node provider requires a Bearer auth token. I believe the way to do this is with NetworkPlugins, and I'm trying to read the source code to understand if it can be used to intercept HTTP calls to add a header, but it's confusing.

I would add:

  • The purpose of NetworkPlugins
  • What they can do
  • An example on how to implement one

seibelj avatar Feb 05 '24 21:02 seibelj

I figured out that you can just pass a new FetchRequest to the JsonRpcProvider constructor:

const fetcher = new FetchRequest(rpcUrl);
fetcher.setHeader(
    "Authorization",
    `Bearer ${api_key}`
);

NetworkPlugin would still be useful to document, it seems powerful.

seibelj avatar Feb 05 '24 21:02 seibelj

The NetworkPlugin is more for internal uses, to allow changes that might otherwise be major breaking changes, but to keep the internal change local.

Once a NetworkPlugin is exposed, then it becomes part of the public API, like the FeeDataNetworkPlugin which was added to allow networks to override fee data, which Polygon uses to access gas stations instead of the normal EIP-1559 sources (which don't produce valid results for Polygon). If a public API is required to be added, then I can easily add a public-facing NetworkPlugin though, so if there is a need, just let me know. :)

For the most part, sub-classing Provider should suffice for your use case though. For example, in your case, I'd prolly use:

class MyProvider extends JsonRpcProvider {
  constructor(url: string | UrlRequest, network?: Network, options?: JsonRpcProviderOptions) {
    if (typeof(url) === "string") { url = new FetchRequest(url); }
    url = url.clone();
    url.setHeader("Authorization", `Bearer ${ API_KEY }`);
    super(url, network, options);
  }
}

There are other features available on the FetchRequest object though for more advanced authentication. For example, often with a Bearer, you may need to sign the request. You can use the above pattern to wrap this behaviour as well, but if curious:

const request = new FetchRequest(someUrl);
request.preflightFunc = (req: FetchRequest) => {
  // The req here is a clone; you may modify freely without affecting the JsonRpcProvider
  const signature = mySigningFunc(req.url, req.body, (new Date()).getTime());
  req.setHeader("Authorization", `Bearer ${ myApiKey }:${ signature }`);
};

This will sign every request via the Bearer, with the url, body and current time. It is called before each request, including retries due to 429 requests, redirects, etc. This provides the most flexibility in making requests.

I have a backlog of documentation points I need to add, so I'll try to add this example to the Cookbook. I'm bringing on a second person to the team soon, so should have more time and help to flesh out the docs. :)

ricmoo avatar Feb 06 '24 00:02 ricmoo