Skip to content

lujax-dev/github-sdk

Repository files navigation

@lujax/github-sdk

npm version CI

A TypeScript-first, ergonomic wrapper around the GitHub REST API. Calls api.github.com directly using native fetch — no Octokit, no third-party HTTP library.

  • Clean TypeScript domain types — camelCase interfaces instead of raw snake_case API responses
  • Typed errorsGithubApiError, InvalidTokenError, MissingConfigError instead of guessing at response shapes
  • Service-based APIgithub.pullRequests.list() instead of hand-building fetch calls
  • Built-in rate limiting, retries, and ETag caching — no extra config required
  • PAT or GitHub App auth, swappable behind one interface
  • Near-zero dependenciesjose for GitHub App JWT signing is the only runtime dependency

Installation

npm install @lujax/github-sdk

Requires Node.js 18 or later (uses native fetch).

Quick start

import { GithubClient } from "@lujax/github-sdk";

const github = new GithubClient({
    token: process.env.GITHUB_TOKEN,
    owner: "lujax-dev",
    repo: "github-sdk",
});

const pullRequests = await github.pullRequests.list();
const repo = await github.repositories.get();

owner, repo, and org are all optional on GithubClientConfig — each service asserts the config it actually needs at construction time, throwing MissingConfigError immediately if something's missing rather than failing later on a request. A GithubClient instance is scoped to one owner/repo pair; to work with multiple repositories, create multiple clients.

Authentication

Personal Access Token (PAT)

The simplest option — pass a token string directly:

const github = new GithubClient({
    token: process.env.GITHUB_TOKEN,
    owner: "lujax-dev",
    repo: "github-sdk",
});

GitHub App

For production use as a GitHub App, pass a GitHubAppAuth instance via auth instead of token. It signs a JWT from your App's private key, exchanges it for an installation access token, and refreshes it automatically as it nears expiry — no extra code required:

import { GithubClient, GitHubAppAuth } from "@lujax/github-sdk";

const github = new GithubClient({
    auth: new GitHubAppAuth({
        appId: process.env.GITHUB_APP_ID,
        privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
        installationId: process.env.GITHUB_APP_INSTALLATION_ID,
    }),
    owner: "lujax-dev",
    repo: "github-sdk",
});

token and auth are both optional individually, but at least one is required — GithubClient throws InvalidTokenError otherwise. If both are set, auth takes precedence.

Both strategies implement the same GitHubAuth interface ({ getToken(): Promise<string> }), so you can switch between them — e.g. PAT locally, GitHub App in production — without changing any other code. PatAuth (also exported) wraps a token identically to passing token directly; use auth: new PatAuth(token) if you'd rather keep every strategy behind the same auth field.

Services

Each service is scoped to the owner/repo/org set on the client and exposed as a property:

Service Access Requires What it covers
Pull Requests github.pullRequests owner, repo list/get/create/update/merge, commits, files, reviews, cycleTime(), isMerged()
Repositories github.repositories owner, repo (some methods need org instead) get/update/delete, topics, tags, languages, contributors, security settings, org-scoped listing
Issues github.issues owner, repo list/get/create/update, comments
Releases github.releases owner, repo list/get/getLatest/getByTag/create/update/delete
Workflows github.workflows owner, repo list/get workflows, list/get/cancel/re-run runs, trigger dispatch
Organizations github.organizations org get/update, listings, membership management
Users github.users authenticated user, lookup by username/ID, list

There's no standalone commits service — list commits on a PR via github.pullRequests.listCommits(pullNumber).

Every public method has JSDoc with a runnable example — full signatures are in the source, e.g. PullRequestService.ts, RepositoryService.ts. For complete standalone scripts, see /examples.

Error handling

All non-2xx API responses throw typed errors instead of returning a raw error shape:

import {
    GithubApiError,
    InvalidTokenError,
    MissingConfigError,
} from "@lujax/github-sdk";

try {
    await github.pullRequests.get(9999);
} catch (err) {
    if (err instanceof GithubApiError) {
        console.error(err.status, err.message, err.documentationUrl);
    }
}
  • GithubSdkError — base class for every error the SDK throws
  • GithubApiError — thrown on any non-ok GitHub API response; carries .status, .documentationUrl, and .details
  • InvalidTokenError — thrown when a GithubClient is constructed without token or auth
  • MissingConfigError — thrown when a service is constructed without the owner/repo/org it needs

HTTP behaviour

Built into every request, with no configuration needed:

  • Rate limiting — proactively waits out the reset window when x-ratelimit-remaining hits 0, instead of letting requests fail with a 403
  • Retries — 3 attempts with 1s/2s/4s backoff on 5xx and 429 responses
  • ETag caching — repeat GETs send If-None-Match; a 304 returns the cached body for free and doesn't count against your rate limit

Contributing

See CONTRIBUTING.md for setup, the module pattern, and the PR process. This project follows the Contributor Covenant Code of Conduct.

License

MIT

About

TypeScript-first GitHub REST API SDK - ergonomic, typed, near-zero dependencies

Topics

Resources

License

Code of conduct

Contributing

Stars

3 stars

Watchers

2 watching

Forks

Sponsor this project

 

Contributors