Automatically sync OpenAPI specs to TypeScript clients. No manual steps, just real-time updates.
# Install
npm install -D @tryloop/oats
# Initialize config
npx oats init
# Start development
npx oats startOATS eliminates the manual 6-step workflow of syncing OpenAPI changes:
Wait for backend to regenerate OpenAPI specCopy spec to client generatorRun generatorBuild clientLink to frontendRestart frontend
With OATS: Change your API β Everything syncs automatically β¨
OATS supports multiple configuration formats:
{
"$schema": "node_modules/@tryloop/oats/schema/oats.schema.json",
"services": {
"backend": {
"path": "./backend",
"port": 8000,
"startCommand": "npm run dev",
"apiSpec": {
"path": "/api/openapi.json"
}
},
"client": {
"path": "./api-client",
"packageName": "@myorg/api-client",
"generator": "@hey-api/openapi-ts"
},
"frontend": {
"path": "./frontend",
"port": 3000,
"startCommand": "npm run dev"
}
}
}import { defineConfig } from '@tryloop/oats'
export default defineConfig({
services: {
backend: {
path: './backend',
port: 8000,
startCommand: 'npm run dev',
apiSpec: {
path: '/api/openapi.json'
}
},
client: {
path: './api-client',
packageName: '@myorg/api-client',
generator: '@hey-api/openapi-ts'
},
frontend: {
path: './frontend',
port: 3000,
startCommand: 'npm run dev'
}
}
})Note: TypeScript configs are fully supported. OATS includes esbuild for consistent transpilation across all environments.
const { defineConfig } = require('@tryloop/oats')
module.exports = defineConfig({
// Same structure as TypeScript config
})| Language | Frameworks | OpenAPI Support |
|---|---|---|
| Node.js | Express, Fastify, NestJS, Koa, Hapi | Static files or runtime generation |
| Python | FastAPI, Flask, Django REST | Runtime endpoints (e.g., /openapi.json) |
All major frameworks: React, Vue, Angular, Svelte, Next.js, Nuxt, Remix
- @hey-api/openapi-ts (Recommended)
- swagger-typescript-api
- openapi-generator-cli
- Custom generators (via
generateCommand)
| Command | Description | Options |
|---|---|---|
oats start |
Start all services with auto-sync | --config, --quiet, --init-gen |
oats init |
Create configuration interactively | --force, --yes |
oats validate |
Validate configuration file | --config |
oats detect |
Auto-detect project structure | - |
- File watching with intelligent debouncing
- Smart change detection - ignores formatting changes
- Hash-based caching - skip unnecessary regeneration
- Concurrent sync prevention - no duplicate operations
- Auto port management - kills conflicting processes
- Cross-platform support - Windows, macOS, Linux
- Config hot-reload - changes restart services automatically
- IntelliSense support - JSON schema for autocompletion
- Multiple config formats - JSON, JS, or TypeScript (with built-in transpilation)
- Automatic backend URL injection - Frontend uses local API in dev, production in prod
- 45% faster sync than manual process
- Incremental builds when possible
- Parallel service startup
- Efficient polling for runtime API specs
{
"services": {
"backend": {
"path": "../backend",
"port": 8000,
"runtime": "python",
"python": {
"virtualEnv": ".venv"
},
"startCommand": ".venv/bin/uvicorn main:app --reload",
"apiSpec": {
"path": "/openapi.json"
}
},
"client": {
"path": "../api-client",
"packageName": "@myapp/api",
"generator": "@hey-api/openapi-ts"
},
"frontend": {
"path": "./",
"port": 3000,
"startCommand": "npm start"
}
}
}{
"services": {
"backend": {
"path": "./apps/api",
"port": 3333,
"startCommand": "nx serve api",
"apiSpec": {
"path": "swagger.json"
}
},
"client": {
"path": "./packages/api-client",
"packageName": "@myapp/api-client",
"generator": "custom",
"generateCommand": "yarn openapi-ts"
},
"frontend": {
"path": "./apps/web",
"port": 4200,
"startCommand": "nx serve web"
}
}
}| Property | Description | Required |
|---|---|---|
path |
Path to service directory | β |
port |
Port number (backend/frontend) | |
startCommand |
Command to start service | β |
runtime |
"node" or "python" |
β |
apiSpec.path |
Path to OpenAPI spec | β |
| Option | Default | Description |
|---|---|---|
strategy |
"smart" |
"smart" or "aggressive" |
debounceMs |
1000 |
Delay before regenerating |
autoLink |
true |
Auto-link packages |
pollingInterval |
5000 |
For runtime API specs |
| Option | Default | Description |
|---|---|---|
level |
"info" |
"debug", "info", "warn", "error" |
colors |
true |
Enable colored output |
timestamps |
false |
Show timestamps in logs |
showServiceOutput |
true |
Display output from services |
OATS automatically handles port conflicts. To disable:
{
"services": {
"backend": {
"env": {
"OATS_AUTO_KILL_PORTS": "false"
}
}
}
}- Check package is linked:
npm ls @myorg/api-client - For Vite: Exclude from optimization in
vite.config.ts - Ensure
packageNamematches your client'spackage.json
OATS automatically injects your backend URL into your frontend environment:
// Your frontend code
const API_URL = import.meta.env.VITE_OATS_BACKEND_BASE_URL || 'https://api.production.com'How it works:
- OATS detects your frontend framework (Vite, CRA, Next.js, etc.)
- Injects the backend URL with the correct prefix:
- Vite:
VITE_OATS_BACKEND_BASE_URL - Create React App:
REACT_APP_OATS_BACKEND_BASE_URL - Next.js:
NEXT_PUBLIC_OATS_BACKEND_BASE_URL - Vue CLI:
VUE_APP_OATS_BACKEND_BASE_URL
- Vite:
- Your app uses local backend when running with OATS, production otherwise
No configuration needed - it just works!
OATS fully supports TypeScript configs (.ts files) with built-in transpilation:
- Configs work consistently across all environments
- No additional setup or dependencies required
- Use ESM syntax:
export default defineConfig({...})
Use npx or add to scripts:
{
"scripts": {
"dev:sync": "oats start"
}
}Contributions welcome! See CONTRIBUTING.md for details.
# Clone repo
git clone https://github.com/loopkitchen/oats.git
# Install dependencies
yarn install
# Run tests
yarn test
# Start development
yarn devMIT Β© Hari Shekhar