From 70f7fa6f0e45c88e53cac3c5123a1eb36ac47253 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 9 May 2025 21:39:28 +0200 Subject: [PATCH] docs: clarify examples Signed-off-by: Vincenzo Scamporlino --- docs/plugins/call-existing-api.md | 113 +++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 35 deletions(-) diff --git a/docs/plugins/call-existing-api.md b/docs/plugins/call-existing-api.md index 065427b9ee..60a71f089a 100644 --- a/docs/plugins/call-existing-api.md +++ b/docs/plugins/call-existing-api.md @@ -20,11 +20,18 @@ such as `axios`. Example: -```ts -// Inside your component -fetch('https://api.frobsco.com/v1/list') - .then(response => response.json()) - .then(payload => setFrobs(payload as Frob[])); +```ts title="plugins/my-awesome-plugin/src/components/AwesomeUsersTable.tsx" +import useAsync from 'react-use/esm/useAsync'; + +function AwesomeUsersTable() { + const { value, loading, error } = useAsync(async () => { + const response = await fetch('https://api.frobsco.com/v1/list'); + return response.json(); + }, []); + + + ... +} ``` Internally at Spotify, this has not been a very common choice. Third party APIs @@ -76,12 +83,26 @@ proxy: '/frobs': http://api.frobsco.com/v1 ``` -```ts -// Inside your component -const backendUrl = config.getString('backend.baseUrl'); -fetch(`${backendUrl}/api/proxy/frobs/list`) - .then(response => response.json()) - .then(payload => setFrobs(payload as Frob[])); +```tsx title="plugins/frobs-aggregator/src/components/FrobsAggregator.tsx" +import { + useApi, + discoveryApiRef, + fetchApiRef, +} from '@backstage/core-plugin-api'; +import useAsync from 'react-use/esm/useAsync'; + +function FrobsAggregator() { + const fetchApi = useApi(fetchApiRef); + const discoveryApi = useApi(discoveryApiRef); + + const { value, loading, error } = useAsync(async () => { + const baseUrl = await discoveryApi.getBaseUrl('proxy'); + const response = await fetch(`${baseUrl}/frobs`); + return response.json(); + }, [fetchApi, discoveryApi]); + + // ... +} ``` The proxy is powered by the `http-proxy-middleware` package. See @@ -112,28 +133,55 @@ system. The above mentioned proxy is actually one such plugin. If you were in need of a more involved integration than just direct access to the FrobsCo API, or if you needed to hold state, you may want to make such a plugin. -Example: +For example, assuming you have created a new backend plugin called +`frobs-aggregator`, you can add a new route like this: -```ts -// Inside your component -const backendUrl = config.getString('backend.baseUrl'); -fetch(`${backendUrl}/frobs-aggregator/summary`) - .then(response => response.json()) - .then(payload => setSummary(payload as FrobSummary)); +```tsx title="plugins/frobs-aggregator-backend/src/router.ts" +import Router from 'express-promise-router'; + +export async function createRouter() { + const router = Router(); + router.use(express.json()); + + /* highlight-add-start */ + router.get('/summary', async (req, res) => { + const agg = await Promise.all([ + fetch('https://api.frobsco.com/v1/list'), + fetch('http://flerps.partnercompany.com:8080/flerp-batch'), + database.currentThunk(), + ]).then(async ([frobs, flerps, thunk]) => { + return computeAggregate(await frobs.json(), await flerps.json(), thunk); + }); + res.status(200).json(agg); + }); + /* highlight-add-end */ +} ``` -```ts -// Inside a new frobs-aggregator backend plugin -router.use('/summary', async (req, res) => { - const agg = await Promise.all([ - fetch('https://api.frobsco.com/v1/list'), - fetch('http://flerps.partnercompany.com:8080/flerp-batch'), - database.currentThunk(), - ]).then(async ([frobs, flerps, thunk]) => { - return computeAggregate(await frobs.json(), await flerps.json(), thunk); - }); - res.status(200).json(agg); -}); +Then you can fetch the data from your frontend plugin like this: + +```tsx title="plugins/frobs-aggregator/src/components/FrobsAggregator.tsx" +import { + useApi, + discoveryApiRef, + fetchApiRef, +} from '@backstage/core-plugin-api'; +import useAsync from 'react-use/esm/useAsync'; + +function FrobsAggregator() { + const fetchApi = useApi(fetchApiRef); + const discoveryApi = useApi(discoveryApiRef); + + const { value, loading, error } = useAsync(async () => { + // highlight-next-line + const baseUrl = await discoveryApi.getBaseUrl('frobs-aggregator'); + // highlight-next-line + const response = await fetch(`${baseUrl}/summary`); + return response.json(); + }, [fetchApi, discoveryApi]); + + // ... +} ``` For a more detailed example, see @@ -166,8 +214,3 @@ There is a balance to strike regarding when to make an entirely separate backend for a purpose, and when to make a Backstage backend plugin that adapts something that already exists. General advice is not easy to give, but contact us on Discord if you have any questions, and we may be able to offer guidance. - -## Extending the GraphQL Model - -The extensible GraphQL backend layer is not built yet. This section will be -expanded when that happens. Stay tuned!