Official Reference

Katax Docs Hub

3 published packages · npm + GitHub + DeepWiki

Katax Ecosystem

Three TypeScript packages to build, validate, and deploy Node.js APIs on VPS infrastructure with PM2.

katax-core 1.5.4

Typed validation with 20+ schemas, coercion, preprocess, async validators, file, base64, email and error utilities.

katax-service-manager 0.5.3

Runtime container: config, logger, DB (PostgreSQL/MySQL/MongoDB/Redis), cache, cron, WebSocket, transports, registry and health.

katax-cli 1.4.4

CLI for API scaffolding, CRUD, endpoints, repositories, OpenAPI docs and PM2 deploy on VPS.

Installation

npm install katax-core
npm install katax-service-manager
npm install -g katax-cli

# optional peer deps for katax-service-manager
npm install pg mysql2 mongodb redis socket.io dotenv node-cron pino-pretty

katax-core

katax-core is the validation layer of the ecosystem. The main entry point is k. Supports 20+ schema types, coercion, preprocess, async validators, advanced composition and error utilities.

Quick Start

import { k, type kataxInfer } from 'katax-core';

const UserSchema = k.object({
  email: k.email(),
  name: k.string().minLength(2).maxLength(100),
  age: k.number().min(18).optional(),
});

type User = kataxInfer<typeof UserSchema>;

const result = UserSchema.safeParse(req.body);
if (!result.success) return res.status(400).json({ errors: result.issues });

Schema API

All schemas implement: parse(), safeParse(), validate(), parseAsync(), safeParseAsync(), isValidAsync(), hasAsyncValidation(), kataxInfer.

Result Types

  • SafeParseResult<T>{ success: true; data: T } | { success: false; issues: Issue[] }
  • Issue{ path: (string | number)[]; message: string }
  • KataxError — Custom error with issues: Issue[]
  • Async: AsyncSafeParseResult<T>, AsyncValidationResult

All k factories

k.string()  k.number()  k.boolean()  k.date()  k.twoDates()
k.object()  k.array()   k.tuple()    k.record()
k.union()   k.intersection()  k.lazy()
k.email()   k.file()    k.base64()
k.custom()  k.literal() k.enum()
k.any()     k.unknown() k.never()
k.coerce    k.preprocess()

String Schema — k.string()

MethodDescription
minLength(n, msg?)Minimum length
maxLength(n, msg?)Maximum length
length(n, msg?)Exact length
email(msg?)Valid email format
url(msg?)Valid URL (URL constructor)
regex(pattern, msg?)Regex match
uuid(msg?)Valid UUID (RFC 4122)
ip(msg?)Valid IPv4
startsWith(prefix, msg?)Must start with string
endsWith(suffix, msg?)Must end with string
includes(substr, msg?)Must include substring
oneOf(options[], msg?)Must be one of the options
notOneOf(options[], msg?)Must not be any of the options
lowercase(msg?)Lowercase only
uppercase(msg?)Uppercase only
alpha(msg?)Letters only [A-Za-z]
alphanumeric(msg?)Letters and numbers only
ascii(msg?)ASCII characters only
noWhitespace(msg?)No spaces
nonempty(msg?)Cannot be empty
trim(msg?)No leading/trailing spaces

Number Schema — k.number()

MethodDescription
min(value, msg?)Minimum value (>=)
max(value, msg?)Maximum value (<=)
length(exact, msg?)Exact value
positive(msg?)Must be > 0
negative(msg?)Must be < 0
integer(msg?)Must be integer
finite(msg?)Must be finite
multipleOf(factor, msg?)Must be a multiple of
between(min, max, msg?)Inclusive range
greaterThan(n, msg?)Strictly greater than
lessThan(n, msg?)Strictly less than
notEqual(n, msg?)Not equal to value
oneOf(options[], msg?)Must be one option
notOneOf(options[], msg?)Must not be any option
nonempty(msg?)Alias for min(1)

Boolean Schema — k.boolean()

MethodDescription
isTrue(msg?)Must be true
isFalse(msg?)Must be false
equals(expected, msg?)Must equal the value

Object Schema — k.object(shape)

MethodDescription
extend(extension)Add/override fields
merge(other)Merge two object schemas
pick([keys])Select fields
omit([keys])Exclude fields
partial()All fields optional
strict()No extra keys allowed (throws)
passthrough()Allow extra keys in output
strip()Remove extra keys (default)
getShape()Get raw shape
caseInsensitive()Case-insensitive lookup
const schema = k.object({
  name: k.string().minLength(2),
  age: k.number().min(18).optional(),
}).strict();

Array Schema — k.array(elementSchema?)

MethodDescription
minLength(n, msg?)Minimum length
maxLength(n, msg?)Maximum length
length(n, msg?)Exact length
notEmpty(msg?)Cannot be empty
unique(msg?)Unique elements (deep equality)
contains(element, msg?)Must contain element

Email Schema — k.email()

MethodDescription
domain(domain, msg?)Restrict to specific domain
domains([domains], msg?)Multiple allowed domains
domainPattern(pattern, msg?)Domain regex (*.domain.com)
notDomains([blacklist], msg?)Domain blacklist
localMinLength(n, msg?)Min local part length
localMaxLength(n, msg?)Max local part length
localPattern(regex, msg?)Local part pattern
corporate(msg?)Block free providers (Gmail, Yahoo...)
noPlus(msg?)Forbid '+' addressing
noDots(msg?)Forbid '.' in local part
k.email().corporate().noPlus() // Corporate emails only, no '+'

Date Schema — k.date()

Input: ISO 8601. Output: Date object. Uses date-fns.

MethodDescription
min(dateStr, msg?)On or after the date
max(dateStr, msg?)On or before the date
between(start, end, msg?)Inclusive range
isFuture(msg?)Must be in the future
isPast(msg?)Must be in the past
format(formatStr, msg?)Must match format
isDateOnly(msg?)Date only (YYYY-MM-DD)
hasTime(msg?)Must include time
formatOutput(format)Transforms output to formatted string

TwoDates Schema — k.twoDates(separator?)

Input: Two ISO dates separated by separator (default '|'). Output: [Date, Date].

MethodDescription
maxDifference(days, msg?)Max days apart
minDifference(days, msg?)Min days apart
maxDifferenceHours(hours, msg?)Max hours apart
minDifferenceHours(hours, msg?)Min hours apart
order(ascending, msg?)Chronological order

File Schema — k.file()

Compatible with Browser File API and Node.js Multer objects.

