This guide shows how to add @reeveskeefe/rekurn-sdk to a website, API route, server job, or integration. It is separate from the CLI command guide because the SDK is for application code, not terminal workflows.
Current package version for the next npm publish:
npm install @reeveskeefe/rekurn-sdk@0.2.8Package facts:
- ESM-only.
- Dependency-free at runtime.
- Includes TypeScript declarations.
- Uses
fetch. - Requires HTTPS by default.
- Supports request timeouts, retries, 429 backoff, and sanitized API errors.
You need:
- A Rekurn API site, such as
https://your-site.com. - A bearer token for that site.
- A repository owner ID or username.
- A repository name.
Do not expose write-capable Rekurn tokens in browser code. Use the SDK in server-side code for authenticated operations.
npm install @reeveskeefe/rekurn-sdk@0.2.8Other package managers:
pnpm add @reeveskeefe/rekurn-sdk@0.2.8
yarn add @reeveskeefe/rekurn-sdk@0.2.8For a server-rendered site or API route:
REKURN_BASE_URL=https://your-site.com
REKURN_TOKEN=your-token
REKURN_OWNER=your-username-or-owner-id
REKURN_REPO=my-repoKeep .env and .env.* ignored. Rekurn’s default .rekurnignore already ignores them for new files.
import { RekurnClient } from '@reeveskeefe/rekurn-sdk'
export const rekurn = new RekurnClient({
baseUrl: process.env.REKURN_BASE_URL!,
token: process.env.REKURN_TOKEN,
timeoutMs: 30_000,
retries: 2,
})baseUrl is the origin of the site running Rekurn. Do not include /api/v1; the SDK adds that path.
Correct:
new RekurnClient({ baseUrl: 'https://your-site.com' })Incorrect:
new RekurnClient({ baseUrl: 'https://your-site.com/api/v1' })Use the SDK in a Server Component or Route Handler.
// app/api/rekurn/commits/route.ts
import { NextResponse } from 'next/server'
import { RekurnClient } from '@reeveskeefe/rekurn-sdk'
const rekurn = new RekurnClient({
baseUrl: process.env.REKURN_BASE_URL!,
token: process.env.REKURN_TOKEN,
})
export async function GET() {
const owner = process.env.REKURN_OWNER!
const repo = process.env.REKURN_REPO!
const { commits } = await rekurn.commits.list(owner, repo, { n: 10 })
return NextResponse.json({ commits })
}Call it from the browser through your own route:
const res = await fetch('/api/rekurn/commits')
const data = await res.json()This keeps the token on the server.
import express from 'express'
import { RekurnClient } from '@reeveskeefe/rekurn-sdk'
const app = express()
const rekurn = new RekurnClient({
baseUrl: process.env.REKURN_BASE_URL!,
token: process.env.REKURN_TOKEN,
})
app.get('/rekurn/refs', async (_req, res, next) => {
try {
const refs = await rekurn.refs.list(
process.env.REKURN_OWNER!,
process.env.REKURN_REPO!,
)
res.json(refs)
} catch (err) {
next(err)
}
})The SDK can run anywhere fetch exists, but browser usage should be read-only unless your app issues short-lived scoped tokens.
Safer browser pattern:
// Browser calls your API route.
const refs = await fetch('/api/rekurn/refs').then((res) => res.json())Avoid:
new RekurnClient({
baseUrl: 'https://your-site.com',
token: 'long-lived-write-token',
})The SDK rejects plain HTTP by default. For a local Rekurn API:
const rekurn = new RekurnClient({
baseUrl: 'http://localhost:3000',
token: process.env.REKURN_TOKEN,
allowInsecureHttp: true,
})Only localhost and 127.0.0.1 are allowed with allowInsecureHttp.
Pass a bearer token when constructing the client:
const rekurn = new RekurnClient({
baseUrl: process.env.REKURN_BASE_URL!,
token: process.env.REKURN_TOKEN,
})Or set it later:
rekurn.setToken(newToken)Use setToken when your app refreshes sessions or receives a token from a secure server-side auth flow.
The SDK throws RekurnApiError for API responses that are not successful.
import { RekurnApiError } from '@reeveskeefe/rekurn-sdk'
try {
const repo = await rekurn.repos.get(owner, repoName)
console.log(repo)
} catch (err) {
if (err instanceof RekurnApiError) {
console.error(err.status, err.code, err.message)
} else {
console.error(err)
}
}The SDK retries transient responses:
4084295xx
For 429, it honors Retry-After when present.
const repos = await rekurn.repos.list()const repo = await rekurn.repos.create({
name: 'my-site',
description: 'Website releases',
visibility: 'private',
defaultBranch: 'main',
})const { refs } = await rekurn.refs.list(owner, repo)
const main = refs.find((ref) => ref.name === 'heads/main')Use expectedHash when you want compare-and-swap behavior.
await rekurn.refs.update(owner, repo, 'heads/main', {
commitHash: newCommitHash,
expectedHash: previousCommitHash,
})await rekurn.refs.delete(owner, repo, 'heads/old-branch')const { commits } = await rekurn.commits.list(owner, repo, { n: 20 })const commit = await rekurn.commits.get(owner, repo, commitHash)Commit objects may include deletedPaths when deletion tombstones are available:
for (const path of commit.deletedPaths ?? []) {
console.log('deleted', path)
}Use this before upload to avoid sending objects the remote already has.
const { missing } = await rekurn.objects.want(owner, repo, [
commitHash,
treeHash,
blobHash,
])Object data must be base64-encoded serialized Rekurn object bytes.
await rekurn.objects.upload(owner, repo, hash, base64Data)await rekurn.objects.uploadBatch(owner, repo, [
{ hash: commitHash, data: commitBase64 },
{ hash: treeHash, data: treeBase64 },
])const object = await rekurn.objects.download(owner, repo, hash)
const bytes = Buffer.from(object.data, 'base64')const batch = await rekurn.objects.downloadBatch(owner, repo, hashes)
for (const object of batch.objects) {
const bytes = Buffer.from(object.data, 'base64')
}
for (const missingHash of batch.missing) {
console.warn('missing', missingHash)
}const stream = await rekurn.objects.downloadStream(owner, repo, hash)
if (stream) {
const reader = stream.getReader()
// Read chunks from reader.
}await rekurn.deploy.setHooks(owner, repo, {
production: 'https://example.com/deploy/prod',
preview: 'https://example.com/deploy/preview',
})
const hooks = await rekurn.deploy.getHooks(owner, repo)await rekurn.deploy.record(owner, repo, {
commitHash,
env: 'production',
status: 'ready',
externalDeploymentId: 'vercel-deployment-id',
externalUrl: 'https://my-site.vercel.app',
notes: 'Deployed from CI',
})const deployments = await rekurn.deploy.list(owner, repo)const events = await rekurn.audit.list(owner, repo)rekurn.repos.list()
rekurn.repos.create({ name, description?, visibility?, defaultBranch? })
rekurn.repos.get(ownerId, name)
rekurn.repos.delete(ownerId, name)
rekurn.refs.list(ownerId, repo)
rekurn.refs.update(ownerId, repo, refName, { commitHash, expectedHash?, isImmutable? })
rekurn.refs.delete(ownerId, repo, refName)
rekurn.commits.list(ownerId, repo, { n? })
rekurn.commits.get(ownerId, repo, hash)
rekurn.objects.want(ownerId, repo, hashes)
rekurn.objects.upload(ownerId, repo, hash, data)
rekurn.objects.uploadBatch(ownerId, repo, objects)
rekurn.objects.download(ownerId, repo, hash)
rekurn.objects.downloadBatch(ownerId, repo, hashes)
rekurn.objects.downloadStream(ownerId, repo, hash)
rekurn.deploy.getHooks(ownerId, repo)
rekurn.deploy.setHooks(ownerId, repo, deployHooks)
rekurn.deploy.list(ownerId, repo)
rekurn.deploy.record(ownerId, repo, input)
rekurn.audit.list(ownerId, repo)
rekurn.setToken(token)
rekurn.raw(method, path, body?)
rekurn.request<T>(method, path, body?)Important exported types:
import type {
RekurnClientOptions,
CommitObject,
Ref,
RepoSummary,
CreateRepoInput,
CommitListOptions,
UpdateRefInput,
ObjectDownload,
ObjectBatchItem,
ObjectBatchDownload,
DeployHooksResponse,
CreateDeploymentInput,
DeploymentRecord,
AuditEvent,
} from '@reeveskeefe/rekurn-sdk'Before shipping SDK usage in your site:
- Keep
REKURN_TOKENserver-side. - Use HTTPS
baseUrl. - Set a timeout that fits your route runtime.
- Catch
RekurnApiError. - Use
expectedHashwhen updating refs. - Batch object downloads and uploads when transferring many objects.
- Do not commit
.envfiles.
- CLI commands:
docs/cli-commands.md - Deployment hooks:
docs/deployment-hooks.md - Push security:
docs/push-security.md - Security practices:
docs/security.md