npm package: @nats-ms/core
Description: Type-safe NATS wrapper for microservices with messaging patterns
Author: TechnoFrog
Latest version: 0.2.0

# @nats-ms/core

⚡ Type-safe NATS wrapper for microservices with automatic type inference and built-in messaging patterns.

## Features

- 🎯 **Full Type Safety** - Automatic type inference from ServiceMap (no manual type annotations needed!)
- 📘 **TypeScript-First** - Built with TypeScript, for TypeScript
- 🔄 **Messaging Patterns** - Request-Reply, Pub-Sub, Scatter-Gather, Sharding
- 🛡️ **Resilience** - Circuit Breaker, Retry strategies, Timeouts
- 🔍 **Auto-Discovery** - Services announce themselves automatically
- 📊 **Built-in Metrics** - Prometheus-compatible metrics
- 🎭 **Middleware** - Request/Response interceptors
- 📝 **Schema Validation** - Built-in Zod validation

## Installation

```bash
npm install @nats-ms/core nats zod
```

## Quick Start

### 1. Define ServiceMap

```typescript
import { ServiceMap } from '@nats-ms/core';
import { z } from 'zod';

export class UserServiceMap extends ServiceMap {
  services = {
    'user.create': {
      request: z.object({
        username: z.string(),
        email: z.string().email(),
      }),
      response: z.object({
        userId: z.string(),
        username: z.string(),
      }),
    },
    'user.get': {
      request: z.object({
        userId: z.string(),
      }),
      response: z.object({
        userId: z.string(),
        username: z.string(),
        email: z.string(),
      }),
    },
  } as const;
}
```

### 2. Create Service

```typescript
import { NATSInstanceFactory, createLogger } from '@nats-ms/core';
import { UserServiceMap } from './service-map';

const logger = createLogger({ serviceName: 'user-service' });

// Create factory
const factory = NATSInstanceFactory.create(
  {
    servers: 'nats://localhost:4222',
    name: 'user-service',
    maxReconnectAttempts: -1,
    reconnectTimeWait: 2000,
  },
  logger
);

await factory.connect();

// Create instance with ServiceMap
const instance = factory.createInstance(new UserServiceMap(), {
  discovery: {
    version: '1.0.0',
  },
});
```

### 3. Subscribe to Topics

```typescript
// ✅ Types are inferred automatically from ServiceMap!
instance.subscribe('user.create', async (req) => {
  const { username, email } = req.data; // ✅ Fully typed!
  
  const userId = await createUser(username, email);
  
  // ✅ TypeScript validates response type
  return {
    userId,
    username,
  };
});
```

### 4. Request from Another Service

```typescript
// Make request
const response = await instance.request('user.create', {
  username: 'john',
  email: 'john@example.com',
});

// ✅ Use type guard for type narrowing
if (isSuccessResponse(response)) {
  console.log(response.data.userId); // ✅ string (not undefined!)
  console.log(response.data.username); // ✅ string
}
```

## Type System

### Automatic Type Inference

**❌ DON'T** add explicit type annotations:

```typescript
// ❌ WRONG - types will be 'any'
instance.subscribe('user.create', async (req: MSRequest) => {
  req.data // ❌ type is 'any'
});
```

**✅ DO** let TypeScript infer types automatically:

```typescript
// ✅ CORRECT - types inferred from ServiceMap
instance.subscribe('user.create', async (req) => {
  req.data.username // ✅ string
  req.data.email    // ✅ string
});
```

### Utility Types

```typescript
import { InferRequest, InferResponse, InferTopics } from '@nats-ms/core';

type Services = typeof UserServiceMap.prototype.services;

// Extract all topic names
type Topics = InferTopics<Services>; // 'user.create' | 'user.get'

// Extract request type for specific topic
type CreateRequest = InferRequest<Services, 'user.create'>;
// { username: string; email: string; }

// Extract response type
type CreateResponse = InferResponse<Services, 'user.create'>;
// { userId: string; username: string; }
```

### Type Guards

```typescript
import { isSuccessResponse, isErrorResponse } from '@nats-ms/core';

const response = await instance.request('user.create', data);

// Narrow to success type
if (isSuccessResponse(response)) {
  response.data.userId // ✅ string (NOT undefined)
}

// Narrow to error type
if (isErrorResponse(response)) {
  console.log(response.errorCode);    // ✅ number
  console.log(response.errorMessage); // ✅ string
}
```

## Messaging Patterns

### Request-Reply

```typescript
const response = await instance.request('user.get', { userId: '123' });
```

### Pub-Sub (Fire and Forget)

```typescript
instance.send('user.updated', { userId: '123', username: 'john' });
```

### Broadcast

```typescript
instance.send('notification.alert', { message: 'Alert!' }, { broadcast: true });
```

