diff --git a/docs/architecture-decisions/adr013-use-node-fetch.md b/docs/architecture-decisions/adr013-use-node-fetch.md new file mode 100644 index 0000000000..d802988ae4 --- /dev/null +++ b/docs/architecture-decisions/adr013-use-node-fetch.md @@ -0,0 +1,71 @@ +--- +id: adrs-adr013 +title: 'ADR013: Proper use of HTTP fetching libraries' +# prettier-ignore +description: Architecture Decision Record (ADR) for the proper use of fetchApiRef, node-fetch, and cross-fetch for data fetching. +--- + +## Context + +Using multiple HTTP packages for data fetching increases the complexity and the +support burden of keeping said package up to date. + +## Decision + +Backend (node) packages should use the `node-fetch` package for HTTP data +fetching. Example: + +```ts +import fetch from 'node-fetch'; +import { ResponseError } from '@backstage/errors'; + +const response = await fetch('https://example.com/api/v1/users.json'); +if (!response.ok) { + throw await ResponseError.fromResponse(response); +} +const users = await response.json(); +``` + +Frontend plugins and packages should prefer to use the +[`fetchApiRef`](https://backstage.io/docs/reference/core-plugin-api.fetchapiref). +It uses `cross-fetch` internally. Example: + +```ts +import { useApi } from '@backstage/core-plugin-api'; +const { fetch } = useApi(fetchApiRef); + +const response = await fetch('https://example.com/api/v1/users.json'); +if (!response.ok) { + throw await ResponseError.fromResponse(response); +} +const users = await response.json(); +``` + +Isomorphic packages should have a dependency on the `cross-fetch` package for +mocking and type definitions. Preferably, classes and functions in isomorphic +packages should accept an argument of type `typeof fetch` to let callers supply +their preferred implementation of `fetch`. This lets them adorn the calls with +auth or other information, and track metrics etc, in a cross-platform way. +Example: + +```ts +import crossFetch from 'cross-fetch'; + +export class MyClient { + private readonly fetch: typeof crossFetch; + + constructor(options: { fetch?: typeof crossFetch }) { + this.fetch = options.fetch || crossFetch; + } + + async users() { + return await this.fetch('https://example.com/api/v1/users.json'); + } +} +``` + +## Consequences + +We will gradually transition away from third party packages such as `axios`, +`got` and others. Once we have transitioned to `node-fetch` we will add lint +rules to enforce this decision. diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 4750e1d49d..6c4c5261d9 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -287,7 +287,8 @@ "architecture-decisions/adrs-adr009", "architecture-decisions/adrs-adr010", "architecture-decisions/adrs-adr011", - "architecture-decisions/adrs-adr012" + "architecture-decisions/adrs-adr012", + "architecture-decisions/adrs-adr013" ], "FAQ": ["FAQ"] } diff --git a/mkdocs.yml b/mkdocs.yml index 625236f737..81dad55b84 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -186,6 +186,8 @@ nav: - ADR009 - Entity References: 'architecture-decisions/adr009-entity-references.md' - ADR010 - Luxon Date Library: 'architecture-decisions/adr010-luxon-date-library.md' - ADR011 - Plugin Package Structure: 'architecture-decisions/adr011-plugin-package-structure.md' + - ADR012 - Plugin Package Structure: 'architecture-decisions/adr012-use-luxon-locale-and-date-presets.md' + - ADR013 - Plugin Package Structure: 'architecture-decisions/adr013-use-node-fetch.md' - Support: - Backstage Project Structure: 'support/project-structure.md' - Glossary: glossary.md