content:types
Generate TypeScript types from your content models
Generate TypeScript type definitions from your content models — like supabase gen types typescript.
Usage
better-i18n content:types # Auto-detect project from i18n.config.ts
better-i18n content:types --project acme/landing # Explicit project
better-i18n content:types --output types/cms.ts # Custom output pathOptions
| Option | Description |
|---|---|
--project <org/name> | Project identifier. Default: reads from i18n.config.ts |
--api-key <key> | Content API key. Default: BETTER_I18N_API_KEY env var |
--output <path> | Output file path. Default: src/content-types.ts |
--dir <path> | Directory to scan for config. Default: current directory |
Setup
1. Add your API key
The CLI auto-loads .env files from your project root. Create a .env or .env.local file:
BETTER_I18N_API_KEY=bi_pub_your_api_key_hereYou can get your Content API key from the dashboard → Project Settings → API Keys.
Shell/CI environment variables always take precedence over .env files.
2. Run the generator
npx better-i18n content:types✓ Project: acme/landing
✓ Found 3 content model(s)
✓ Content types generated
Output: src/content-types.ts
Models: blog-posts, changelog, pricing-plans
Usage:
import type { BlogPosts } from "./content-types";
const { data } = await client.from<BlogPostsFields>("blog-posts").execute();Generated Output
The generated file includes typed interfaces for each content model's custom fields, plus convenience type aliases:
/**
* Auto-generated by @better-i18n/cli — do not edit manually.
*/
import type { ContentEntry, ContentEntryListItem } from "@better-i18n/sdk";
/** Blog Posts — Localized blog posts for the landing site */
export interface BlogPostsFields extends Record<string, string | null> {
author: RelationValue | null;
category: RelationValue | null;
featured: string | null;
read_time: string;
}
export type BlogPosts = ContentEntry<BlogPostsFields>;
export type BlogPostsListItem = ContentEntryListItem<BlogPostsFields>;
/** Content model: pricing-plans */
export interface PricingPlansFields extends Record<string, string | null> {
plan_id: "free" | "pro" | "enterprise";
name: string;
description: string;
monthly_prices: string | null;
// ... more fields
}
export type PricingPlans = ContentEntry<PricingPlansFields>;
export type PricingPlansListItem = ContentEntryListItem<PricingPlansFields>;
/** All content model slugs. */
export type ContentModelSlug = "blog-posts" | "pricing-plans" | "changelog";
/** Map from model slug to its custom fields type. */
export interface ContentTypeMap {
"blog-posts": BlogPostsFields;
"pricing-plans": PricingPlansFields;
"changelog": ChangelogFields;
}Using Generated Types
With the SDK client
import { createClient } from "@better-i18n/sdk";
import type { BlogPostsFields, BlogPostsListItem } from "./content-types";
const client = createClient({
project: "acme/landing",
apiKey: process.env.BETTER_I18N_API_KEY!,
});
// Typed list query
const { data } = await client
.from<BlogPostsFields>("blog-posts")
.eq("featured", "true")
.execute();
// data is BlogPostsListItem[] — fully typed!With getEntries (legacy)
const { items } = await client.getEntries<BlogPostsFields>("blog-posts", {
status: "published",
language: "en",
});
// items[0].author — typed as RelationValue | null
// items[0].read_time — typed as stringField Type Mapping
The generator maps content model field types to TypeScript:
| CMS Field Type | TypeScript Type | Example |
|---|---|---|
text, textarea, richtext | string | null | author: string | null |
number | string | null | read_time: string | null |
boolean | string | null | featured: string | null |
date, datetime | string | null | release_date: string | null |
enum | Literal union | status: "active" | "draft" | null |
media | string | null | cover: string | null |
relation | RelationValue | null | author: RelationValue | null |
Required fields omit | null from the type.
.env Auto-Loading
The CLI automatically reads environment variables from .env files at startup:
| File | Priority | Use case |
|---|---|---|
| Shell/CI vars | Highest | export BETTER_I18N_API_KEY=xxx |
.env.local | High | Local overrides (git-ignored) |
.env | Normal | Shared defaults |
Variables already set in the shell are never overridden.
CI/CD Integration
Add type generation to your CI pipeline:
- name: Generate content types
run: npx better-i18n content:types --output src/content-types.ts
env:
BETTER_I18N_API_KEY: ${{ secrets.BETTER_I18N_API_KEY }}
- name: Check for uncommitted changes
run: git diff --exit-code src/content-types.tsOr add it as a package.json script:
{
"scripts": {
"types:content": "better-i18n content:types"
}
}