Last updatedJun 19, 2020

Rate this page:

Create a GIPHY app using Forge UI

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:

  • Forge CLI (version 0.13.0 or later). To update your CLI version, run npm install -g @forge/cli@latest on the command line.
  • The latest version of Forge UI. To update your Forge UI version, navigate to the app's top-level directory, and run npm install @forge/ui@latest --save on the command line.
  • An Atlassian site with Jira and Confluence Cloud where you can install your app.
  • A GIPHY API key. Go to GIPHY help to request an API key.

See Set up Forge for step by step instructions. 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 Forge UI to display.
  • 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
    forge create
    1. Enter a name for the app. For example, giphy-app.
    2. Select the confluence-macro-typescript template from the list.
  3. Open the app directory to see the app files.

Step 2: Use the macro module

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. Replace the scopes entry with [], an empty array.

Your manifest file should look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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>'
  name: giphy-app

See Manifest to learn more about the manifest file.

Step 3: Add a UI

Add UI elements 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
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // Import required components from Forge UI library
    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 content={title}/>
            <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 content="Random GIF!"/>
                <ImageCard src={url} title={title}/>
            </Fragment>
        );
    };
    
    // Exporting the above App function by exporting via 'run'
    export const run = render(<App/>);

In this code:

  • The import statement lists the components to use from the Forge UI. You’ll use Fragment, Image to display the GIF. See Forge UI components to learn more about these components.
  • The TypeScript interface is like a syntactical contract to which an object should conform. The ImageCardProps interface contains title and src, which are used by the component ImageCard. title comes from the GIPHY API response. src is the URL and fixed height of the GIF.
  • ImageCard consists of a Fragment, which contains a Text component and an Image component.
  • The App function is where the GIPHY API call is linked to the UI. This function returns a Fragment that contains a Text component and an ImageCard component. The code has values for a GIF. Later in this tutorial, you’ll update this to make a call to get a random GIF. The sample response from GIPHY for obtaining a GIF by ID has the information you’ll use for title and src in ImageCard.
  • The export of the run constant provides the mechanism that renders your app whenever the App function returns.

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
    forge deploy
  2. Install your app by running:

    1
    forge install
    1. Select Confluence as the product.
    2. Enter the URL for your development site (for example, your-domain.atlassian.net).
    3. Important: Authorize your app by opening the URL displayed.
  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 APl

Next, turn the static app into a dynamic app by replacing the hardcoded response with an API call (step 2 from the flow diagram above). 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
    FORGE_USER_VAR_GIPHY_API_KEY=<your-giphy-api-key> forge tunnel
  2. Open the src/index.txs file.

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    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 Forge UI 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
    const [{ title, url }, setRandomGif] = useAction(getRandomGif, getRandomGif);
    Note, 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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Importing required components from Forge UI library
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 content={title}/>
        <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 content="Random GIF!"/>
            <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 Forge UI.

  1. Open the src/index.tsx file.
  2. Add Button to the Forge UI import statement.
  3. Add the button by replacing the return statement in the App function with:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    return (
        <Fragment>
          <Text content="Random GIF!" />
          <ImageCard src={url} title={title} />
          <Button
            text="🔀 Shuffle!"
            onClick={() => {
              setRandomGif();
            }}
          />
        </Fragment>
      );

Your index.tsx file should look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// Importing required components from Forge UI library
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 content={title}/>
        <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 content="Random GIF!" />
            <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
    forge variables:set GIPHY_API_KEY <your-giphy-api-key>
  2. Deploy the app by running:

    1
    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: