How to call Jira REST API (Cloud) from React?

Hi all, new learner here. I recently learned React and am trying to build an app for Jira.

I managed to successfully upload my React app (built with create-react-app) to my development Jira instance. But I can’t successfully call the Jira REST API to get an issue. In my learnings, I was able to call other APIs with React, so I’m not sure if Jira requires something special.

My code:

fetch('https://blah.atlassian.com/rest/api/3/issue/AP-1', {
      method: 'GET',
      headers: {
        'Authorization': `Basic ${Buffer.from(
          'email: api key'
        ).toString('base64')}`,
        'Accept': 'application/json'
      }
    }).then(response => response.json()
    ).then(jsonResponse => {
      console.log(jsonResponse.key);
    })

Does anyone know if I need to be doing something differently or authenticating somehow?

1 Like

Hey @AdamPramono

How you make calls to the Jira REST API from your frontend code depends on what framework you use. It is either Atlassian Connect or Forge.

If you are using Atlassian Connect…
… you can use the Atlassian Connect JS API. Make sure to have the Atlassian Connect JS dependency included in your iframe HTML.

You can call the API like so:

AP.request('/rest/api/latest/...', {
  success: function(responseText){
    alert(responseText);
  }
});

If you are using Custom UI with Forge…
… you can use the Custom UI Bridge to make calls to the Jira REST API like so:

import { requestJira } from '@forge/bridge';

requestJira('/rest/api/3/issue/ISSUE-1').then(response => {
  console.log(response);
});

Make sure you have the @forge/bridge package installed.

4 Likes

Could you share the error you’re seeing? Does the API call work when testing it in Postman?

@tbinna has shared some awesome example code above for the Connect and Forge platforms, however if you’re building a personal integration you should be able to get things working with just a Basic API token as well (what you appear to be using, and also the simplest way to go for personal scripts).

Here’s how I’m using a Basic API token to authenticate to Confluence (only difference is URL path) for one of my side projects, using Node:

contentForId.js

const fetch = require("node-fetch");

const API_KEY = "redacted";
const API_USER = "skubica@atlassian.com";

const contentForId = async (id) =>
  (
    await fetch(`https://foo.atlassian.net/content/${id}?expand=history,body.editor2`, {
      headers: {
        Authorization: `Basic ${Buffer.from(`${API_USER}:${API_KEY}`).toString(
          "base64"
        )}`,
      },
    })
  ).json();

module.exports = contentForId;

Yours looks pretty close, but testing in Postman and applying some closer scrutiny at your error message should get you over the line. Also, this is likely just how you’ve redacted your code snippet, but note that your Jira site path should be foo.atlassian.net, rather than .com.

Good luck, let me know how you go!

1 Like

Hey @tbinna

I also have this problem. The code you supplied there looks good and I have it working in the hbs file, as this one includes the layout which includes the atlassian connect js library you mentioned.
However, I want to do this from the JSX file.
When doing it in JSX, I get an error that AP cannot be found.
What import would I use for AP.Request to work in my JSX file, or what would be the proper way to do this?

Hey @JarrodMoura there shouldn’t be any difference really. You should be able to call AP.request() from your JSX file, or more explicitly window.AP.request(). If you call console.log(window.AP.request) in your JSX file you should see some output in your browser console.

If your Connect module renders in Jira, you could navigate to that page and use the browser console to test if Atlassian Connect JS has loaded correctly. Make sure to select your iframe in the console dropdown (see screenshot below) before trying to out window.AP.request in the console.

If it works in the browser it should also work in your JSX file. Otherwise, the issue may be with the setup of your iframe and loading the JS code in the iframe.

Hey @tbinna,

Thanks for the super quick response and the new insight, this will help me in future debugging.

I can now see that it can pickup the window.AP.request in my console as follows:

I’m still unable to access it from the JSX code. Maybe I’m missing something small, here is the error and the code:
Error:

ReferenceError: window is not defined
    at HelloWorld.getProjects (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\views\node\hello-world.js:146:5)
    at HelloWorld.render (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\views\node\hello-world.js:161:10)
    at processChild (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\react-dom\cjs\react-dom-server.node.development.js:3134:18)
    at resolve (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\react-dom\cjs\react-dom-server.node.development.js:2960:5)
    at ReactDOMServerRenderer.render (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\react-dom\cjs\react-dom-server.node.development.js:3435:22)
    at ReactDOMServerRenderer.read (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\react-dom\cjs\react-dom-server.node.development.js:3373:29)
    at renderToString (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\react-dom\cjs\react-dom-server.node.development.js:3988:27)
    at View.ssrEngine [as engine] (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\server-side-rendering.js:44:27)
    at View.render (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\express\lib\view.js:135:8)
    at tryRender (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\express\lib\application.js:640:10)
    at Function.render (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\express\lib\application.js:592:3)
    at ServerResponse.render (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\express\lib\response.js:1012:7)
    at D:\Atlassian\Atlassian Getting Started App\jira-getting-started\routes\index.js:13:9
    at Layer.handle [as handle_request] (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\express\lib\router\layer.js:95:5)
    at next (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\express\lib\router\route.js:137:13)
    at requestHandler (D:\Atlassian\Atlassian Getting Started App\jira-getting-started\node_modules\atlassian-connect-express\lib\middleware\request.js:126:5)
