The Adaptavist Group LogoDocumentation

Make API Calls with Fetch

Learn how to call Jira and external REST APIs from your scripts with Fetch, including GET and POST requests, error handling, and authentication overrides.

Fetch is a built-in JavaScript API for making HTTP requests. It's the TypeScript equivalent of what Groovy developers would have used Unirest or HttpBuilder for on Data Center; the standard way to call external services or REST APIs from your scripts.

Because Fetch is built into the JavaScript runtime, there's nothing to install or import. It's always available. For a complete reference, see the MDN Fetch API documentation.

Fetch is built around two core interfaces: Request, which represents the outgoing request, and Response, which represents what comes back. When you call fetch(), it returns a Promise that resolves to a Response as soon as the server responds with headers — the body hasn't arrived yet. That's why you need a second await to parse it:

const response = await fetch('https://api.example.com/data')  // waits for headers
const data = await response.json()                             // waits for the body

Basic usage

Fetch is asynchronous, so you always use it with await. It returns a Response object, which you then parse depending on the content type.

const response = await fetch('/rest/api/3/myself')
const me = await response.json()
console.log(me.displayName)

Notice the two await calls; one to wait for the response, and one to parse the body. This is a common pattern you'll use every time.

GET requests

const response = await fetch('/rest/api/3/issue/JRA-1?fields=summary,status')

if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`)
}
const issue = await response.json()

console.log(issue.fields.summary)

response.ok is true for any 2xx status code. It's good practice to check this before parsing the body.

POST requests

To send data, pass a second argument to fetch with the method, headers, and body:
const response = await fetch('/rest/api/3/issue', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    fields: {
      project: { key: 'JRA' },
      issuetype: { name: 'Task' },
      summary: 'Created via fetch'
    }
  })
})

const created = await response.json()
console.log(created.key)

Error handling

Fetch only throws an error for network failures. It does not throw for error status codes like 404 or 500. You need to handle those yourself:

try {
  const response = await fetch('/rest/api/3/issue/JRA-1')

  if (!response.ok) {
    throw new Error(`Request failed with status: ${response.status}`)
  }
  const issue = await response.json()
  console.log(issue.fields.summary)
} catch (error) {
  console.error('Fetch failed:', error)
}

This is a common gotcha for developers new to Fetch: always check response.ok.

Calling Atlassian's APIs

You can call Jira's REST API directly with Fetch using a /rest/api/3/ path. Authentication is handled automatically based on the Run As setting configured on your script; there's nothing extra to set up:

const response = await fetch('/rest/api/3/myself')
const user = await response.json()
console.log(user.displayName)

Overriding authentication

By default, calls use the item's Run as setting. To override it, you can either wrap a block of code with the @avst-sr/jira-client helpers, or set the authentication on a single request with a header.

Override a block of code

Wrap your calls in Users.asApp(...) to run as the ScriptRunner app, or Users.impersonateUser(accountId, ...) to run as a specific user. This applies to everything inside the block, including HAPI calls, not just fetch:

import * as Users from '@scriptrunnerhq/jira-client'

// Run as the ScriptRunner app
await Users.asApp(async () => {
  const response = await fetch('/rest/api/3/myself')
  return response.json()
})

// Run as a specific user
await Users.impersonateUser('557058:f58131cb-b67d-43c7-b30d-6b58d40bd077', async () => {
  const response = await fetch('/rest/api/3/myself')
  return response.json()
})

Calls made outside a wrapper follow the item's Run as setting. Some operations (for example, reading a user's email regardless of profile visibility) require app context.

Search documentation

Start typing to search the docs.