A declarative infrastructure-as-code CLI for managing Typesense collections, aliases, synonyms, and curations.
- Declarative configuration: Define your Typesense schema in TypeScript config files
- Plan/Apply workflow: See what will change before applying
- State management: State stored in Typesense itself—no external dependencies
- Type-safe: Full TypeScript support with autocomplete and validation
- Import existing: Import existing Typesense resources into managed state
npm install -g tsctl
# or
npx tsctltsctl initThis creates:
tsctl.config.ts- Your infrastructure definition.env- Connection settings
Edit .env:
TYPESENSE_HOST=localhost
TYPESENSE_PORT=8108
TYPESENSE_PROTOCOL=http
TYPESENSE_API_KEY=your-api-key-hereEdit tsctl.config.ts:
import { defineConfig } from "tsctl";
export default defineConfig({
collections: [
{
name: "products",
fields: [
{ name: "name", type: "string" },
{ name: "description", type: "string", optional: true },
{ name: "price", type: "float" },
{ name: "category", type: "string", facet: true },
{ name: "tags", type: "string[]", facet: true, optional: true },
],
default_sorting_field: "price",
},
],
aliases: [
{
name: "products_live",
collection: "products",
},
],
});tsctl planOutput:
Typesense Plan:
+ collection.products (create)
+ name: "products"
+ fields: [...]
+ alias.products_live (create)
+ name: "products_live"
+ collection: "products"
Summary:
2 to create, 0 to update, 0 to delete, 0 unchanged
tsctl apply| Command | Description |
|---|---|
tsctl init |
Initialize a new project |
tsctl validate |
Validate config file |
tsctl plan |
Show planned changes |
tsctl apply |
Apply changes to Typesense |
tsctl destroy |
Destroy all managed resources |
tsctl import |
Import existing resources |
tsctl state list |
List managed resources |
tsctl state show |
Show full state JSON |
tsctl state clear |
Clear state (keeps resources) |
tsctl env list |
List available environments |
tsctl env show |
Show current environment config |
tsctl drift |
Detect changes made outside of tsctl |
tsctl migrate |
Blue/green migration for collections |
Global Options:
--env <name>- Use environment-specific.env.<name>file
tsctl supports multiple configuration file formats and locations. Files are searched in the following order:
| File | Format |
|---|---|
package.json |
"tsctl" property |
.tsctlrc |
JSON or YAML |
.tsctlrc.json |
JSON |
.tsctlrc.yaml / .tsctlrc.yml |
YAML |
.tsctlrc.js / .tsctlrc.cjs / .tsctlrc.mjs |
JavaScript |
.tsctlrc.ts / .tsctlrc.cts / .tsctlrc.mts |
TypeScript |
tsctl.config.js / tsctl.config.cjs / tsctl.config.mjs |
JavaScript |
tsctl.config.ts / tsctl.config.cts / tsctl.config.mts |
TypeScript |
tsctl.config.json |
JSON |
tsctl.config.yaml / tsctl.config.yml |
YAML |
typesense.config.* |
Legacy (all formats) |
TypeScript (recommended):
// tsctl.config.ts
import { defineConfig } from "tsctl";
export default defineConfig({
collections: [{ name: "products", fields: [...] }],
});JSON:
// tsctl.config.json
{
"collections": [{ "name": "products", "fields": [...] }]
}YAML:
# tsctl.config.yaml
collections:
- name: products
fields:
- name: title
type: stringpackage.json:
{
"name": "my-app",
"tsctl": {
"collections": [{ "name": "products", "fields": [...] }]
}
}{
name: "products",
fields: [
{
name: "title",
type: "string", // Required
optional: true, // Allow null/missing
facet: true, // Enable faceting
index: true, // Index for search (default: true)
sort: true, // Enable sorting
infix: true, // Enable infix search
locale: "en", // Language for stemming
stem: true, // Enable stemming
store: true, // Store original value
num_dim: 384, // Vector dimensions
vec_dist: "cosine", // Vector distance metric
reference: "users.id", // JOINs
range_index: true, // For numeric range queries
},
],
default_sorting_field: "created_at",
token_separators: ["-", "/"],
symbols_to_index: ["#", "@"],
enable_nested_fields: true,
}string,string[]- Textint32,int32[],int64,int64[]- Integersfloat,float[]- Decimalsbool,bool[]- Booleansgeopoint,geopoint[]- Coordinatesobject,object[]- Nested objectsauto- Auto-detect typestring*- Auto-embeddingimage- Image embedding
{
name: "products_live",
collection: "products",
}{
id: "smartphone-synonyms",
collection: "products",
synonyms: ["phone", "mobile", "smartphone", "cell phone"],
}For one-way synonyms (root word):
{
id: "tv-synonym",
collection: "products",
root: "television",
synonyms: ["tv", "telly", "television set"],
}{
id: "pin-featured",
collection: "products",
rule: {
query: "featured",
match: "exact",
},
includes: [
{ id: "product-123", position: 1 },
{ id: "product-456", position: 2 },
],
}Additional options:
{
id: "boost-category",
collection: "products",
rule: {
query: "shoes",
match: "contains",
},
filter_by: "category:=footwear",
sort_by: "popularity:desc",
remove_matched_tokens: true,
effective_from_ts: 1672531200,
effective_to_ts: 1704067200,
}{
description: "Search-only key for frontend",
actions: ["documents:search"],
collections: ["products", "categories"],
}With expiration:
{
description: "Temporary admin key",
actions: ["*"],
collections: ["*"],
expires_at: 1735689600, // Unix timestamp
}Note: API key values are only shown once when created. If you update an API key's configuration, a new key will be generated and the old one will be deleted.
State is stored in a special Typesense collection (_tsctl_state). This means:
- No external state storage needed
- State travels with your Typesense instance
- Easy backup/restore with Typesense snapshots
If you have existing collections/aliases:
tsctl importThis will:
- Scan your Typesense instance
- Generate a
tsctl.imported.config.tsfile - Save the current state
Review the generated config, then rename it to tsctl.config.ts.
| Variable | Default | Description |
|---|---|---|
TYPESENSE_HOST |
localhost |
Typesense host |
TYPESENSE_PORT |
8108 |
Typesense port |
TYPESENSE_PROTOCOL |
http |
http or https |
TYPESENSE_API_KEY |
- | API key (required) |
Manage multiple Typesense environments (development, staging, production) using environment-specific .env files.
Initialize with environment files:
tsctl init --with-environmentsThis creates:
.env- Default/development settings.env.development- Development environment.env.staging- Staging environment.env.production- Production environment
Use the --env flag to target a specific environment:
# Plan changes against staging
tsctl plan --env staging
# Apply to production
tsctl apply --env production
# Import from development
tsctl import --env development# List available environments
tsctl env list
# Show current environment configuration
tsctl env show
# Show specific environment
tsctl --env production env show- Base
.envfile is always loaded first - If
--env <name>is specified,.env.<name>is loaded and overrides base values - State is stored per-Typesense-instance, so each environment has its own state
Detect when resources have been modified outside of tsctl (e.g., via Typesense dashboard or API).
tsctl driftOutput shows:
- Modified: Resources changed outside of tsctl
- Deleted: Resources removed outside of tsctl
- Unmanaged: Resources that exist but aren't in your config
The drift command exits with code 1 if drift is detected, making it useful for CI pipelines:
# In your CI pipeline
tsctl drift --env production || echo "Drift detected!"For programmatic use:
tsctl drift --jsonPerform zero-downtime collection schema updates using the blue/green deployment pattern.
- Create a new versioned collection (e.g.,
products_1706486400000) - Index your data to the new collection
- Switch the alias to point to the new collection
- Cleanup the old collection when ready
Full migration in one command:
tsctl migrate -a products_live -c tsctl.config.tsFor more control, migrate in stages:
# Step 1: Create the new collection
tsctl migrate -a products_live -c tsctl.config.ts --create-only
# Step 2: Index your data to the new collection
# (use your own indexing process)
# Step 3: Switch the alias to the new collection
tsctl migrate -a products_live -c tsctl.config.ts --switch-only
# Step 4: Delete the old collection when ready
tsctl migrate -a products_live -c tsctl.config.ts --cleanup products_1706486400000| Option | Description |
|---|---|
-a, --alias <name> |
Alias to migrate (required) |
-c, --config <path> |
Path to config file (required) |
--collection <name> |
Collection from config (if multiple) |
--skip-delete |
Keep old collection for rollback |
--create-only |
Only create new collection |
--switch-only |
Only switch alias |
--cleanup <name> |
Delete old collection |
If something goes wrong, switch the alias back:
# List collection versions
tsctl state list
# Manually switch alias back
# Edit your config to point to the old collection and run:
tsctl apply- Collections
- Aliases
- Synonyms
- Overrides/Curations
- API Keys management
- Multi-environment support
- Drift detection
- Migration support (blue/green collections)
MIT