### Scatter-Gather (Request All)

```typescript
const responses = await instance.requestAll('inventory.check', {
  productId: 'p1',
});

// Aggregate results from all instances
const totalStock = responses
  .filter(isSuccessResponse)
  .reduce((sum, r) => sum + r.data.quantity, 0);
```

### First Responder

```typescript
const response = await instance.requestFirst('cache.get', { key: 'user:123' });
```

### Sharding

```typescript
instance.sendSharded(
  'analytics.track',
  { userId: '123', event: 'click' },
  'user:123', // shard key
  10 // total shards
);
```

## Resilience Patterns

### Circuit Breaker

```typescript
const response = await instance.request(
  'external-api.call',
  { data },
  {
    circuitBreaker: {
      failureThreshold: 5,
      resetTimeout: 30000,
    },
  }
);
```

### Retry Strategy

```typescript
const response = await instance.request(
  'unstable-service.call',
  { data },
  {
    retry: {
      maxRetries: 3,
      retryDelay: 1000,
      backoffMultiplier: 2,
    },
  }
);
```

### Timeout

```typescript
const response = await instance.request(
  'slow-service.call',
  { data },
  {
    timeout: 5000, // 5 seconds
  }
);
```

## Middleware

```typescript
// Logging middleware
instance.use(async (req, next) => {
  console.log(`Received: ${req.topic}`);
  const response = await next();
  console.log(`Responded: ${response.isSuccess}`);
  return response;
});

// Auth middleware
instance.use(async (req, next) => {
  const token = req.headers['authorization'];
  if (!token) {
    return {
      isSuccess: false,
      errorCode: 401,
      errorMessage: 'Unauthorized',
    };
  }
  return next();
});
```

## Auto-Discovery

Services automatically announce themselves:

```typescript
const instance = factory.createInstance(new UserServiceMap(), {
  discovery: {
    enabled: true,        // default: true
    version: '1.0.0',
    announceInterval: 30000, // 30 seconds
    // topics auto-detected from subscriptions
  },
});
```

## Metrics

Built-in Prometheus-compatible metrics:

```typescript
const metrics = factory.getMetrics();

// Available metrics:
// - messages_sent_total
// - messages_received_total
// - request_duration_seconds
// - errors_total
// - active_connections
```

## Error Handling

```typescript
import { MSError } from '@nats-ms/core';

instance.subscribe('user.create', async (req) => {
  if (await userExists(req.data.email)) {
    throw new MSError(409, 'User already exists');
  }
  
  return createUser(req.data);
});
```

## Lifecycle Events

```typescript
import { LifecycleEvent } from '@nats-ms/core';

instance.on(LifecycleEvent.REQUEST_START, (data) => {
  console.log(`Request started: ${data.topic}`);
});

instance.on(LifecycleEvent.REQUEST_END, (data) => {
  console.log(`Request completed in ${data.duration}ms`);
});

instance.on(LifecycleEvent.REQUEST_ERROR, (data) => {
  console.error(`Request error: ${data.error.message}`);
});
```

## Graceful Shutdown

```typescript
process.on('SIGINT', async () => {
  await instance.shutdown();
  await factory.close();
  process.exit(0);
});
```

## API Reference

### NATSInstanceFactory

- `create(config, logger?)` - Create factory instance
- `connect()` - Connect to NATS server
- `createInstance(serviceMap, options?)` - Create typed instance
- `createUnmappedInstance<T>(options?)` - Create instance without ServiceMap
- `close()` - Close connection
- `getMetrics()` - Get metrics instance

### NATSInstance

- `subscribe(topic, handler, options?)` - Subscribe to topic
- `request(topic, data?, options?)` - Request-reply pattern
- `requestAll(topic, data?, options?)` - Scatter-gather pattern
- `requestFirst(topic, data?, options?)` - First responder pattern
- `send(topic, data?, options?)` - Pub-sub pattern
- `sendSharded(topic, data, shardKey, totalShards?)` - Sharded messaging
- `use(middleware)` - Add middleware
- `unsubscribe(topic)` - Unsubscribe from topic
- `shutdown(options?)` - Graceful shutdown
- `getSubscriptions()` - Get active subscriptions

## CLI Tool

For quick project scaffolding, use the CLI:

```bash
npm install -g @nats-ms/cli
nats-ms init my-service
```

See [@nats-ms/cli](https://www.npmjs.com/package/@nats-ms/cli) for details.

## Requirements

- Node.js >= 18.0.0
- NATS Server 2.x

## Links

- [GitHub Repository](https://github.com/techno-frog/nats-ms)
- [npm Package](https://www.npmjs.com/package/@nats-ms/core)
- [CLI Tool](https://www.npmjs.com/package/@nats-ms/cli)

## License

MIT © 2025 TechnoFrog LLC