@keeldotrun/client
v0.1.0
Published
TypeScript client library for the Keel.run platform API
Downloads
13
Readme
@keeldotrun/client
TypeScript client library for the Keel.run platform API.
Development (monorepo)
This package lives in the keel.run monorepo alongside the SvelteKit frontend.
The root package.json declares both as npm workspaces:
keel.run/
├── package.json # workspaces: ["client", "frontend"]
├── client/ # @keeldotrun/client ← you are here
└── frontend/ # SvelteKit app (imports @keeldotrun/client)From the repo root, a single npm install links everything together.
The frontend references the client locally via workspace protocol:
// frontend/package.json
{
"dependencies": {
"@keeldotrun/client": "workspace:*"
}
}No publish step needed during development — Vite resolves the workspace symlink automatically. Import it like any other package:
import { createClient } from "@keeldotrun/client";Install (external consumers)
The package is also published to Codeberg's npm registry for use outside
the monorepo. Add an .npmrc line (or configure your registry globally),
then install:
# One-time project setup — tells npm where @keeldotrun packages live
echo "@keeldotrun:registry=https://codeberg.org/api/packages/Keeldotrun/npm/" >> .npmrc
npm install @keeldotrun/clientOr globally:
npm config set @keeldotrun:registry https://codeberg.org/api/packages/Keeldotrun/npm/Publishing
First, authenticate with Codeberg (one-time):
# Interactive — prompts for username / password / email
npm login --registry=https://codeberg.org/api/packages/Keeldotrun/npm/
# Username: your Codeberg username
# Password: a Codeberg access token (not your account password)
# Email: your Codeberg emailGenerate a token at https://codeberg.org/user/settings/applications
(scopes: read:user and packages).
For CI, skip npm login and create client/.npmrc instead:
//codeberg.org/api/packages/Keeldotrun/npm/:_authToken=<token>This file is git-ignored so your token stays local.
Then publish:
cd client
npm run build
npm publishThe publishConfig.registry in package.json already points at Codeberg —
no --registry flag needed.
Quick start
import { createClient } from "@keeldotrun/client";
const keel = createClient({
baseUrl: "https://api.keel.run",
getToken: () => localStorage.getItem("access_token"),
});
// Every method returns a Result<T> — check .error first.
const result = await keel.databases.list({ page: 1, perPage: 20 });
if (result.error) {
// result.errorValue is a typed KeelError — inspect .code
console.error(result.errorValue.code, result.errorValue.message);
return;
}
// result.data is fully typed
for (const db of result.data.databases) {
console.log(db.name, db.status);
}Error handling
Every API call returns Result<T> — a discriminated union:
type Result<T> =
| { data: T; error: false; errorValue: null }
| { data: null; error: true; errorValue: KeelError };This forces callers to handle errors before accessing data, just like Go's
val, err := fn() pattern. TypeScript narrows the type when you check
result.error.
const result = await keel.databases.get("db_123");
if (result.error) {
switch (result.errorValue.code) {
case "not_found": /* 404 */ break;
case "unauthenticated": /* 401 */ break;
case "permission_denied": /* 403 */ break;
case "rate_limited": /* 429 */ break;
default: /* ... */ break;
}
return;
}
// TypeScript knows result.data is Database here
console.log(result.data.engine);Error codes
| Code | HTTP | Meaning |
|---|---|---|
| invalid_argument | 400 | Validation failed (check .fields) |
| unauthenticated | 401 | Missing or invalid credentials |
| permission_denied | 403 | Authenticated but not authorized |
| not_found | 404 | Resource doesn't exist |
| already_exists | 409 | Unique constraint violation |
| rate_limited | 429 | Too many requests |
| internal_error | 500 | Server bug |
| unavailable | 502/503 | Downstream service unreachable |
| deadline_exceeded | 504 | Request timed out |
API reference
Auth
await keel.auth.register({ email, password, name });
await keel.auth.login({ email, password });
await keel.auth.refresh({ refreshToken });
await keel.auth.logout({ refreshToken });
await keel.auth.forgotPassword({ email });
await keel.auth.resetPassword({ token, newPassword });
await keel.auth.changePassword({ currentPassword, newPassword });
await keel.auth.verifyEmail({ token });
await keel.auth.resendVerification();
await keel.auth.verify2FA({ tempToken, code });Users
await keel.users.me();
await keel.users.get(id);
await keel.users.list({ page, perPage });
await keel.users.update(id, { name });
await keel.users.delete(id);Teams
await keel.teams.create({ name });
await keel.teams.get(id);
await keel.teams.list({ page, perPage });
await keel.teams.addMember(teamId, { userId, role });
await keel.teams.removeMember(teamId, userId);Tokens
await keel.tokens.create({ name });
await keel.tokens.list({ page, perPage });
await keel.tokens.revoke(id);
await keel.tokens.validate(token);Databases
await keel.databases.create({ name, plan: "micro", region: "us-east" });
await keel.databases.get(id);
await keel.databases.list({ page, perPage });
await keel.databases.delete(id);
await keel.databases.resetPassword(id);Billing
await keel.billing.listInvoices({ page, perPage });
await keel.billing.getInvoice(id);
await keel.billing.getUsage();
await keel.billing.listPaymentMethods();
await keel.billing.addPaymentMethod({ ... });
await keel.billing.deletePaymentMethod(id);Use with SvelteKit
// src/lib/keel.ts
import { createClient } from "@keeldotrun/client";
import { browser } from "$app/environment";
export const keel = createClient({
baseUrl: import.meta.env.VITE_API_URL ?? "http://localhost:8080",
getToken: () => {
if (!browser) return null;
return localStorage.getItem("access_token");
},
});Then in your load functions or components:
import { keel } from "$lib/keel";
export async function load() {
const result = await keel.databases.list();
if (result.error) {
return { databases: [], error: result.errorValue.message };
}
return { databases: result.data.databases };
}Advanced: raw HTTP
If you need to call an endpoint not yet covered by the service modules, use the raw HTTP helpers:
import { get, post, patch, del } from "@keeldotrun/client";
const result = await get<MyCustomType>(config, "/v1/custom/endpoint");Testing
The service interfaces are exported as types — mock them in your tests:
import type { DatabasesService } from "@keeldotrun/client";
import { ok } from "@keeldotrun/client";
const mockDb: DatabasesService = {
list: () => Promise.resolve(ok({ databases: [], pagination: { page: 1, perPage: 20, total: 0 } })),
// ... etc
};License
MIT