MethodDescription
maxSize(bytes, msg?)Maximum size
minSize(bytes, msg?)Minimum size
type(mimeType, msg?)Exact MIME type
types([mimeTypes], msg?)Multiple MIME types
typePattern(pattern, msg?)MIME pattern (image/*)
extension(ext, msg?)File extension
extensions([exts], msg?)Multiple extensions
namePattern(regex, msg?)Filename regex
image(msg?)Shortcut: image/*
video(msg?)Shortcut: video/*
audio(msg?)Shortcut: audio/*
document(msg?)Shortcut: PDF, Word, Excel, plaintext
k.file().image().maxSize(5 * 1024 * 1024) // image max 5MB

Base64 Schema — k.base64()

Input: Base64 string (optionally with data URL). Compatible Browser + Node.js.

MethodDescription
minDecodedSize(bytes, msg?)Min decoded size
maxDecodedSize(bytes, msg?)Max decoded size
mimeType(type, msg?)Exact data URL MIME type
mimeTypePattern(pattern, msg?)MIME pattern
dataUrl(msg?)Must be data URL (data:...)
json(msg?)Decoded must be valid JSON
image(msg?)Shortcut: image/*
pdf(msg?)Shortcut: application/pdf

Union / Intersection / Lazy

// Union — at least one schema must match (short-circuit)
k.union([k.string(), k.number()])

// Intersection — ALL schemas must match (merge objects)
k.intersection([
  k.object({ id: k.number() }),
  k.object({ name: k.string() })
])

// Lazy — deferred resolution for recursive types
const categorySchema = k.lazy(() =>
  k.object({
    name: k.string(),
    subcategories: k.array(categorySchema)
  })
);

Literal / Enum / Tuple / Record / Any / Unknown / Never

k.literal('active')               // Exact value (===)
k.enum(['active', 'inactive'])    // String union
k.tuple([k.number(), k.string()]) // Fixed-length typed array
k.record(k.number())              // Record<string, number>
k.any()                           // Accepts everything (bypasses safety)
k.unknown()                       // Accepts everything (forces narrowing)
k.never()                         // Never matches

Custom Schema — k.custom<T>(validator)

const positiveEven = k.custom<number>((value, path) => {
  if (typeof value !== 'number') return [{ path, message: 'Must be number' }];
  if (value <= 0 || value % 2 !== 0) return [{ path, message: 'Must be positive even' }];
  return value;
});

// Add refinement
positiveEven.refine((v) => v < 1000, 'Must be less than 1000');

Modifiers / Coercion / Preprocess

Universal modifiers (all schemas)

schema.optional()            // T | undefined
schema.nullable()            // T | null
schema.default(42)           // Returns default if undefined
schema.transform((v) => ...) // Transforms output type

Coercion — k.coerce

Automatically converts before validating:

k.coerce.number()   // "42" → 42, true → 1
k.coerce.boolean()  // "true"/"1"/"yes"/"on" → true
k.coerce.string()   // 42 → "42", null → ""
k.coerce.date()     // ISO string → Date, timestamp → Date

Preprocess — k.preprocess(fn, schema)

k.preprocess(
  (val) => typeof val === 'string' ? val.trim().toLowerCase() : val,
  k.string().email()
)

Error Utilities

import { createIssue, issues, mergeIssues, isIssueArray } from 'katax-core';

createIssue(['field'], 'error message');
mergeIssues(arr1, arr2);
isIssueArray(value); // type guard

Async Validation

const usernameSchema = k.string().minLength(3).asyncRefine(async (value) => {
  const exists = await usersRepo.existsByUsername(value);
  return exists ? [{ path: ['username'], message: 'Already taken' }] : [];
});

const result = await usernameSchema.safeParseAsync(input);

katax-service-manager

Singleton container that manages the full lifecycle: config, logger, databases, WebSocket, cron, cache, registry, health and shutdown.

Core

  • Katax, katax

Services

  • ConfigService, LoggerService
  • DatabaseService, WebSocketService
  • CronService, CacheService
  • RegistryService, RedisStreamBridgeService

Errors

  • KataxServiceError, KataxConfigError
  • KataxNotInitializedError
  • KataxDatabaseError, KataxRedisError
  • KataxWebSocketError, KataxRegistryError

Transports

  • RedisTransport, CallbackTransport, TelegramTransport

Helpers

  • registerVersionToRedis, startHeartbeat, registerProjectInRedis

Initialization, Environment and Lifecycle

Init

import { katax } from 'katax-service-manager';

await katax.init({
  loadEnv: true,
  appName: 'my-api',
  logger: { level: 'info', prettyPrint: true, enableBroadcast: false },
  hooks: {
    beforeInit: () => {},  afterInit: () => {},
    beforeShutdown: () => {},  afterShutdown: () => {},
    onError: (context, error) => {},
  },
  registry: { url: 'https://dashboard.example.com/api/services' },
});

Critical order

// ✅ CORRECT — dotenv before import
import dotenv from 'dotenv';
dotenv.config();
import { katax } from 'katax-service-manager';

// ✅ CORRECT — built-in loadEnv (v0.5+)
import { katax } from 'katax-service-manager';
await katax.init({ loadEnv: true });

// ❌ WRONG — katax reads env on import, before dotenv
import { katax } from 'katax-service-manager';
import dotenv from 'dotenv';
dotenv.config();

Environment Helpers

katax.env('PORT', '3000');        // string with default
katax.env('PORT', 3000);          // number (auto-cast)
katax.env('DEBUG', false);        // boolean (auto-cast)
katax.envRequired('JWT_SECRET');  // throws if missing

katax.isDev;   katax.isProd;   katax.isTest;
katax.nodeEnv; katax.appName;  katax.version;

Lifecycle Hooks

interface KataxLifecycleHooks {
  beforeInit?: () => void | Promise<void>;
  afterInit?: () => void | Promise<void>;
  beforeShutdown?: () => void | Promise<void>;
  afterShutdown?: () => void | Promise<void>;
  onError?: (context: string, error: unknown) => void | Promise<void>;
}

Shutdown

katax.onShutdown(async () => {
  await cleanupCustomResource();
});

await katax.shutdown(); // Closes all services in order
// SIGTERM and SIGINT are handled automatically

Override for testing

import { Katax } from 'katax-service-manager';

beforeEach(() => Katax.reset());

katax.overrideService('db:main', mockDb);
katax.overrideService('logger', mockLogger);
katax.clearOverride('db:main');  // Remove specific
katax.clearOverride();           // Remove all

Database Service

Supports PostgreSQL, MySQL, MongoDB and Redis with typed connections and configurable pool.

Configuration

interface DatabaseConfig {
  name: string;
  type: 'postgresql' | 'mysql' | 'mongodb' | 'redis';
  required?: boolean;  // Default: true
  connection: string | ConnectionOptions;
  pool?: { max?: 10, min?: 2, idleTimeoutMillis?: 30000, connectionTimeoutMillis?: 30000 };
}

// Connection options per type:
// PostgreSQL: { host, port?: 5432, database, user, password, ssl? }
// MySQL:      { host, port?: 3306, database, user, password, ssl? }
// MongoDB:    { host, port?: 27017, database, user?, password?, authSource? }
// Redis:      { host, port?: 6379, password?, db?, tls? }

Setup and access

// Create
await katax.database({
  name: 'main', type: 'postgresql',
  connection: {
    host: katax.envRequired('DB_HOST'),
    port: katax.env('DB_PORT', 5432),
    database: katax.envRequired('DB_NAME'),
    user: katax.envRequired('DB_USER'),
    password: katax.envRequired('DB_PASSWORD'),
  },
  pool: { max: 10, min: 2 },
});

// Optional (does not crash on failure)
await katax.database({ name: 'analytics', type: 'postgresql', required: false, connection: { ... } });

// Redis
await katax.database({
  name: 'cache', type: 'redis',
  connection: { host: '127.0.0.1', port: 6379 },
});

// Retrieve
const db = katax.db('main');
db.asSql();    // ISqlDatabase (throws if not SQL)
db.asMongo();  // IMongoDatabase
db.asRedis();  // IRedisDatabase

Typed interfaces

interface ISqlDatabase {
  query<T>(sql: string, params?: unknown[]): Promise<T>;
  getClient(): Promise<unknown>;
  close(): Promise<void>;
}

interface IMongoDatabase {
  getClient(): Promise<unknown>;  // MongoClient
  close(): Promise<void>;
}

interface IRedisDatabase {
  redis(...args: (string | number | Buffer)[]): Promise<unknown>;
  close(): Promise<void>;
}

Redis reconnect is built-in since v0.5+. No manual configuration required.

Cache Service

High-level API over Redis with automatic JSON serialization.

const cache = katax.cache('cache'); // redis db name
MethodDescription
get<T>(key)Get value (auto-deserializes JSON)
set(key, value, ttl?)Store with optional TTL (seconds)
del(key)Delete key
delMany(keys[])Delete multiple keys
exists(key)Check existence
ttl(key)Remaining TTL (-1: no expiry, -2: missing)
expire(key, seconds)Set expiration
incr(key)Increment by 1
incrBy(key, n)Increment by n
decr(key)Decrement by 1
mget<T>(keys[])Get multiple values
mset(entries[])Store multiple [key, value]
clear(pattern?)Delete by pattern (disabled in prod for '*')
stats()Redis statistics
await cache.set('user:123', userData, 3600);
const user = await cache.get<User>('user:123');
await cache.del('user:123');
await cache.incr('page:views');
await cache.mset([['k1', v1], ['k2', v2]]);

Logger and Transports

Structured logger based on Pino with WebSocket broadcasting and transport system.

Configuration

interface LoggerConfig {
  level?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
  prettyPrint?: boolean;
  enableBroadcast?: boolean;
  destination?: string;
}

Usage

// Strings or objects
katax.logger.info('Server started');
katax.logger.info({ message: 'Server started', port: 3000 });
katax.logger.error({ message: 'Query failed', error: err });

// Broadcast to WebSocket
katax.logger.info({ message: 'Trade executed', broadcast: true, room: 'admins' });

// Persist to Redis transport
katax.logger.info({ message: 'Critical event', persist: true });

// Avoid feedback loops
katax.logger.warn({ message: 'Transport failed', skipTransport: true });
katax.logger.warn({ message: 'Telegram issue', skipTelegram: true });

// Child logger with context
const log = katax.logger.child({ service: 'payments', userId: 123 });
log.info({ message: 'Payment processed' });

LogMessage fields

interface LogMessageObject {
  message: string;   broadcast?: boolean;   room?: string;
  persist?: boolean;  skipTransport?: boolean;
  skipTelegram?: boolean;  skipRedis?: boolean;
  error?: Error;  stack?: string;  code?: string | number;
  userId?: string | number;  requestId?: string;
  duration?: number;  statusCode?: number;
  method?: string;  path?: string;  ip?: string;
  [key: string]: unknown;
}

Transports

import { RedisTransport, TelegramTransport, CallbackTransport } from 'katax-service-manager';

// Redis — persist logs to Redis Stream
const redis = new RedisTransport(katax.db('cache'), { streamKey: 'katax:logs' });
redis.filter = (log) => log.level === 'error' || log.persist === true;
katax.logger.addTransport(redis);

// Telegram — critical alerts
const telegram = new TelegramTransport({
  botToken: katax.envRequired('TELEGRAM_BOT_TOKEN'),
  chatId: katax.envRequired('TELEGRAM_ALERTS_CHAT_ID'),
  levels: ['error', 'fatal'],
  includePersist: true,
  parseMode: 'Markdown',
  name: 'telegram-errors',
});
katax.logger.addTransport(telegram);

// Callback — custom handler
const callback = new CallbackTransport({
  name: 'custom',
  send: async (log) => { /* custom logic */ },
  filter: (log) => log.level === 'error',
});
katax.logger.addTransport(callback);

