tsctl - Terraform-like CLI for Typesense
A declarative infrastructure-as-code CLI for managing Typesense collections, aliases, synonyms, and curations.
Features
- 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
Installation
npm install -g tsctl
npx tsctl
Quick Start
1. Initialize a project
tsctl init
This creates:
tsctl.config.ts - Your infrastructure definition
.env - Connection settings
2. Configure connection
Edit .env:
TYPESENSE_HOST=localhost
TYPESENSE_PORT=8108
TYPESENSE_PROTOCOL=http
TYPESENSE_API_KEY=your-api-key-here
3. Define your schema
Edit 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",
},
],
});
4. Plan changes
tsctl plan
Output:
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
5. Apply changes
tsctl apply
Commands
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
Configuration Files
tsctl supports multiple configuration file formats and locations. Files are searched in the following order:
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) |
Examples
TypeScript (recommended):
import { defineConfig } from "tsctl";
export default defineConfig({
collections: [{ name: "products", fields: [...] }],
});
JSON:
{
"collections": [{ "name": "products", "fields": [...] }]
}
YAML:
collections:
- name: products
fields:
- name: title
type: string
package.json:
{
"name": "my-app",
"tsctl": {
"collections": [{ "name": "products", "fields": [...] }]
}
}
Configuration Reference
Collections
{
name: "products",
fields: [
{
name: "title",
type: "string",
optional: true,
facet: true,
index: true,
sort: true,
infix: true,
locale: "en",
stem: true,
store: true,
num_dim: 384,
vec_dist: "cosine",
reference: "users.id",
range_index: true,
},
],
default_sorting_field: "created_at",
token_separators: ["-", "/"],
symbols_to_index: ["#", "@"],
enable_nested_fields: true,
}
Field Types
string, string[] - Text
int32, int32[], int64, int64[] - Integers
float, float[] - Decimals
bool, bool[] - Booleans
geopoint, geopoint[] - Coordinates
object, object[] - Nested objects
auto - Auto-detect type
string* - Auto-embedding
image - Image embedding
Aliases
{
name: "products_live",
collection: "products",
}
Synonyms
{
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"],
}
Overrides/Curations
{
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,
}
API Keys
{
description: "Search-only key for frontend",
actions: ["documents:search"],
collections: ["products", "categories"],
}
With expiration:
{
description: "Temporary admin key",
actions: ["*"],
collections: ["*"],
expires_at: 1735689600,
}
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 Management
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
Import Existing Resources
If you have existing collections/aliases:
tsctl import
This will:
- Scan your Typesense instance
- Generate a
tsctl.imported.config.ts file
- Save the current state
Review the generated config, then rename it to tsctl.config.ts.
Environment Variables
TYPESENSE_HOST | localhost | Typesense host |
TYPESENSE_PORT | 8108 | Typesense port |
TYPESENSE_PROTOCOL | http | http or https |
TYPESENSE_API_KEY | - | API key (required) |
Multi-Environment Support
Manage multiple Typesense environments (development, staging, production) using environment-specific .env files.
Setup
Initialize with environment files:
tsctl init --with-environments
This creates:
.env - Default/development settings
.env.development - Development environment
.env.staging - Staging environment
.env.production - Production environment
Usage
Use the --env flag to target a specific environment:
tsctl plan --env staging
tsctl apply --env production
tsctl import --env development
Environment Commands
tsctl env list
tsctl env show
tsctl --env production env show
How It Works
- Base
.env file 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
Drift Detection
Detect when resources have been modified outside of tsctl (e.g., via Typesense dashboard or API).
tsctl drift
Output shows:
- Modified: Resources changed outside of tsctl
- Deleted: Resources removed outside of tsctl
- Unmanaged: Resources that exist but aren't in your config
CI/CD Integration
The drift command exits with code 1 if drift is detected, making it useful for CI pipelines:
tsctl drift --env production || echo "Drift detected!"
JSON Output
For programmatic use:
tsctl drift --json
Blue/Green Migrations
Perform zero-downtime collection schema updates using the blue/green deployment pattern.
How It Works
- 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
Quick Migration
Full migration in one command:
tsctl migrate -a products_live -c tsctl.config.ts
Step-by-Step Migration
For more control, migrate in stages:
tsctl migrate -a products_live -c tsctl.config.ts --create-only
tsctl migrate -a products_live -c tsctl.config.ts --switch-only
tsctl migrate -a products_live -c tsctl.config.ts --cleanup products_1706486400000
Options
-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 |
Rollback
If something goes wrong, switch the alias back:
tsctl state list
tsctl apply
Roadmap
License
MIT