bitbucket-datacenter-api-client
v1.11.0
Published
TypeScript client for Bitbucket Data Center REST API
Downloads
1,279
Maintainers
Readme
bitbucket-datacenter-api-client
TypeScript client for the Bitbucket Data Center REST API. Works in Node.js and the browser (isomorphic). Fully typed, zero runtime dependencies.
Installation
npm install bitbucket-datacenter-api-clientQuick start
import { BitbucketClient } from 'bitbucket-datacenter-api-client';
const bb = new BitbucketClient({
apiUrl: 'https://bitbucket.example.com',
apiPath: 'rest/api/latest',
user: 'your-username',
token: 'your-personal-access-token',
});API reference
Projects
// List all accessible projects
const projects = await bb.projects();
const projects = await bb.projects({ limit: 50, name: 'platform' });
// Get a single project
const project = await bb.project('PROJ');
// Webhooks on a project
const hooks = await bb.project('PROJ').webhooks();
const hooks = await bb.project('PROJ').webhooks({ event: 'pr:opened' });
// Users with access to a project
const members = await bb.project('PROJ').users();
const members = await bb.project('PROJ').users({ permission: 'PROJECT_WRITE' });Repositories
// List repositories in a project
const repos = await bb.project('PROJ').repos();
const repos = await bb.project('PROJ').repos({ limit: 25, name: 'api' });
// Get a single repository
const repo = await bb.project('PROJ').repo('my-repo');
// Repository size (bytes)
const size = await bb.project('PROJ').repo('my-repo').size();
// { repository: 1048576, attachments: 0 }
// Forks of the repository
const forks = await bb.project('PROJ').repo('my-repo').forks();
// Webhooks on a repository
const hooks = await bb.project('PROJ').repo('my-repo').webhooks();
const hooks = await bb.project('PROJ').repo('my-repo').webhooks({ event: 'repo:push' });
// Files last modified with the commit that touched each
const entries = await bb.project('PROJ').repo('my-repo').lastModified();
const entries = await bb.project('PROJ').repo('my-repo').lastModified({ at: 'main' });
// Raw file content
const content = await bb.project('PROJ').repo('my-repo').raw('src/index.ts');
const content = await bb.project('PROJ').repo('my-repo').raw('src/index.ts', { at: 'feature/my-branch' });
// Browse repository contents
const root = await bb.project('PROJ').repo('my-repo').browse();
const dir = await bb.project('PROJ').repo('my-repo').browse('src');
const dir = await bb.project('PROJ').repo('my-repo').browse('src', { at: 'main' });Branches
const branches = await bb.project('PROJ').repo('my-repo').branches();
const branches = await bb.project('PROJ').repo('my-repo').branches({
filterText: 'feature',
orderBy: 'MODIFICATION',
details: true,
});Tags
// List tags
const tags = await bb.project('PROJ').repo('my-repo').tags();
const tags = await bb.project('PROJ').repo('my-repo').tags({ filterText: 'v1', orderBy: 'ALPHABETICAL' });
// Tags associated with specific commits (POST)
const tags = await bb.project('PROJ').repo('my-repo').tagsByCommits(['abc123', 'def456']);
// Override the API path for this call only
const tags = await bb.project('PROJ').repo('my-repo').tagsByCommits(['abc123'], { apiPath: 'rest/api/1.0' });Commits
// List commits in a repository
const commits = await bb.project('PROJ').repo('my-repo').commits();
const commits = await bb.project('PROJ').repo('my-repo').commits({
limit: 25,
until: 'main',
path: 'src/index.ts',
followRenames: true,
});
// Get a single commit
const commit = await bb.project('PROJ').repo('my-repo').commit('abc123');
// File changes introduced by a commit
const changes = await bb.project('PROJ').repo('my-repo').commit('abc123').changes();
const changes = await bb.project('PROJ').repo('my-repo').commit('abc123').changes({ since: 'def456' });
// Full diff for a commit
const diff = await bb.project('PROJ').repo('my-repo').commit('abc123').diff();
const diff = await bb.project('PROJ').repo('my-repo').commit('abc123').diff({ contextLines: 5 });
// Diff scoped to a single file (srcPath is appended as a URL path segment)
const diff = await bb.project('PROJ').repo('my-repo').commit('abc123').diff({ srcPath: 'src/index.ts' });Pull requests
// List pull requests
const pullRequests = await bb.project('PROJ').repo('my-repo').pullRequests();
const pullRequests = await bb.project('PROJ').repo('my-repo').pullRequests({
state: 'OPEN',
order: 'NEWEST',
limit: 10,
});
// Get a single pull request
const pr = await bb.project('PROJ').repo('my-repo').pullRequest(42);
// Pull request sub-resources
const activities = await bb.project('PROJ').repo('my-repo').pullRequest(42).activities();
const tasks = await bb.project('PROJ').repo('my-repo').pullRequest(42).tasks();
const commits = await bb.project('PROJ').repo('my-repo').pullRequest(42).commits();
const changes = await bb.project('PROJ').repo('my-repo').pullRequest(42).changes();
const reports = await bb.project('PROJ').repo('my-repo').pullRequest(42).reports();
const summaries = await bb.project('PROJ').repo('my-repo').pullRequest(42).buildSummaries();
const issues = await bb.project('PROJ').repo('my-repo').pullRequest(42).issues();Repository search
// Search repositories across all projects
const repos = await bb.search();
const repos = await bb.search({ name: 'api' }); // matches any repo containing 'api'
const repos = await bb.search({ name: 'api', projectkey: 'PROJ' });
const repos = await bb.search({ projectname: 'platform', visibility: 'private', limit: 25 });
const repos = await bb.search({ state: 'AVAILABLE', permission: 'REPO_WRITE' });Users
// List all users
const users = await bb.users();
const users = await bb.users({ filter: 'john', limit: 20 });
// Get a single user
const user = await bb.user('pilmee');
// List repositories belonging to a user
const repos = await bb.user('pilmee').repos();
const repos = await bb.user('pilmee').repos({ name: 'api' });
// Navigate into a user repository — all repo sub-resources are available
const repo = await bb.user('pilmee').repo('my-repo');
const content = await bb.user('pilmee').repo('my-repo').raw('src/index.ts');
const commits = await bb.user('pilmee').repo('my-repo').commits();
const prs = await bb.user('pilmee').repo('my-repo').pullRequests();Pagination
Every list method returns a PagedResponse<T> with the full Bitbucket pagination envelope:
const page = await bb.project('PROJ').repos({ limit: 25 });
page.values // BitbucketRepository[] — the items
page.isLastPage // boolean
page.nextPageStart // number | undefined — pass as `start` to get the next page
page.size // number — items in this page
page.limit // number
page.start // numberRequest events
Subscribe to every HTTP request made by the client to get timing and error information. Useful for logging, monitoring, or debugging.
bb.on('request', (event) => {
console.log(`[${event.method}] ${event.url} → ${event.statusCode} (${event.durationMs}ms)`);
if (event.error) {
console.error('Request failed:', event.error.message);
}
});The event object contains:
| Field | Type | Description |
|---|---|---|
| url | string | Full URL that was requested |
| method | 'GET' \| 'POST' | HTTP method used |
| startedAt | Date | When the request started |
| finishedAt | Date | When the request finished |
| durationMs | number | Duration in milliseconds |
| statusCode | number \| undefined | HTTP status code, if a response was received |
| error | Error \| undefined | Present only if the request failed |
The event is always emitted after the request completes, whether it succeeded or failed. Multiple listeners can be registered.
Error handling
Non-2xx responses throw a BitbucketApiError with the HTTP status code and status text:
import { BitbucketApiError } from 'bitbucket-datacenter-api-client';
try {
await bb.project('NONEXISTENT');
} catch (err) {
if (err instanceof BitbucketApiError) {
console.log(err.status); // 404
console.log(err.statusText); // 'Not Found'
console.log(err.message); // 'Bitbucket API error: 404 Not Found'
console.log(err.stack); // full stack trace
}
}Chainable resource pattern
Every resource that maps to a single entity implements PromiseLike, so you can await it directly or chain methods to access sub-resources:
// Await directly → fetches the project
const project = await bb.project('PROJ');
// Chain → fetches the list
const repos = await bb.project('PROJ').repos({ limit: 10 });
// Deep chain
const activities = await bb.project('PROJ').repo('my-repo').pullRequest(42).activities();
const diff = await bb.project('PROJ').repo('my-repo').commit('abc123').diff();Authentication
The client uses HTTP Basic Authentication with a Personal Access Token (PAT). Generate one in Bitbucket under Profile → Manage account → Personal access tokens.
const bb = new BitbucketClient({
apiUrl: 'https://bitbucket.example.com',
apiPath: 'rest/api/latest',
user: 'your-username',
token: 'your-personal-access-token',
});TypeScript types
All domain types are exported:
import type {
// Core
PagedResponse, PaginationParams,
BitbucketApiError,
RequestEvent, BitbucketClientEvents,
// Projects
BitbucketProject, ProjectsParams,
// Repositories
BitbucketRepository, ReposParams, SearchReposParams,
BitbucketRepositorySize,
BitbucketLastModifiedEntry, LastModifiedParams,
RawFileParams,
BitbucketBrowseResponse, BitbucketBrowseChild, BitbucketBrowsePath, BrowseParams,
// Branches & Tags
BitbucketBranch, BranchesParams,
BitbucketTag, TagsParams,
// Commits
BitbucketCommit, CommitsParams,
BitbucketDiff, BitbucketDiffEntry, DiffParams, CommitChangesParams,
// Pull Requests
BitbucketPullRequest, PullRequestsParams,
BitbucketPullRequestActivity, ActivitiesParams,
BitbucketPullRequestTask, TasksParams,
BitbucketChange, ChangesParams,
BitbucketReport, ReportsParams,
BitbucketBuildSummaries,
BitbucketIssue,
// Users
BitbucketUser, UsersParams,
BitbucketUserPermission, ProjectUsersParams,
// Webhooks
BitbucketWebhook, BitbucketWebhookStatistics, BitbucketWebhookDelivery,
WebhooksParams, WebhookEvent, WebhookScopeType,
} from 'bitbucket-datacenter-api-client';Documentation
Full API documentation is published at: https://eljijuna.github.io/BitbucketDataCenterApiClient
Contributing
See CONTRIBUTING.md.