// Management
katax.logger.removeTransport('telegram-errors');
await katax.logger.closeTransports();
katax.logger.getPinoLogger(); // Underlying Pino instance

WebSocket Service (Socket.IO)

// Shared with Express (preferred)
const httpServer = createServer(app);
await katax.socket({
  name: 'main',
  httpServer,
  cors: { origin: '*' },
});

// Standalone with auth
await katax.socket({
  name: 'events',
  port: 3001,
  enableAuth: true,
  authValidator: async (token) => token === katax.envRequired('WS_SECRET'),
});

const ws = katax.ws('main');
MethodDescription
emit(event, data, room?)Emit event (optionally to room)
emitToRoom(room, event, data)Emit to specific room
on(event, handler)Listen for events
onConnection(handler)Handle new connections
hasRoomListeners(room)Has listeners in room?
getRoomClientsCount(room)Clients in room
hasConnectedClients()Any clients connected?
getConnectedClientsCount()Total connected clients
getServer()Underlying Socket.IO server
close()Close WebSocket server

Connection Object

ws.onConnection((socket) => {
  socket.on('message', (data) => { ... });
  socket.emit('welcome', { status: 'connected' });
  socket.join('room-123');
  socket.leave('room-123');
});

Cron Service

katax.cron({
  name: 'process-assets',
  schedule: '*/10 6-15 * * 1-5', // every 10 min, 6am-3pm, Mon-Fri
  task: processAssets,
  runOnInit: katax.isProd,
  timezone: 'America/Mexico_City',
  enabled: true,
});

