๐พ OATS - OpenAPI TypeScript Sync
Stop manually syncing your OpenAPI specs with TypeScript clients. OATS watches your backend API changes and automatically regenerates TypeScript clients in real-time.

๐ฅ Features
- ๐ Real-time Sync: Automatically syncs OpenAPI changes to TypeScript clients
- ๐ Zero Manual Steps: No more copy-paste workflows
- ๐ Port Conflict Resolution: Automatically frees up ports when needed
- ๐ฆ Multiple Generators: Support for any OpenAPI TypeScript generator
- ๐ ๏ธ Smart Defaults: Minimal configuration required
- ๐ Intelligent Watching: Only rebuilds when necessary
- ๐ Auto-linking: Seamlessly links packages in monorepos
- ๐ฏ Framework Agnostic: Works with React, Vue, Angular, etc.
- โก 45% Faster Sync: Optimized performance with incremental builds
- ๐ Smart Polling: Efficient polling for runtime-generated API specs
- ๐ง Hash-based Caching: Skip unnecessary regeneration with SHA-256 comparison
- ๐ Performance Tracking: Optional timing display for each sync step
๐ฏ The Problem
You're building a TypeScript full-stack app with:
- A backend (Node.js or Python) that generates OpenAPI/Swagger specs
- A TypeScript client generated from those specs
- A frontend that uses the TypeScript client
Every time you change your backend API, you need to:
- Wait for the backend to regenerate the OpenAPI spec
- Manually copy it to your client generator
- Run the generator
- Build the client
- Link it to your frontend
- Restart your frontend
That's 6 manual steps for every API change! ๐ฑ
โจ The Solution: OATS
OATS automates this entire workflow. Just run:
npx @loopkitchen/oats start
Now when you change your backend API, OATS automatically:
- โ
Detects the OpenAPI spec change
- โ
Copies it to your client project
- โ
Runs your generator
- โ
Builds the client
- โ
Links it to your frontend
- โ
Your frontend hot-reloads with the new types!
๐ Supported Technologies
Backend Frameworks
- Node.js: Express, Fastify, NestJS, Koa, Hapi, Restify
- Python: FastAPI, Flask, Django (with DRF)
Frontend Frameworks
- React, Vue, Angular, Svelte, Next.js, Nuxt, Remix
TypeScript Client Generators
- Custom generators (recommended)
- @hey-api/openapi-ts
- swagger-typescript-api
- openapi-generator-cli
๐ Quick Start
1. Install OATS
npm install --save-dev @loopkitchen/oats
yarn add -D @loopkitchen/oats
2. Create Configuration
Create oats.config.json
in your project root:
{
"services": {
"backend": {
"path": "./backend",
"port": 8000,
"startCommand": "npm run dev",
"apiSpec": {
"path": "/api/openapi.json"
}
},
"client": {
"path": "./api-client",
"generator": "@hey-api/openapi-ts",
"packageName": "@myorg/api-client"
},
"frontend": {
"path": "./frontend",
"port": 3000,
"startCommand": "npm run dev"
}
}
}
Minimal config example (OATS uses smart defaults for everything else):
{
"services": {
"backend": {
"path": "./backend",
"startCommand": "npm run dev",
"apiSpec": { "path": "/api/openapi.json" }
},
"client": {
"path": "./api-client",
"generator": "@hey-api/openapi-ts"
}
}
}
Note: Frontend configuration is optional! If you're running @loopkitchen/oats from your frontend project, it will just sync the backend and client without starting another frontend server.
Why port
and startCommand
are required for frontend
When you do configure a frontend service, both port
and startCommand
are required because:
- Different frameworks use different default ports (React: 3000, Vite: 5173, Angular: 4200)
- Different frameworks use different start commands (
npm start
, yarn dev
, ng serve
)
- This ensures OATS knows exactly how to start and monitor your frontend
3. Add Script to package.json
{
"scripts": {
"dev": "vite",
"dev:oats": "@loopkitchen/oats start"
}
}
4. Start Development
yarn dev:oats
That's it! Your entire stack is now running with automatic API synchronization.
๐ฆ Installation
npm install --save-dev @loopkitchen/oats
yarn add -D @loopkitchen/oats
pnpm add -D @loopkitchen/oats
๐จ Features
๐ง Smart Change Detection
OATS uses intelligent comparison algorithms to detect meaningful changes in your OpenAPI specs:
- โ
New endpoints or schemas
- โ
Modified request/response types
- โ
Changed authentication requirements
- โ Ignores formatting or property reordering
๐ Complete Automation
- Watches your OpenAPI/Swagger files
- Detects meaningful changes
- Copies swagger.json to client directory
- Generates TypeScript clients
- Builds the client package
- Links to your frontend projects
- Triggers frontend hot-reload via HMR
๐ฏ Developer Experience
- Port-based service detection - More reliable than log parsing
- Automatic port conflict resolution - Kills conflicting processes
- Colored logs for easy tracking
- Clear change reporting - know exactly what changed
- Error recovery - automatic retry with exponential backoff
- Concurrent sync prevention - No duplicate operations
- Desktop notifications (optional)
๐ง Flexible Configuration
Works with any OpenAPI client generator:
@hey-api/openapi-ts
swagger-typescript-api
openapi-generator-cli
- Custom generators
๐ Configuration
Complete Configuration Reference
{
"services": {
"backend": {
"path": "../backend",
"port": 4000,
"startCommand": "yarn dev",
"runtime": "node",
"python": {
"virtualEnv": "venv",
"packageManager": "pip",
"executable": "python"
},
"apiSpec": {
"path": "src/swagger.json"
}
},
"client": {
"path": "../api-client",
"packageName": "@myorg/api",
"generator": "custom",
"generateCommand": "yarn generate",
"buildCommand": "yarn build",
"linkCommand": "yarn link"
},
"frontend": {
"path": ".",
"port": 5173,
"startCommand": "yarn dev",
"packageLinkCommand": "yarn link"
}
},
"sync": {
"strategy": "smart",
"debounceMs": 1000,
"autoLink": true,
"notifications": false,
"retryAttempts": 3,
"retryDelayMs": 2000,
"runInitialGeneration": false,
"ignore": ["**/node_modules/**"]
},
"log": {
"level": "info",
"colors": true,
"timestamps": false,
"showServiceOutput": true,
"quiet": false,
"file": "./oats.log"
}
}
Port Conflict Handling
OATS automatically handles port conflicts by default. When a port is already in use:
- Detects the conflicting process
- Kills the process to free the port
- Starts your service on the freed port
To disable automatic port killing:
{
"sync": {
"autoKillConflictingPorts": false
}
}
Performance Options
Enable performance tracking and optimizations:
{
"sync": {
"showStepDurations": true,
"pollingInterval": 3000
}
}
- showStepDurations: Display how long each step takes (detection, generation, build, linking)
- pollingInterval: For runtime API specs (like FastAPI), how often to check for changes (default: 5000ms)
Python Backend Notes
For Python backends like FastAPI that generate OpenAPI specs at runtime:
- Use runtime endpoints for the API spec path (recommended)
- OATS will fetch the spec from the running server
- Make sure your backend is configured to expose the OpenAPI spec
Example for FastAPI:
{
"apiSpec": {
"path": "/openapi.json"
}
}
Minimal Configuration
If you're running @loopkitchen/oats from your frontend project, here's the minimal config:
{
"services": {
"backend": {
"path": "../backend",
"startCommand": "yarn dev",
"apiSpec": {
"path": "src/swagger.json"
}
},
"client": {
"path": "../api-client",
"packageName": "@myorg/api-client",
"generator": "custom",
"generateCommand": "yarn generate",
"buildCommand": "yarn build"
}
}
}
Generator Types
Using Custom Commands (Recommended)
{
"client": {
"generator": "custom",
"generateCommand": "yarn generate",
"buildCommand": "yarn build"
}
}
Using @hey-api/openapi-ts
{
"client": {
"generator": "@hey-api/openapi-ts",
"generatorConfig": {
"input": "./swagger.json",
"output": "./src",
"client": "axios"
}
}
}
๐ ๏ธ CLI Commands
@loopkitchen/oats start
Start all services with automatic synchronization.
@loopkitchen/oats start [options]
Options:
--init-gen Run initial client generation on startup
-c, --config Path to config file (default: oats.config.json)
--quiet Quiet mode - only show essential messages
--no-colors Disable colored output
Examples:
@loopkitchen/oats start
@loopkitchen/oats start --init-gen
@loopkitchen/oats start --config my-oats.config.json
@loopkitchen/oats start --quiet
@loopkitchen/oats start --no-colors
@loopkitchen/oats init
Interactively create a configuration file.
@loopkitchen/oats init [options]
Options:
-f, --force Overwrite existing configuration
-y, --yes Use defaults without prompting
@loopkitchen/oats validate
Check if your configuration is valid.
@loopkitchen/oats validate [options]
Options:
-c, --config Path to config file
@loopkitchen/oats detect
Auto-detect your project structure and create config.
@loopkitchen/oats detect
Note: This will scan your project and try to find:
- Backend with OpenAPI/Swagger specs
- TypeScript client projects
- Frontend applications
๐ Real-World Examples
Example 1: FastAPI (Python) + React + @hey-api/openapi-ts
Project Structure:
my-project/
โโโ backend/ # FastAPI backend
โโโ api-client/ # Generated TypeScript client
โโโ frontend/ # React app
oats.config.json:
{
"services": {
"backend": {
"path": "../backend",
"port": 8000,
"runtime": "python",
"python": {
"virtualEnv": "venv"
},
"startCommand": "source venv/bin/activate && uvicorn main:app --reload --port 8000",
"apiSpec": {
"path": "runtime:/openapi.json"
}
},
"client": {
"path": "../api-client",
"packageName": "@myapp/api-client",
"generator": "@hey-api/openapi-ts",
"generateCommand": "npm run generate",
"buildCommand": "npm run build"
},
"frontend": {
"path": "./",
"port": 3000,
"startCommand": "npm start"
}
}
}
Example 2: Express + React + Custom Generator
Project Structure:
my-project/
โโโ backend/ # Express API
โโโ api-client/ # Generated TypeScript client
โโโ frontend/ # React app
oats.config.json:
{
"services": {
"backend": {
"path": "../backend",
"port": 4000,
"startCommand": "npm run dev",
"apiSpec": {
"path": "src/swagger.json"
}
},
"client": {
"path": "../api-client",
"packageName": "@myapp/api-client",
"generator": "custom",
"generateCommand": "npm run generate",
"buildCommand": "npm run build",
"linkCommand": "npm link"
},
"frontend": {
"path": ".",
"port": 3000,
"startCommand": "npm start",
"packageLinkCommand": "npm link"
}
}
}
Example 2: NestJS + Next.js + Monorepo
Project Structure:
my-monorepo/
โโโ apps/
โ โโโ api/ # NestJS backend
โ โโโ web/ # Next.js frontend
โโโ packages/
โโโ api-client/ # Generated client
oats.config.json:
{
"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",
"buildCommand": "yarn build"
},
"frontend": {
"path": "./apps/web",
"port": 4200,
"startCommand": "nx serve web"
}
}
}
Example 3: Microservices with Multiple APIs
{
"services": {
"backend": {
"path": "../services/main-api",
"port": 4000,
"startCommand": "docker-compose up api",
"apiSpec": {
"path": "docs/openapi.yaml"
}
},
"client": {
"path": "../packages/main-api-client",
"packageName": "@company/main-api",
"generator": "custom",
"generateCommand": "yarn codegen",
"buildCommand": "yarn tsc"
},
"frontend": {
"path": "../apps/dashboard",
"port": 8080,
"startCommand": "yarn serve"
}
},
"sync": {
"debounceMs": 2000,
"retryAttempts": 5
}
}
๐ค Common Issues & Solutions
Issue: "Port already in use"
Solution: OATS now automatically kills processes using required ports! If you still have issues:
{
"services": {
"backend": { "port": 4001 },
"frontend": { "port": 5174 }
}
}
Issue: "Client not updating in frontend"
Solution: OATS now includes Vite HMR integration! Check that:
- Your OpenAPI spec path is correct
- The generator command works when run manually
- The package is properly linked (
yarn link
or npm link
)
- For Vite users: Add to your
vite.config.ts
:
export default defineConfig({
optimizeDeps: {
exclude: ['@yourorg/api-client']
}
})
Issue: "Command not found: @loopkitchen/oats"
Solution: Use npx or add to package.json scripts:
npx @loopkitchen/oats start
"scripts": {
"dev:sync": "@loopkitchen/oats start"
}
๐ค How OATS Works
1. Start: OATS starts your backend, frontend, and watches for changes
โ
2. Port Detection: Uses port-based detection to know when services are ready
โ
3. Watch: File watcher monitors your OpenAPI spec file
โ
4. Detect: When spec changes, OATS compares with previous version
โ
5. Copy: Copies swagger.json to client directory for local generation
โ
6. Generate: Run your generator command with local spec
โ
7. Build: Build the TypeScript client package
โ
8. Link: Ensure client is linked to frontend (yarn/npm link)
โ
9. HMR Trigger: Touch .oats-sync file to trigger Vite hot-reload
โ
10. Reload: Frontend hot-reloads with new types
Robust Architecture:
- โก Port-based service detection (no flaky log parsing)
- ๐ Concurrent sync prevention with operation locking
- ๐ Automatic retry with exponential backoff (2s, 4s, 8s)
- ๐งน Automatic port conflict resolution
- ๐ Local swagger.json copy for reliable generation
- ๐ฅ Vite HMR integration for instant updates
Smart Detection: OATS uses intelligent comparison to avoid unnecessary regeneration:
- โ
Detects real API changes (new endpoints, changed types)
- โ Ignores formatting changes or timestamp updates
- โ
Uses file hashing for quick comparison
๐ Why OATS?
Without OATS ๐ซ
cp ../backend/swagger.json ../client/
cd ../client && yarn generate
yarn build
yarn link
cd ../frontend && yarn link "@myorg/api-client"
With OATS ๐
yarn dev:oats
Feature Comparison
Automatic sync | โ | โ | โ
|
Smart detection | โ | โ | โ
|
Zero config | โ | โ | โ
|
Hot reload | โ | Partial | โ
|
Error recovery | โ | โ | โ
|
Multi-service | โ | Complex | โ
|
๐ก๏ธ Reliability & Performance
OATS is built with production reliability in mind:
Robust Service Management
- Port-based detection: Services are detected by port binding, not log parsing
- Automatic port cleanup: Kills existing processes on required ports before starting
- Health monitoring: Continuous port checking to ensure services stay alive
- Graceful shutdown: Proper cleanup of all processes and resources
Intelligent Sync Engine
- Concurrent operation prevention: Lock mechanism prevents duplicate syncs
- Automatic retry: Failed operations retry with exponential backoff
- Debounced file watching: Prevents rapid regeneration from multiple saves
- Smart change detection: Only syncs when meaningful API changes occur
Error Recovery
- Service crash recovery: Emits events when services crash after startup
- Malformed spec handling: Graceful error messages for invalid swagger.json
- Network resilience: Handles temporary network issues during generation
- Resource cleanup: Proper cleanup of intervals, watchers, and child processes
Performance Optimizations
- Minimal file I/O: Efficient file watching with chokidar
- Smart caching: Change detection uses efficient hashing
- Parallel operations: Services start concurrently when possible
- Memory efficient: Proper event listener management
๐ค Contributing
We welcome contributions! Please see our Contributing Guide for details.
git clone https://github.com/loopkitchen/oats.git
npm install
npm test
npm run dev
๐ License
MIT ยฉ Hari Shekhar
๐ Acknowledgments
OATS is inspired by the challenges faced by developers working with OpenAPI specifications and TypeScript in modern microservices architectures. Special thanks to:
- The OpenAPI community
- TypeScript ecosystem contributors
- Developers who value their time and sanity
Made with โค๏ธ and ๐พ for developers who deserve better tooling
GitHub โข
npm โข
Issues โข
Discussions