jira.js

repository·master·Indexed 19 days ago

https://github.com/mrrefactoring/jira.js

A type-safe, Promise-based JavaScript and TypeScript client for Jira Cloud REST APIs, including the Jira Agile and Jira Service Management APIs. It features runtime response validation, support for Node.js and browser environments, and multiple authentication strategies including Basic (Email and API Token), Bearer tokens, and OAuth 2.0 (3LO) with automatic token refreshing and cloudId resolution. Version 6.0.0 is ESM-only.

Tokens
37.3K
Snippets
82
Records
139
Agent score
65%

What's inside jira.js

  1. Overview of jira.js

    master

    jira.js is a type-safe Jira REST API client designed for the Jira Cloud platform, including Agile (boards, sprints, backlog) and Jira Service Management APIs. It is built for Node.js (v22+) and browsers using ESM and is designed to be tree-shakable.

    Key capabilities include:

    • Full Cloud Coverage: Access to nearly every Jira Cloud endpoint.
    • TypeScript-first: Every endpoint, parameter, and model is fully typed.
    • Runtime Validation: Responses are validated against schemas to detect API drift.
    • Built-in OAuth 2.0: Supports Email + API token, bearer tokens, and 3LO (Three-Legged OAuth) with automatic token refresh and 401 retries.
    • Environment Agnostic: Runs in Node.js 22+ and modern browsers.
  2. What is the jira.js playground

    master

    The jira.js playground is a collection of self-contained mini-projects designed to demonstrate how to use jira.js with a live Jira instance. Each scenario is an independent npm project with its own package.json, tsconfig, and dependencies, allowing them to be tested or moved to other repositories without side effects.

    Key scenarios include:

    • oauth2/: Demonstrates the full OAuth 2.0 (3LO) auto-flow, including browser consent, token acquisition, automatic cloudId retrieval, GET /myself calls, and automatic token refreshing.
  3. Supported Jira API surfaces

    master

    The jira.js library provides coverage for three main Atlassian Jira Cloud API surfaces:

    1. Jira Cloud Platform API: Handles issues, projects, users, fields, workflows, and schemes.
    2. Jira Software (Agile) API: Handles sprints, boards, backlogs, and agile processes.
    3. Jira Service Management API: Handles requests, queues, customers, and organizations.
  4. Working with Rich Text and Atlassian Document Format

    master

    Fields like issue descriptions or comment bodies use the Atlassian Document Format (ADF).

    While reads always return the data as a structured document, you can still write content using wiki-markup strings. The library automatically routes these writes through Jira's v2 endpoint, which parses the markup server-side and returns the resulting document. This allows you to use familiar markup like *bold* or {code} without manually constructing ADF objects.

    // Wiki markup — still works, still formats
    await jira.issueComments.addComment({
      issueIdOrKey: 'TEST-1',
      body: 'h2. Heading\n\n*bold* and {code}inline{code}',
    });
  5. Handle rotating refresh tokens in OAuth 2.0

    master

    Atlassian uses rotating refresh tokens. This means every time a token is refreshed, Atlassian returns a new refreshToken and invalidates the old one.

    Critical Implementation Details

    • Persistence: You must use the onTokenRefresh callback in createCloudClient to persist the new refreshToken. If you fail to save the new token, the next attempt to refresh will use a dead token and fail.
    • Concurrency: jira.js implements 'single-flight' logic, collapsing concurrent refresh requests into a single network call to prevent multiple rotations from happening at once.
    • Expiry: Refresh tokens expire after 90 days of inactivity. They have a 10-minute reuse leeway to handle transient concurrency issues.
  6. Use error predicates instead of `instanceof`

    master

    To identify error types, use the provided predicate functions (e.g., isApiError, isNotFoundError, isRateLimitError) instead of the JavaScript instanceof operator.

    Predicates are more reliable in environments with code splitting, minification, or multiple package instances where instanceof might fail. Note that predicates are hierarchical: for example, a NotFoundError will also satisfy isApiError.

    import { isApiError } from 'jira.js';
    
    try {
      await jira.issues.createIssue({ fields });
    } catch (error) {
      if (isApiError(error)) {
        console.error(error.status, error.body);
      }
    }
  7. Handle Rich Text (Wiki Markup vs. ADF)

    master

    6.0 uses the Jira v3 specification. While you can still send wiki markup strings (the library will route them through a v2 endpoint internally), all reads return Atlassian Document Format (ADF) objects.

    Writing:

    • You can pass a string (wiki markup) or an ADF object.

    Reading:

    • You must walk the document tree to extract text. To avoid flattening headings into paragraphs, join the blocks rather than the leaves.
    // Writing wiki markup (still works)
    await jira.issueComments.addComment({
      issueIdOrKey: 'PROJ-1',
      body: 'h2. Heading\n\n*bold*',
    });
    
    // Reading text from a comment body (ADF)
    const text = comment.body.content
      ?.map(block => (block.content ?? []).map(node => node.text ?? '').join(''))
      .join('\n');
  8. Access API Endpoints

    master

    The API follows a client.<group>.<method> pattern. Different functional areas are grouped into specific namespaces such as projects, agile, or issues.

    // Get all projects
    const projects = await jira.projects.searchProjects();
    
    // Create a sprint (Agile surface)
    const sprint = await agile.sprint.createSprint({ name: 'Q4 Sprint' });
  9. How to use multiple Jira surfaces with a single client

    master

    The library provides specialized clients for different Jira surfaces: Jira Cloud platform, Jira Agile (boards, sprints), and Jira Service Management (requests, queues).

    Important for OAuth 2.0: If you are using OAuth 2.0, do not create multiple clients. Instead, create a single core client using createClient from jira.js/core, and then pass that core client into the specific surface factories (like createCloudClient or createAgileClient). This ensures that token rotation and refresh states are managed in one place, preventing one client from invalidating the other's refresh token.

    import { createClient } from 'jira.js/core';
    import { createAgileClient, createCloudClient } from 'jira.js';
    
    const client = createClient({ host, auth });
    
    const jira = createCloudClient(client);
    const agile = createAgileClient(client);
  10. Understand the API Structure

    master

    Endpoints are accessed using the pattern client.<group>.<method>.

    Example:

    • jira.projects.searchProjects() to find projects.
    • agile.sprint.createSprint(...) to create a sprint.
    // Get all projects
    const projects = await jira.projects.searchProjects();
    
    // Create a sprint
    const sprint = await agile.sprint.createSprint({ name: 'Q4 Sprint' });
  11. How to use multiple API surfaces with a single client

    master

    If you need to access multiple Jira API surfaces (e.g., Platform and Agile), you should create a single core client first and then pass it to the specific surface factories.

    Why this is important: This is critical for OAuth 2.0 users. Because Atlassian rotates the refresh token upon every update, having two separate clients would result in two separate token states. The client that updates first would invalidate the token held by the second client.

    import { createClient } from 'jira.js/core';
    import { createAgileClient, createCloudClient } from 'jira.js';
    
    const client = createClient({ host, auth });
    
    const jira = createCloudClient(client);
    const agile = createAgileClient(client);
  12. Handling Wiki-markup and Atlassian Document Format (ADF)

    master

    When writing to fields that support wiki-markup (like issue comments), you can send a plain string containing wiki-syntax. Jira will parse this on the server side.

    Important: When reading these fields back, the library will always return the data as an Atlassian Document Format (ADF) object, never as a raw string.

    // Sending Wiki-markup (works and gets formatted by Jira)
    await jira.issueComments.addComment({
      issueIdOrKey: 'PROJ-1',
      body: 'h2. Heading\n\n*bold* and {code}monospace{code}',
    });
    
    // Note: When reading this comment back, you will receive an ADF document, not a string.