// Management
katax.cronService.getJobs();
katax.cronService.startJob('process-assets');
katax.cronService.stopJob('process-assets');
katax.cronService.removeJob('process-assets');
katax.cronService.stopAll();

Registry and Health Check

Registry

Register your service with a dashboard or custom handler. Auto-heartbeat with retry.

await katax.init({
  registry: {
    url: 'https://dashboard.example.com/api/services',
    apiKey: katax.env('REGISTRY_KEY'),
    heartbeatInterval: 30000,   // ms
    requestTimeoutMs: 5000,
    retryAttempts: 2,
    retryBaseDelayMs: 300,
    metadata: { env: katax.nodeEnv, region: 'us-east' },
  },
});

// Or custom handler
registry: {
  handler: {
    register: async (info) => { ... },
    heartbeat: async (info) => { ... },
    unregister: async (payload) => { ... },
  }
}

katax.isRegistered;       // boolean
katax.getServiceInfo();   // ServiceInfo | null

Health Check

const health = await katax.healthCheck();
// { status: 'healthy' | 'degraded' | 'unhealthy',
//   services: { databases: {}, sockets: {}, cron: boolean },
//   timestamp: number }

app.get('/api/health', async (req, res) => {
  const health = await katax.healthCheck();
  const code = health.status === 'healthy' ? 200 : health.status === 'degraded' ? 503 : 500;
  res.status(code).json(health);
});

