jira-api-client v1.0.3
Jira API Client
A TypeScript client for interacting with the Jira API. This package provides a simple and type-safe way to work with Jira's REST API, including authentication, CRUD operations on issues, project management, and user operations.
Features
- 🔒 Authentication - Support for both Bearer token and Basic authentication
- 📝 Issue Management - Create, read, update, and delete Jira issues
- 🔄 Transitions - Move issues through workflows
- 💬 Comments - Manage issue comments
- 📊 Projects - Access project details and metadata
- 👥 Users - Search and manage users
- 📄 Pagination - Handle paginated responses with ease
- ⚠️ Error Handling - Detailed error information
- 📦 TypeScript - Full type definitions for all API responses
Installation
npm install jira-api-client
Authentication
This client supports two authentication methods for Jira:
Bearer Token Authentication (Recommended)
Bearer token authentication is the recommended approach for security reasons. It uses a personal access token that you can generate from your Atlassian account.
To generate a token: 1. Log in to https://id.atlassian.com/manage-profile/security/api-tokens 2. Click "Create API token" 3. Give your token a name and click "Create" 4. Copy the token value (you won't be able to see it again)
Basic Authentication
Basic authentication uses your email and API token (as password). This method is supported for backward compatibility but is less secure than Bearer token authentication.
Note: Never use your actual Atlassian account password. Always use an API token as the password.
Configuration
You can configure the client using environment variables or by passing a configuration object directly.
Using Environment Variables
Create a .env
file in your project root:
# Required
JIRA_BASE_URL=https://your-domain.atlassian.net
JIRA_TOKEN=your-bearer-token
# For Basic authentication (optional)
# JIRA_EMAIL=your-email@example.com
# Optional Configuration
JIRA_API_VERSION=3
JIRA_REQUEST_TIMEOUT=30000
Then initialize the client:
import { JiraClient } from 'jira-api-client';
const jira = JiraClient.fromEnv();
Using Configuration Object
import { JiraClient } from 'jira-api-client';
// Bearer token authentication (recommended)
const jira = new JiraClient({
baseUrl: 'https://your-domain.atlassian.net',
token: 'your-bearer-token',
apiVersion: 3, // optional, defaults to 3
timeout: 30000, // optional, defaults to 30000 (30 seconds)
});
// OR Basic authentication with email/password
const jiraBasic = new JiraClient({
baseUrl: 'https://your-domain.atlassian.net',
email: 'your-email@example.com',
token: 'your-api-token', // In this case, token is used as password
apiVersion: 3, // optional, defaults to 3
timeout: 30000, // optional, defaults to 30000 (30 seconds)
});
The client automatically detects which authentication method to use based on whether you provide an email:
- If
email
is provided, Basic authentication is used - If only
token
is provided, Bearer token authentication is used
Security Considerations
Token Storage: Never hardcode your authentication tokens in your source code. Use environment variables or a secure secrets management system.
Token Permissions: When creating API tokens, follow the principle of least privilege. Only grant the permissions that are necessary for your application to function.
Token Rotation: Regularly rotate your API tokens, especially in production environments.
HTTPS: Always use HTTPS when communicating with the Jira API to ensure your authentication credentials are encrypted during transmission.
Usage Examples
Working with Issues
import { JiraClient, CreateIssueData } from 'jira-api-client';
const jira = JiraClient.fromEnv();
// Get an issue
const issue = await jira.issues.getIssue('PROJECT-123');
console.log(issue.fields.summary);
// Create an issue
const newIssueData: CreateIssueData = {
fields: {
project: { key: 'PROJECT' },
summary: 'New issue created via API',
description: 'This is a description of the issue',
issuetype: { name: 'Task' },
},
};
const newIssue = await jira.issues.createIssue(newIssueData);
console.log(`Created issue: ${newIssue.key}`);
// Update an issue
await jira.issues.updateIssue('PROJECT-123', {
fields: {
summary: 'Updated summary',
description: 'Updated description',
},
});
// Delete an issue
await jira.issues.deleteIssue('PROJECT-123');
// Search for issues using JQL
const searchResults = await jira.issues.searchIssues(
'project = PROJECT AND status = "In Progress"'
);
console.log(`Found ${searchResults.total} issues`);
Working with Comments
// Get comments for an issue
const comments = await jira.issues.getComments('PROJECT-123');
console.log(`Issue has ${comments.total} comments`);
// Add a comment
const comment = await jira.issues.addComment('PROJECT-123', 'This is a new comment');
console.log(`Added comment with ID: ${comment.id}`);
// Update a comment
await jira.issues.updateComment('PROJECT-123', comment.id, 'Updated comment text');
// Delete a comment
await jira.issues.deleteComment('PROJECT-123', comment.id);
Working with Transitions
// Get available transitions
const transitions = await jira.issues.getTransitions('PROJECT-123');
console.log('Available transitions:', transitions.map(t => t.name));
// Transition an issue
await jira.issues.transitionIssue('PROJECT-123', 'Done');
Working with Projects
// Get all projects
const projects = await jira.projects.getAllProjects();
console.log(`Found ${projects.values.length} projects`);
// Get a specific project
const project = await jira.projects.getProject('PROJECT');
console.log(`Project name: ${project.name}`);
// Get project issue types
const issueTypes = await jira.projects.getProjectIssueTypes('PROJECT');
console.log('Issue types:', issueTypes.map(t => t.name));
// Search for projects
const searchResults = await jira.projects.searchProjects('Marketing');
console.log(`Found ${searchResults.values.length} matching projects`);
Working with Users
// Get current user
const currentUser = await jira.users.getCurrentUser();
console.log(`Current user: ${currentUser.displayName}`);
// Search for users
const users = await jira.users.searchUsers('john');
console.log(`Found ${users.values.length} matching users`);
// Get assignable users for a project
const assignableUsers = await jira.users.getAssignableUsers('PROJECT');
console.log(`Found ${assignableUsers.values.length} assignable users`);
Handling Pagination
import { fetchAllPages } from 'jira-api-client';
// Fetch all issues across multiple pages
const allIssues = await fetchAllPages(
params => jira.issues.searchIssues('project = PROJECT', undefined, params)
);
console.log(`Fetched all ${allIssues.length} issues`);
// Or manually handle pagination
let startAt = 0;
const maxResults = 50;
let isLast = false;
while (!isLast) {
const response = await jira.issues.searchIssues(
'project = PROJECT',
undefined,
{ startAt, maxResults }
);
// Process the current page of results
response.values.forEach(issue => {
console.log(issue.key, issue.fields.summary);
});
isLast = response.isLast;
startAt += maxResults;
}
Error Handling
import { isJiraError } from 'jira-api-client';
try {
await jira.issues.getIssue('NONEXISTENT-123');
} catch (error) {
if (isJiraError(error)) {
console.error(`Jira API Error (${error.status}):`, error.message);
console.error('Error details:', error.errors);
console.error('Error messages:', error.errorMessages);
} else {
console.error('Unknown error:', error);
}
}
API Documentation
JiraClient
The main client class that provides access to all API endpoints.
class JiraClient {
readonly issues: IssuesApiClient;
readonly projects: ProjectsApiClient;
readonly users: UsersApiClient;
constructor(config: JiraClientConfig);
static fromEnv(): JiraClient;
}
IssuesApiClient
class IssuesApiClient {
getIssue(issueIdOrKey: string, fields?: string[]): Promise<JiraIssue>;
createIssue(data: CreateIssueData): Promise<JiraIssue>;
updateIssue(issueIdOrKey: string, data: UpdateIssueData): Promise<void>;
deleteIssue(issueIdOrKey: string, deleteSubtasks?: boolean): Promise<void>;
searchIssues(jql: string, fields?: string[], pagination?: PaginationParams): Promise<PaginatedResponse<JiraIssue>>;
getTransitions(issueIdOrKey: string): Promise<JiraTransition[]>;
transitionIssue(issueIdOrKey: string, transitionIdOrName: string): Promise<void>;
getComments(issueIdOrKey: string, pagination?: PaginationParams): Promise<PaginatedResponse<JiraComment>>;
addComment(issueIdOrKey: string, body: string): Promise<JiraComment>;
updateComment(issueIdOrKey: string, commentId: string, body: string): Promise<JiraComment>;
deleteComment(issueIdOrKey: string, commentId: string): Promise<void>;
assignIssue(issueIdOrKey: string, accountId: string | null): Promise<void>;
}
ProjectsApiClient
class ProjectsApiClient {
getAllProjects(pagination?: PaginationParams): Promise<PaginatedResponse<JiraProject>>;
getProject(projectIdOrKey: string): Promise<JiraProject>;
getProjectIssueTypes(projectIdOrKey: string): Promise<JiraIssueType[]>;
getProjectComponents(projectIdOrKey: string): Promise<any[]>;
getProjectVersions(projectIdOrKey: string): Promise<any[]>;
getProjectStatuses(projectIdOrKey: string): Promise<any[]>;
searchProjects(query: string, pagination?: PaginationParams): Promise<PaginatedResponse<JiraProject>>;
}
UsersApiClient
class UsersApiClient {
getCurrentUser(): Promise<JiraUser>;
getUser(accountId: string): Promise<JiraUser>;
searchUsers(query: string, pagination?: PaginationParams): Promise<PaginatedResponse<JiraUser>>;
getAssignableUsers(projectIdOrKey: string, pagination?: PaginationParams): Promise<PaginatedResponse<JiraUser>>;
}
Changelog
See the CHANGELOG.md file for details on all changes and releases.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Jira REST API Documentation
- Axios for HTTP requests
- TypeScript for type safety