CLI
A Command Line Interface is shipped with the Galbe package. You can use it to perform useful tasks around your application.
After installing Galbe, the CLI is available locally to your project.
To use it directly from your terminal, either install it globally:
$ bun i -g galbe
Or run it through bunx:
$ bunx galbe
dev
Start a dev server running your Galbe application.
Arguments
| Name | Description |
|---|---|
| index | The js or ts file that exports your Galbe server instance. |
Options
| Short | Long | Description | Default |
|---|---|---|---|
| -p | --port | port number [1-65535] | 3000 |
| -w | --watch [dir] | watch file changes (defaults to index dir) | false |
| -wi | --watchignore | ignored watch files regex | |
| -nc | --noclear | don't clear on file changes | false |
Example
index.js
import { Galbe } from 'galbe'
const g = new Galbe()
g.get('example', () => '')
export default g $ galbe dev index.js -p 7357 -w
🏗️ Constructing routes
[GET] /example
done🚀 Server running at http://localhost:7357
build
Bundle your Galbe application.
Arguments
| Name | Description |
|---|---|
| index | The js or ts file that exports your Galbe server instance. |
Options
| Short | Long | Description | Default |
|---|---|---|---|
| -o | --out | output directory | dist/app |
| -C | --compile | create a standalone executable | false |
| -c | --config | extra Bun build config (js or ts) |
Example
index.js
import { Galbe } from 'galbe'
export default new Galbe() $ galbe build index.js
generate
Generate resources around your Galbe application.
client
Generate a type-aware HTTP client for your Galbe application.
Arguments
| Name | Description |
|---|---|
| index | The js or ts file that exports your Galbe server instance. |
Options
| Short | Long | Description | Default |
|---|---|---|---|
| -o | --out | output file | dist/(client.ts | client.js) |
| -t | --target | build target [ts, js] | ts |
| -c | --config | config file (.ts or .js) |
Example
Let's first setup a new Galbe project:
$ bun create galbe galbe-example --template hello --lang ts
$ cd galbe-example $ bun install
To generate a JS or TS client of that application, you can run:
$ galbe generate client index.ts
This generates dist/client.ts by default. The generator prints every method it creates and flags auto-derived names (routes without an explicit operationId):
💻 Building Galbe client
+ hello (explicit operationId)
~ get-ping (auto-derived)
! 1 auto-derived operationId(s) — add explicit operationIds to stabilise names API
The generated client exposes three layers:
Primary (simple, throws on error)
import { Client } from './dist/client'
const client = new Client({ server: { url: 'http://localhost:3000' } })
// Awaiting resolves to the typed body directly.
// Throws GalbeClientError on non-2xx.
const users = await client.listUsers({ query: { page: 1 } }) .safe() (typed errors, no try/catch)
const result = await client.createUser({ name: 'Alice' }).safe()
if (result.ok) {
console.log(result.data) // typed as the 200 schema
} else if (result.error.status === 400) {
console.log(result.error.body) // typed from the 400 schema
} $raw (full response, streaming, headers)
const resp = await client.$raw.getUser('abc')
if (resp.status === 200) {
const user = await resp.body.json() // typed from 200 schema
const reqId = resp.headers.get('x-request-id')
} else {
const err = await resp.body.json() // typed from error schema
} GalbeClientError (thrown by the primary API) carries .status, .headers, and .body (pre-consumed as text).
Multiple body content-types
When a route accepts more than one content-type, the generator creates a separate method per content-type:
// POST /users accepts both application/json and application/x-www-form-urlencoded
client.createUserJson({ name: 'Alice' })
client.createUserUrlForm({ name: 'Alice' }) Runtime config
new Client({
server: { url: 'http://localhost:3000' },
headers: { 'x-api-key': 'secret' }, // default headers on every request
fetch: myCustomFetch, // override fetch (interceptors, mocking, retry)
}) Config file
Pass --config <file> to customise what is generated. The file can export two named values:
transform— receivesGalbeClientRoute[](all routes, pre-split by content-type) and returns the modified array. Runs after plugin hooks.options— generation-time options baked into the generated output.
// client.config.ts
import type { GalbeClientRoute, GalbeClientOptions } from 'galbe/extras'
export const transform = (routes: GalbeClientRoute[]): GalbeClientRoute[] =>
routes
.filter(r => !r.tags.includes('internal'))
.map(r => ({ ...r, operationId: r.operationId.replace(/^get-/, '') }))
export const options: GalbeClientOptions = {
className: 'MyAppClient',
} $ galbe generate client index.ts --config client.config.ts
cli
Generate a CLI for your Galbe application. Commands are derived from your routes and grouped by their tags.
Arguments
| Name | Description |
|---|---|
| index | The js or ts file that exports your Galbe server instance. |
Options
| Short | Long | Description | Default |
|---|---|---|---|
| -o | --out | output file | dist/cli (standalone) | dist/cli.ts (module) |
| -t | --target | CLI target [cac] | cac |
| -m | --mode | output mode [standalone, module] | standalone |
| -c | --config | config file (.ts or .js) |
Standalone mode
Compiles a self-contained binary. Routes with the same tag are grouped under a sub-command; untagged routes appear at the root level.
$ galbe generate cli index.ts
This generates a dist/cli binary. Given a route tagged users:
$ ./dist/cli --help galbe-example/0.1.0
Usage:
$ galbe-example <command> [options]Commands:
users users commandsOptions:
-h, --help Display this message-v, --version Display version number
$ ./dist/cli users --help galbe-example/0.1.0
Usage:
$ galbe-example users <command> [options]Commands:
list List all users get <id> Get user by IDOptions:
-h, --help Display this message$ ./dist/cli users get abc123 200
{"id":"abc123","name":"Alice"}Important
A
GCLI_SERVER_URLenvironment variable must be defined. It should point to the URL of the Galbe server you want to target.
Each command exposes the following built-in options for controlling the request and response:
| Short | Long | Description | Default |
|---|---|---|---|
| -H | --header | extra request header as name=value (repeatable) |
[] |
| -Q | --query | extra query param as name=value (repeatable) |
[] |
| -b | --body | request body string | |
| -B | --body-file | path to a file used as request body |
The default response output shows status, body, and pretty-prints JSON. To customise this behaviour, use responseFormatter in your config file.
Module mode
Generates a .ts (or .js) module that exports a register function. The caller owns the cac instance and calls .parse() themselves.
$ galbe generate cli index.ts --mode module --out src/api-cli.ts
src/api-cli.ts is generated. You use it like this:
import cac from 'cac'
import { register } from './src/api-cli'
const cli = cac('myapp')
register(cli)
cli.help()
cli.parse() You can also override runtime options:
register(cli, {
baseUrl: () => process.env.API_URL ?? 'http://localhost:3000',
headers: { 'x-api-key': 'secret' },
responseFormatter: async res => `[${res.status}] ${await res.text()}\n`,
}) Config file
Pass --config <file> to customise commands and set baked-in defaults. The file can export two named values:
transform— a function that receives the generatedGalbeCLICommand[]and returns the modified array. Runs after plugin hooks.options— an object with request/response defaults baked into the generated output.
// cli.config.ts
import type { GalbeCLICommand, GalbeCLIOptions } from 'galbe/extras'
export const transform = (commands: GalbeCLICommand[]): GalbeCLICommand[] =>
commands
.filter(c => !c.tags.includes('internal'))
.map(c => ({ ...c, name: c.name.replace(/^get-/, '') }))
export const options: GalbeCLIOptions = {
baseUrl: () => process.env.API_URL ?? 'http://localhost:3000',
headers: { 'x-api-key': process.env.API_KEY ?? '' },
requestInterceptor: async req => {
req.headers.set('x-request-id', crypto.randomUUID())
return req
},
responseFormatter: async res => {
const body = res.headers.get('content-type')?.includes('application/json')
? JSON.stringify(await res.json(), null, 2)
: await res.text()
return `${res.status}\n${body}\n`
},
} $ galbe generate cli index.ts --config cli.config.ts
spec
Generate the spec of your Galbe application.
Arguments
| Name | Description |
|---|---|
| index | The js or ts file that exports your Galbe server instance. |
Options
| Short | Long | Description | Default |
|---|---|---|---|
| -t | --target | spec target [openapi:3.0:json, openapi:3.0:yaml] | openapi:3.0:yaml |
| -b | --base | base spec file | |
| -o | --out | output file | spec/api.(yaml | json) |
Example
Let's try to generate the spec of the project defined in the previous client section. You can then run:
$ galbe generate spec index.ts
This should generate the following spec/api.yaml file:
openapi: 3.0.3
info:
title: galbe-app
version: 0.1.0
paths:
/hello/{name}:
get:
summary: Greeting endpoint
operationId: hello
parameters:
- name: age
in: query
required: true
schema:
type: integer
responses:
'200':
description: OK
content:
text/plain:
schema:
type: string code
Generate the code and project structure from a spec.
The command is non-destructive: re-running it on an existing project diffs the spec against the routes already in src/routes/** and surgically updates them. Schema definitions and route metadata (JSDoc, schema arg) are refreshed, but your handler bodies and hooks are preserved verbatim. Routes that exist in code but are absent from the new spec are reported as stale and require an explicit decision before the command will write anything.
Arguments
| Name | Description |
|---|---|
| input | The input spec file from which the code will be generated. |
Options
| Short | Long | Description | Default |
|---|---|---|---|
| -f | --format | input format [openapi:3.0:yaml, openapi:3.0:json] | openapi:3.0:(yaml | json) |
| -t | --target | source target [ts, js] | ts |
| -o | --out | output dir | src |
| -n | --dry-run | show planned changes without writing | false |
| --remove-stale | delete routes present in code but absent from the spec | false | |
| --rename | "OLD=NEW" — preserve handler when a route id changes (repeatable) |
||
| --ignore-route | "METHOD /path" — leave a stale route alone, treat as user-managed (repeatable) |
How re-generation handles each route
| Situation | Action |
|---|---|
| In spec, not in code | Added — fresh stub appended to the route file. |
In spec, in code (same METHOD path) |
Updated — JSDoc + schema argument refreshed; handler body and hook array left untouched. |
| In code, not in spec | Stale — command exits with the list and prompts for --rename, --ignore-route, or --remove-stale. |
In code, with --rename "OLD=NEW" |
Treated as an update of NEW. Handler preserved; path string and schema arg rewired. |
In code, with --ignore-route "METHOD /path" |
Left alone, including its schema import. |
In code, with --remove-stale |
Deleted; unused schema imports pruned. |
User-added imports, helpers, and other top-level statements in route files are preserved.
Stale-route prompt
If you change the spec to drop a route, the next run will refuse to write and print:
$ galbe generate code petstore.spec.json
The following routes exist in code but are not in the spec:- DELETE /pet/:petId (scope: /pet)
Re-run with one of:
--rename "OLD=NEW" treat as a rename, preserve handler --ignore-route "ROUTE" leave alone, keep as user-managed--remove-stale confirm deletion of stale routes
Use --dry-run at any point to preview the diff without touching the filesystem.
Example
For that example, we will generate the Galbe source code from the Swagger Petstore Openapi spec.
First, initiate a new bun project and install the galbe dependency.
$ mkdir petstore && cd petstore $ bun init && bun add galbe
Now modify the index.ts file with the following content:
import { Galbe } from 'galbe'
export default new Galbe() Then download the petstore json spec from Swagger website into petstore.spec.json:
$ curl -o petstore.spec.json https://petstore3.swagger.io/api/v3/openapi.json
You can now generate the sources from the petstore spec:
$ galbe generate code petstore.spec.json
This should generate the code of our application in the src directory by default.
To test that the code was successfully generated, you can run:
$ galbe dev index.ts
🏗️ Constructing routes
src/routes/pet.route.ts
[PUT] /pet Update an existing pet
[POST] /pet Add a new pet to the store
[GET] /pet/findByStatus Finds Pets by status
[GET] /pet/findByTags Finds Pets by tags
[GET] /pet/:petId Find pet by ID
[POST] /pet/:petId Updates a pet in the store with form data[DELETE] /pet/:petId Deletes a pet
[POST] /pet/:petId/uploadImage uploads an image
src/routes/store.route.ts
[GET] /store/inventory Returns pet inventories by status
[POST] /store/order Place an order for a pet[GET] /store/order/:orderId Find purchase order by ID
[DELETE] /store/order/:orderId Delete purchase order by ID
src/routes/user.route.ts
[POST] /user Create user
[POST] /user/createWithList Creates list of users with given input array[GET] /user/login Logs user into the system
[GET] /user/logout Logs out current logged in user session[GET] /user/:username Get user by user name
[PUT] /user/:username Update user
[DELETE] /user/:username Delete user
done🚀 Server running at http://localhost:3000
model
Generate TypeScript types from a database schema.
Options
| Short | Long | Description | Default |
|---|---|---|---|
| -u | --url | database connection url (e.g. postgres://user:pwd@host:port) |
required |
| -t | --table | table name (e.g. users or public.users) |
all tables |
| -s | --schema | schema name | public |
| -o | --out | output file (.ts) or directory |
. |
| -F | --force | force overriding output | false |
Example
$ galbe generate model -u postgres://postgres:secret@localhost:5432/app -o src/models.ts
If --out is a directory, one file per table is created (<tableName>.ts). If it ends in .ts, all generated types are written to a single file.
info
Print information about the current OS, Bun, and Galbe versions.
Example
$ galbe info
OS
name: Linux
arch: x64version: 6.8.0-110-generic
Bun
version: 1.1.34
revision: ...
Galbe
version: 0.x.y