Redis Stream Bridge and Heartbeat

Stream Bridge

Connects Redis Stream logs to WebSocket for real-time dashboards.

const bridge = katax.bridge('cache', 'main', {
  appName: 'my-api',
  streamKey: 'katax:logs',
  group: 'katax-bridge-my-api',
  batchSize: 10,
  blockTimeout: 2000,
});
await bridge.start();
bridge.isRunning();
bridge.stop();

// Client events:
// subscribe-project(appName) → receives 'project-history' + 'log'
// unsubscribe-project(appName)

Heartbeat

// Managed (auto-cleanup on shutdown)
katax.heartbeat(
  { app: katax.appName, port: 3000, version: katax.version, intervalMs: 10000 },
  'cache', 'main',
);

// Manual helpers
import { registerProjectInRedis, startHeartbeat } from 'katax-service-manager';
await registerProjectInRedis(redisDb, { app: katax.appName, version: katax.version, port: PORT });
const hb = startHeartbeat(redisDb, { app: katax.appName, port: PORT, intervalMs: 10000 }, ws);
hb?.stop();

Bootstrap Template

import dotenv from 'dotenv';
dotenv.config();

import { katax } from 'katax-service-manager';
import { createServer } from 'http';
import app from './app.js';

async function bootstrap(): Promise<void> {
  try {
    await katax.init({
      loadEnv: true,
      logger: {
        level: katax.env('LOG_LEVEL', 'info') as any,
        prettyPrint: katax.isDev,
        enableBroadcast: true,
      },
    });

    await katax.database({
      name: 'main', type: 'postgresql',
      connection: {
        host: katax.envRequired('DB_HOST'),
        port: katax.env('DB_PORT', 5432),
        database: katax.envRequired('DB_NAME'),
        user: katax.envRequired('DB_USER'),
        password: katax.envRequired('DB_PASSWORD'),
      },
      pool: { max: 10, min: 2 },
    });

    await katax.database({
      name: 'cache', type: 'redis',
      connection: { host: katax.env('REDIS_HOST', '127.0.0.1'), port: 6379 },
    });

    const PORT = katax.env('PORT', '3000');
    const httpServer = createServer(app);
    await katax.socket({ name: 'main', httpServer, cors: { origin: '*' } });

    katax.cron({
      name: 'cleanup', schedule: '0 3 * * *',
      task: async () => { /* nightly cleanup */ },
    });

    httpServer.listen(PORT, () => {
      katax.logger.info({ message: `Server running on http://localhost:${PORT}` });
    });
  } catch (err) {
    console.error('Bootstrap failed:', err);
    process.exit(1);
  }
}

void bootstrap();

katax-cli

CLI for scaffolding Katax-based APIs and managing VPS deployments with PM2.

Commands

katax init [project-name]

Scaffolds a complete Express + TypeScript + katax-core project.

FlagDescription
-f, --forceOverwrite existing directory
--pm <npm|pnpm>Package manager (default: pnpm)
--ignore-scriptsDisable lifecycle scripts
--write-npmrcWrite .npmrc for reproducible installs

Interactive prompts: name/description, database (PostgreSQL/MySQL/MongoDB/None), auth (JWT/None), validation (katax-core/None), Swagger/OpenAPI, katax-service-manager mode (singleton/instance), registry, lifecycle hooks, Redis cache, WebSocket/Socket.IO, port, git init.

katax add endpoint <name>

Scaffolds an endpoint with 4 files: validator, controller, handler, routes.

FlagDescription
-m, --method <method>HTTP method (GET, POST, PUT, PATCH, DELETE)
-p, --path <path>Custom route path

Supports interactive field definition (name, type, required, rules). Auto-updates main router and regenerates OpenAPI docs. Supports nested resources: admin/audit/logs.

katax generate crud <resource-name>

Generates complete CRUD (5 endpoints): list, get by ID, create, update, delete.

FlagDescription
--no-authWithout auth middleware

katax generate repository <name>

Generates data access layer. Detects DB type from package.json. Typed methods: findAll(), findById(), create(), update(), delete(). Uses ISqlDatabase / IMongoDatabase.

katax generate docs

FlagDescription
-f, --forceForce regeneration
-o, --output <path>Custom output path
-p, --port <port>Server port
-u, --url <url>Production URL

Scans src/api/, generates OpenAPI 3.0 spec, creates Swagger UI at /docs and /api-docs.

katax info

Shows project structure, dependencies and routes. Aliases: status, ls.

Deploy

katax deploy init       # Initial PM2 setup (repo, branch, path, cluster, memory, env)
katax deploy update     # Pull, rebuild, restart
  -b, --branch <branch>
  --hard                # Hard reset
  -a, --app-name <name>
katax deploy rollback   # Revert to previous commit(s)
  -c, --commits <n>     # Default: 1
  -a, --app-name <name>
katax deploy logs       # View PM2 logs
  -l, --lines <n>
  -f, --follow
  -a, --app-name <name>
katax deploy status     # Show PM2 apps

Fix

katax fix docs        # Patches build script to copy openapi.json
  --skip-install
katax fix all         # Apply all fixes
katax fix list        # List available patches

Global options

--no-color     # No colors
--verbose      # Detailed logging
-v, --version  # Show version

Examples

katax init my-api --pm pnpm --ignore-scripts --write-npmrc
katax add endpoint admin/audit/logs -m POST
katax generate crud admin/users --no-auth
katax generate repository products
katax generate docs -u https://api.example.com
katax deploy update -a my-api-prod

Generators and Templates

Code Generators

Validator Generator

Generates k.object() schemas with per-field validation, async validators and TypeScript inference via kataxInfer.

Controller Generator

Generates business logic with ControllerResult<T>, createSuccessResult() and createErrorResult().

Handler Generator

Generates Express middleware chaining validator + controller with sendResponse().

Route Generator

Generates Express Router with method calls and JSDoc documentation.

Router Updater

AST-based update of the main routes.ts. Prevents duplicates.

OpenAPI Generator

Scans validators and routes, generates OpenAPI 3.0 spec with schemas and tags.

Template Generators

Auth Utils

  • hashPassword() (bcrypt)
  • hashPasswordArgon2()
  • JWT generation/verification
  • Refresh tokens
  • Crypto utilities

Stream Utils (SSE)

  • initSSE() — init connection
  • sendSSEEvent() — send event
  • sendSSEComment() — keep-alive
  • closeSSE() — cleanup
  • SSEStream class with auto keep-alive

Response Utils

  • sendSuccess<T>()
  • sendError()
  • sendValidationError()
  • sendResult<T,E>()
  • sendResponse()

CLI Utilities

  • CodeBuilder: line(), import(), export(), openBlock(), build()
  • File Utils: renderTemplate(), toPascalCase(), toCamelCase()
  • Logger: success(), error(), warning(), info()

Skill katax-ecosystem

The Copilot skill for the Katax ecosystem. Knows versions, exports, patterns, CLI commands and troubleshooting.

What it covers

  • Versions: core 1.5.4, service-manager 0.5.3, cli 1.4.4
  • All schema types and their methods
  • All services and their API
  • All CLI commands with options
  • Complete bootstrap template
  • Troubleshooting and gotchas

When to invoke it

  • Create/extend APIs with Katax
  • Design validations with katax-core
  • Bootstrap with katax-service-manager
  • Generate endpoints/CRUD/repos with CLI
  • Configure DB, cache, cron, WebSocket
  • Deploy on VPS with PM2
# Local skill path
.github/skills/katax-ecosystem/SKILL.md

# Usage examples
@katax-ecosystem create a schema with coerce and asyncRefine for query params
@katax-ecosystem bootstrap PostgreSQL + Redis + graceful shutdown
@katax-ecosystem generate a nested CRUD admin/users with typed repository

Current Published Status

  • katax-core 1.5.4: 20+ schema types, coercion, async validation, file/base64, advanced email, union/intersection/lazy, preprocess, error utilities.
  • katax-service-manager 0.5.3: Singleton/instance, typed DB (PostgreSQL/MySQL/MongoDB/Redis), cache, cron, WebSocket with auth, transports (Redis/Telegram/Callback), registry, health, lifecycle hooks, stream bridge, heartbeat.
  • katax-cli 1.4.4: Init with advanced prompts, CRUD/endpoint/repository generators, OpenAPI docs, PM2 deploy (init/update/rollback/logs/status), fixes, nested resources, typed access.