Custom app external fetch backend permissions error

I am building a Confluence app that pulls images from Imgur api and displays them in Confluence pages.

Here is the JSX

import ForgeUI, { render, Fragment, Macro, Text, Image, useProductContext, useState, useEffect } from "@forge/ui";
import { fetch } from '@forge/api';

const IMGUR_API_ENDPOINT = 'https://api.imgur.com/3/gallery/hot/viral/0.json';

const App = () => {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const fetchData = async () => {
            try {
                const response = await fetch(IMGUR_API_ENDPOINT, {
                    headers: {
                        'Authorization': 'Client-ID <client-id>' // Replace with your Client-ID
                    }
                });

                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }

                const result = await response.json();
                console.log("Received data:", result);  // Log received data
                setData(result.data);
                setLoading(false);
            } catch (err) {
                console.error("Error occurred:", err);  // Log any error
                setError(err);
                setLoading(false);
            }
        };

        fetchData();
    }, []);

    if (loading) return <Text>Loading images...</Text>;
    if (error) return <Text>Error: {error.message}</Text>;

    return (
        <Fragment>
            <Text>Hot Images from Imgur:</Text>
            {data && data.map(image => (
                <Fragment key={image.id}>
                    <Image src={image.link} alt={image.title} />
                    <Text>{image.title}</Text>
                </Fragment>
            ))}
        </Fragment>
    );
};

export const run = render(
    <Macro
        app={<App />}
    />
);

and here is the manifest.yml

  modules:
    macro:
      - key: imgur-gallery-macro
        function: main
        title: Imgur Gallery Viewer
        description: View hot images from Imgur in Confluence.
    function:
      - key: main
        handler: index.run
  app:
    id: ari:cloud:ecosystem::app/<removed>
  permissions:
    external:
      fetch:
        backend:
          - 'https://api.imgur.com/*'

I keep getting the error even though I have added the URL. At a loss on how to resolve this error.

  message: 'URL not included in the external fetch backend permissions: https://api.imgur.com/3/gallery/hot/viral/0.json. Visit go.atlassian.com/forge-egress for more information.',
  name: 'REQUEST_EGRESS_ALLOWLIST_ERR',
  status: 403
}

I have read and re-read the Forge egress article go.atlassian.com/forge-egress. I have tried formatting the URL with a wild card and without, with and without quotes, and as the full URL. I have gone through the steps of forge upgrade and forge tunnel and forge deploy after making changes. I am unsure what else I need to do to make this work. This is a API endpoint that allows anonymous users so it shouldn’t require the full OAuth workflow or am wrong in saying that?

Hi @ShaunFlagg ,

Did you re-deploy the app after adding the permission to the manifest> If so, you need to do this and may be prompted in the CLI to update the installation of the app.

Also, I think your anonymous useEffect method needs to be declares as async and fetchData needs to be invoked with await:

    useEffect(() => {
....
        await fetchData();

Regards,
Dugald

Yes, as it’s a UI Kit (original) app, you need to include an async and await to ensure you await asynchronous code. If not, the lambda might return before the async code has had a chance to finish executing.

I’ve written a blog A deeper look at hooks in Forge which might help explain it!

1 Like

Thanks @mpaisley that was just the guidance I needed!

See the updated code below with the async and await. I also changed the endpoint for the images so I could get back some beautiful nature scapes instead of weird and disturbing images from viral and hot feed.

Also I added the images url to the manifest yml

external:
      images:
        - 'https://i.imgur.com/*'
import ForgeUI, { render, Fragment, Macro, Text, Image, useProductContext, useState, useEffect } from "@forge/ui";
import { fetch } from '@forge/api';

const IMGUR_API_ENDPOINT = 'https://api.imgur.com/3/gallery/r/earthporn/0';

const App = () => {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(async () => {
        const fetchData = async () => {
            try {
                const response = await fetch(IMGUR_API_ENDPOINT, {
                    headers: {
                        'Authorization': 'Client-ID <client-id>' // Replace with your Client-ID
                    }
                });

                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }

                const result = await response.json();
                console.log("Received data:", result);  // Log received data
                setData(result.data);
                setLoading(false);
            } catch (err) {
                console.error("Error occurred:", err);  // Log any error
                setError(err);
                setLoading(false);
            }
        };
        await fetchData();
    }, []);

    if (loading) return <Text>Loading images...</Text>;
    if (error) return <Text>Error: {error.message}</Text>;

    return (
        <Fragment>
            <Text>Our beautiful Earth from Imgur:</Text>
            {data && data.map(image => (
                <Fragment key={image.id}>
                    <Image src={image.link} alt={image.title} />
                    <Text>{image.title}</Text>
                </Fragment>
            ))}
        </Fragment>
    );
};

export const run = render(
    <Macro
        app={<App />}
    />
);

1 Like