Generate TypeScript types + an ergonomic typed client for a Grist document.
Given a Grist doc URL + API key, this tool:
- Fetches table + column metadata via Grist REST API
- Generates TypeScript code into an output directory:
createGristClient({ apiKey, docUrl })client.tables.<TableAlias>.<list|get|create|update|delete>()- typed row/field models
- ref/refList helper methods under
record.refs.<colAlias>()
bun install
bun run buildnpm install
npm run build# local (after build)
bun dist/cli.js --docUrl ... --apiKey ... --emit all --outDirTs ./generated/grist --outDirPy ./generated/grist_py
# via bunx from a published package:
# bunx grist-codegen --docUrl ... --apiKey ... --emit all --outDirTs ./generated/grist --outDirPy ./generated/grist_py
# via npx from a published package:
# npx grist-codegen --docUrl ... --apiKey ... --emit all --outDirTs ./generated/grist --outDirPy ./generated/grist_py
# TypeScript only (default)
node dist/cli.js \
--docUrl "https://docs.getgrist.com/abc123/MyDoc" \
--apiKey "$GRIST_API_KEY" \
--emit ts \
--outDirTs ./src/generated/grist
# Python only
node dist/cli.js \
--docUrl "https://docs.getgrist.com/abc123/MyDoc" \
--apiKey "$GRIST_API_KEY" \
--emit py \
--outDirPy ./src/generated/grist_py
# Both
node dist/cli.js \
--docUrl "https://docs.getgrist.com/abc123/MyDoc" \
--apiKey "$GRIST_API_KEY" \
--emit all \
--outDirTs ./src/generated/grist \
--outDirPy ./src/generated/grist_py
# Back-compat: --outDir is the same as --outDirTs
node dist/cli.js \
--docUrl "https://docs.getgrist.com/abc123/MyDoc" \
--apiKey "$GRIST_API_KEY" \
--outDir ./src/generated/grist
# self-hosted base url override
node dist/cli.js \
--docUrl "https://your.grist.host/o/org/abc123" \
--baseUrl "https://your.grist.host" \
--emit all \
--outDirTs ./src/generated/grist \
--outDirPy ./src/generated/grist_pyAfter generation, import from your outDir:
import { createGristClient } from "./generated/grist/client.js";
const client = createGristClient({
docUrl: "https://docs.getgrist.com/abc123/MyDoc",
apiKey: process.env.GRIST_API_KEY!,
});
const users = await client.tables.Users.list();
const u0 = users.records[0];
// Column access (exact + alias)
u0.fields["First Name"]; // exact column id
u0.fields.firstName; // alias (camelCased)
// Writes (formula columns excluded from write type)
await client.tables.Users.update(u0.id, { firstName: "Ada" });
// Refs
const order = await client.tables.Orders.get(10);
const customer = await order.refs.customer();- Column type mapping is best-effort (unknown types become
unknown). - Date/DateTime values are typed as
string | null(ISO strings). - Record writes use the Grist endpoints directly; for create/update inputs we exclude formula columns.
- The generated client includes both:
client.tables.<Alias>(sanitized PascalCase)client.tables["Exact Table Id"](string-literal access)
This repo uses Bun for running tests.
bun run build
bun testThe smoke test will:
- read
GRIST_API_KEYandGRIST_DOC_URL - generate code into a temp directory
- run a TypeScript compile of the generated output
If CI is not set to "true", the test will load .env automatically.
If CI=true, the test will not load .env and will skip unless GRIST_API_KEY and GRIST_DOC_URL are set.
- Better column type coverage (e.g. Choice/ChoiceList literal unions, Attachments objects)
- Filtering/sorting parameters for list
- Pagination helpers
- Configurable nullability / Date ->
Date - Better ref typing when refTableId cannot be resolved