GET /hello-world?xdm_e=https%3A%2F%2Fatlasconflux.atlassian.net&xdm_c=channel-my-app-poc__hello-world-page-jira-2&cp=&xdm_deprecated_addon_key_do_not_use=my-app-poc&lic=none&cv=1001.0.0-SNAPSHOT&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI1YWQxYmI0YWI3ODNkMjJiNWRhZTNjMGYiLCJxc2giOiJiMTRiODExMjQ3YTViMjdkZTllZjgzYmYyYTc1N2QxNDEwYzk0OWUxMjA3YTYwYmY5NTIzZTYzOGY3ZWQ3OTdlIiwiaXNzIjoiOTJhZjNjOTQtOTMyNC0zZjg5LWIyNjMtYTMwMjA0NTZhYjhmIiwiY29udGV4dCI6e30sImV4cCI6MTYxNzg3NDAwMywiaWF0IjoxNjE3ODczMTAzfQ.Dqn8er6qu4c8nRyjo2wYIq-UzAv-Wk20pr_hnyIwtvI 500 946.273 ms - -

Code:

import SectionMessage from '@atlaskit/section-message';

import React, { Component } from 'react';

export default class HelloWorld extends Component {

  constructor(props) {

    super(props);

    this.getProjects = this.getProjects.bind(this);

  }

  // const [excitementLevel, setExcitementLevel] = React.useState(0);

  getProjects() {

    window.AP.request({

      url: '/rest/api/latest/project',

      success: function (response) {

        // convert the string response to JSON

        response = JSON.parse(response);

        // dump out the response to the console

        console.log(response);

      },

      error: function () {

        console.log(arguments);

      }

    });

  }

  render() {

    this.getProjects();

    return <SectionMessage title="Cannot connect to the database" appearance="warning">

      <p>We are unable to save any progress at this time.</p>

    </SectionMessage>;

  }

}

Could it be that you are trying to do some server-side rendering? Maybe using Next.js or something? window is only available in the browser.

Ah, thanks for making me understand that the React is server side. I see there is an option to turn this off in the index.js, but this just seems to prevent the iframe from loading, so I’ll just leave it commented out:
//, browserOnly: true // you can set this to disable server-side rendering for react views

I’m not familiar with next.js, but if I go to the Atlassian Docs and just copy what they have there for retrieving a project list, they just use node-fetch. I gave this a whirl and it worked - woohoo!

// This code sample uses the 'node-fetch' library:
// https://www.npmjs.com/package/node-fetch
const fetch = require('node-fetch');

fetch('https://your-domain.atlassian.com/rest/api/3/project/search', {
  method: 'GET',
  headers: {
    'Authorization': `Basic ${Buffer.from(
      'email@example.com:<api_token>'
    ).toString('base64')}`,
    'Accept': 'application/json'
  }
})
  .then(response => {
    console.log(
      `Response: ${response.status} ${response.statusText}`
    );
    return response.text();
  })
  .then(text => console.log(text))
  .catch(err => console.error(err));

I’ll just have to figure out how to get around the user and token situation as I want the app to impersonate the user.
But thanks again @tbinna

Hi. I have just gotten to this problem myself. Did you figure out a way to get current user info and the ability to act on behalf of the user?

Thanks in advance, Halli

Hey @HallbjrnMagnsson

I’m sorry for the late response.

You might have sorted this out by now? If you did, would you mind sharing how?

But to close the loop on this, the way I sorted this out, was to resort to using Forge instead. It’s a really cool framework that lets you either select to create a Custom UI or what they call UIKit, which is the lighter version. You can even create triggers if you’d like.

Then all of this user stuff is handled for you, when calling one of the endpoints you’d simply add:

.asUser()
// or even .asApp()

There’s a lot of documentation on Forge and from what I can tell Atlassian are pretty excited about it, which I’ve found cantagious :grinning_face_with_smiling_eyes:

An example of what the above would look like in Forge:
Documentation

// This sample uses Atlassian Forge
// https://developer.atlassian.com/platform/forge/
import api from "@forge/api";

const response = await api.asApp().requestJira('/rest/api/3/issue/{issueIdOrKey}', {
  headers: {
    'Accept': 'application/json'
  }
});

console.log(`Response: ${response.status} ${response.statusText}`);
console.log(await response.json());
1 Like

Hi. Thanks for the reply. I have figured it out. I make info available from backend to frontend via props. I use AP.request in the frontend to act as the user and a REST api to send info back to the backend.

But I´m totally with you that Forge is an exciting .

B.r. Halli

2 Likes