Last updated Feb 27, 2023

Rate this page:

Create a GIPHY app using the UI kit

This tutorial describes how to make API calls to an external API from your Forge app and display the result on a Confluence Cloud page. You’ll use the GIPHY API to display GIFs on the Confluence page. The result will look like this:

A gif is added to a Confluence page

Before you begin

To complete this tutorial, you need the following:

We recommend that you complete all the steps in Getting started so that you’re familiar with the Forge development process.

Before we dive into the code, let's review how this app works.

A flow diagram describing how the app interacts with the GIPHY API and Confluence site

The flow diagram shows the app’s 3 main parts.

  • Macro module: The context in which the app is installed. In this case, a macro for the editor in your Confluence Cloud site.
  • Function module: The function you’ll write that defines the UI component to be displayed in the app.
  • GIPHY API: The API used to fetch the GIFs from GIPHY.

These parts work together as follows:

  1. When you use this app in the Confluence editor, the macro module calls the associated function.
  2. That function fetches GIFs using the GIPHY API.
  3. The GIPHY app adds the fetched GIF to the UI, which is rendered by the app.

Step 1: Create your app

Create an app based on the Hello world TypeScript template.

  1. Navigate to the directory where you want to create the app.

  2. Create your app by running:

    1
    2
    forge create
    
  3. Enter a name for the app. For example, giphy-app.

  4. Select the UI Kit category from the list.

  5. Select the confluence-macro-typescript template from the list.

  6. Open the app directory to see the app files.

Step 2: Configure the app manifest

This app uses a Confluence macro module. Macros enable you to add functionality or include dynamic content in a Confluence page.

  1. In the app’s top-level directory, open the manifest.yml file.
  2. Change the key under macro to giphy.
  3. Change the title under macro to GIPHY.
  4. Change the description under macro to View random GIPHY gifs!
  5. Add or replace the scopes entry with [], an empty array.

Your manifest file should look like this:

1
2
permissions:
  scopes: []
modules:
  macro:
    - key: giphy
      function: main
      title: GIPHY
      description: View random GIPHY gifs!
  function:
    - key: main
      handler: index.run
app:
  id: '<your-app-id>'

See Manifest to learn more about the manifest file.

Step 3: Add a user interface

Add UI kit components that render when the app is called. You’ll use a sample response from the GIPHY API to make a static app (steps 1 and 3 from the flow diagram above).

  1. Open the src/index.tsx file.

  2. Replace the contents of the file with:

    1
    2
    // Import required components from the UI kit
    import ForgeUI, { render, Text, Fragment, Image } from '@forge/ui';
    
    // ImageCardProps interface which will be used by ImageCard component
    interface ImageCardProps {
        title: string;
        src: string;
    }
    
    // ImageCard component containing text and image
    const ImageCard = ({title, src}: ImageCardProps) => (
        <Fragment>
            <Text>{title}</Text>
            <Image src={src} alt={title}/>
        </Fragment>
    );
    
    // App function will return the final output
    const App = () => {
        const {title, url} = {
            title: "awesome avalanche GIF",
            url: "https://media3.giphy.com/media/26vUJR5VABcUJaCTm/200.gif?cid=74f3ab6481fcd606c80e02418b301c17130050edc03b7521&rid=200.gif"
        };
    
        return (
            <Fragment>
                <Text>Random GIF!</Text>
                <ImageCard src={url} title={title}/>
            </Fragment>
        );
    };
    
    // Exporting the above App function by exporting via 'run'
    export const run = render(<App/>);
    

In this code:

Step 4: Build, deploy, and install

Build, deploy, and install the app to see it in your Confluence site.

  1. In the app's top-level directory, deploy your app by running:

    1
    2
    forge deploy
    
  2. Install your app by running:

    1
    2
    forge install
    
    1. Select Confluence as the product.
    2. Enter the URL for your development site (for example, your-domain.atlassian.net).
  3. Edit a Confluence page and insert the app using the quick insert menu (activated by pressing /), as shown below.

The app is inserted into a Confluence page

Step 5: Call the GIPHY API

Next, we'll turn the static app into a dynamic app by replacing the hardcoded response with an API call (step 2 from the flow diagram above).

To allow our app to access external resources, we need to update the manifest to allow API calls to the GIPHY API. Add the GIPHY API domain (api.giphy.com) to the list of approved domains under permissions.external.fetch.backend.

Your manifest.yml permissions should look like this:

1
2
permissions:
  scopes: []
  external:
    fetch:
      backend:
        - 'api.giphy.com'

See Permissions - External Permissions to learn more about external permissions.

You’ll speed up your development process by starting a tunnel, which uses the local code where your app is installed. You need to set the GIPHY API key as a local variable so your code can access it when making calls to the GIPHY API.

  1. Start a tunnel with access to your GIPHY API key by running:

    1
    2
    FORGE_USER_VAR_GIPHY_API_KEY=<your-giphy-api-key> forge tunnel
    
  2. Open the src/index.tsx file.

  3. Add the following code directly after the import statement to call the GIPHY API:

    1
    2
    import api from "@forge/api";
    
    // GIPHY API base URL
    const GIPHY_API_BASE = 'https://api.giphy.com/v1/gifs/';
    
    // GiphyJson interface to be used by our getRandomGif function
    interface GiphyJson {
      title: string;
      url: string;
    }
    
    // getRandomGif function makes the GIPHY API call to get a random GIF and filter out title and url
    const getRandomGif = async (): Promise<GiphyJson> => {
      console.log("Making GIPHY API call...")
      const response = await api.fetch(
        `${GIPHY_API_BASE}random?api_key=${process.env.GIPHY_API_KEY}&rating=g`,
      );
    
      const {
        data: {
          title,
          images: {
            fixed_height: { url },
          },
        },
      } = await response.json();
    
      return {
        title,
        url,
      };
    };
    

    In this code:

    1. GIPHY_API_BASE is a constant containing the URL to call the GIPHY API.
    2. The GiphyJson interface has title and url properties for the ImageCard.
    3. getRandomGif is an asynchronous function that makes the API call and returns the result in the format of GiphyJson.
    4. fetch from the Runtime API makes a call to GIPHY’s random endpoint and stores the response. This function uses the GIPHY API key from your environment variables with process.env.GIPHY_API_KEY.
  4. Add the UI kit hook useAction to the import statement from @forge/ui.

  5. Replace const { title, url } and its value in the App function with the following to update the state:

    1
    2
    const [{ title, url }, setRandomGif] = useAction(getRandomGif, getRandomGif);
    

The first parameter of useAction defines how to update the state, and the second parameter is the initial value. useAction returns the current value, and a function you can call to update the value.

Your index.tsx file should look like this:

1
2
// Importing required components from the UI kit
import ForgeUI, { render, Text, Fragment, Image, useAction } from '@forge/ui';
// Importing the api object
import api from "@forge/api";

// GIPHY API base URL
const GIPHY_API_BASE = 'https://api.giphy.com/v1/gifs/';

// GiphyJson interface to be used by our getRandomGif function
interface GiphyJson {
    title: string;
    url: string;
}

// getRandomGif function makes the GIPHY API call to get a random GIF and filter out title and url
const getRandomGif = async (): Promise<GiphyJson> => {
    console.log("Making GIPHY API call...")
    const response = await api.fetch(
        `${GIPHY_API_BASE}random?api_key=${process.env.GIPHY_API_KEY}&rating=g`,
    );

    const {
        data: {
            title,
            images: {
                fixed_height: { url },
            },
        },
    } = await response.json();

    return {
        title,
        url,
    };
};

// ImageCardProps interface which will be used by ImageCard component
interface ImageCardProps {
    title: string;
    src: string;
}

// ImageCard component containing text and image
const ImageCard = ({title, src}: ImageCardProps) => (
    <Fragment>
        <Text>{title}</Text>
        <Image src={src} alt={title}/>
    </Fragment>
);

// App function will return the final output
const App = () => {
    const [{ title, url }, setRandomGif] = useAction(getRandomGif, getRandomGif);

    return (
        <Fragment>
            <Text>Random GIF!</Text>
            <ImageCard src={url} title={title}/>
        </Fragment>
    );
};

// Exporting the above App function by exporting via 'run'
export const run = render(<App/>);

When you refresh the page in Confluence, a random GIF is displayed.

Step 6: Add a button

Instead of refreshing the page, add a button to load a new GIF. This will use the Button component from the UI kit.

  1. Open the src/index.tsx file.

  2. Add Button to the UI kit import statement.

  3. Add the button by replacing the return statement in the App function with:

    1
    2
    return (
        <Fragment>
          <Text>Random GIF!</Text>
          <ImageCard src={url} title={title} />
          <Button
            text="🔀 Shuffle!"
            onClick={() => {
              setRandomGif();
            }}
          />
        </Fragment>
    );
    

Your index.tsx file should look like this:

1
2
// Importing required components from the UI kit
import ForgeUI, { render, Text, Fragment, Image, useAction, Button } from '@forge/ui';
// Importing the api object
import api from "@forge/api";

// GIPHY API base URL
const GIPHY_API_BASE = 'https://api.giphy.com/v1/gifs/';

// GiphyJson interface to be used by our getRandomGif function
interface GiphyJson {
    title: string;
    url: string;
}

// getRandomGif function makes the GIPHY API call to get a random GIF and filter out title and url
const getRandomGif = async (): Promise<GiphyJson> => {
    console.log("Making GIPHY API call...")
    const response = await api.fetch(
        `${GIPHY_API_BASE}random?api_key=${process.env.GIPHY_API_KEY}&rating=g`,
    );

    const {
        data: {
            title,
            images: {
                fixed_height: { url },
            },
        },
    } = await response.json();

    return {
        title,
        url,
    };
};

// ImageCardProps interface which will be used by ImageCard component
interface ImageCardProps {
    title: string;
    src: string;
}

// ImageCard component containing text and image
const ImageCard = ({title, src}: ImageCardProps) => (
    <Fragment>
        <Text>{title}</Text>
        <Image src={src} alt={title}/>
    </Fragment>
);

// App function will return the final output
const App = () => {
    const [{ title, url }, setRandomGif] = useAction(getRandomGif, getRandomGif);

    return (
        <Fragment>
            <Text>Random GIF!</Text>
            <ImageCard src={url} title={title} />
            <Button
                text="🔀 Shuffle!"
                onClick={() => {
                    setRandomGif();
                }}
            />
        </Fragment>
    );
};

// Exporting the above App function by exporting via 'run'
export const run = render(<App/>);

Now you can use the button to display a new GIF.

Step 7: Deploy your updates

Now that the code is working, set your app environment variable and deploy the app so it keeps working after you close the tunnel.

  1. Store the GIPHY API key in your app environment variables by running:

    1
    2
    forge variables set GIPHY_API_KEY <your-giphy-api-key>
    
  2. Deploy the app by running:

    1
    2
    forge deploy
    

That’s it. You now have an app that fetches data from an external API and renders the result in the Confluence editor.

Next steps

Continue to one of the other tutorials or look through the reference pages to learn more.

Rate this page: