A complete event-driven microservice demonstrating how nats-plus integrates with the core-ts ecosystem.
This project shows how to build a lightweight message-driven service that:
- Publishes events to NATS
- Consumes events from NATS
- Validates incoming messages
- Retries failed processing
- Persists data to MySQL
- Exposes Kubernetes-ready health checks
Rather than demonstrating nats-plus in isolation, this sample shows how multiple libraries work together to build a production-style application.
HTTP Request
│
POST /send (JSON)
│
▼
nats-plus Publisher
│
▼
NATS Server
│
▼
nats-plus Subscriber
│
▼
validation-core
│
▼
message-processing
(Retry / Error Handler)
│
▼
mysql2-core Writer
│
▼
MySQL
GET /health
│
▼
health-service
│
┌─────────┴─────────┐
▼ ▼
NATSChecker MySQLChecker
- Event-driven architecture
- Type-safe publisher and subscriber
- Automatic JSON serialization
- Queue group support
- Message validation
- Configurable retry policy
- MySQL persistence
- Health checks
- Structured logging
- Environment-based configuration
This sample demonstrates how several core-ts libraries work together.
| Library | Purpose |
|---|---|
nats-plus |
Publish and subscribe to NATS |
message-processing |
Retry and error handling |
mysql2-core |
Write data into MySQL |
validation-core |
Validate incoming messages |
health-service |
Health endpoint |
logger-core |
Structured logging |
config-plus |
Configuration management |
Each library focuses on a single responsibility.
That demonstrates the intended layering very well.
src
├── app.ts
├── config.ts
└── user
└── index.ts
- app.ts – application bootstrap
- config.ts – application configuration
- user/ – domain model and validation schema
HTTP POST
│
▼
Publisher.publish()
│
▼
NATS
│
▼
Subscriber.subscribe()
│
▼
Validator
│
▼
Retry Processor
│
▼
MySQL Writer
- Node.js 18+
- NATS Server
- MySQL 8+
npm installExample using Docker:
docker run --rm -p 4222:4222 natsCreate a database.
CREATE DATABASE masterdata;Create the table.
CREATE TABLE users (
id VARCHAR(40) PRIMARY KEY,
username VARCHAR(255) NOT NULL,
email VARCHAR(120),
phone VARCHAR(14),
date_of_birth DATETIME
);Update src/config.ts if necessary.
Development
npm run devor
npm startProduction
npm run build
npm run prodPOST /send
Example
{
"id": "1001",
"username": "john",
"email": "john@example.com",
"phone": "0123456789",
"dateOfBirth": "1990-01-01T00:00:00Z"
}The request flow is:
HTTP
↓
Publisher
↓
NATS
↓
Subscriber
↓
Validation
↓
Retry
↓
MySQL
GET /health
The endpoint checks:
- NATS connectivity
- MySQL connectivity
They can easily be extended to include additional infrastructure services.
Example response
{
"status": "UP",
"details": {
"nats": {
"status": "UP"
},
"mysql": {
"status": "UP"
}
}
}Incoming messages are validated using validation-core.
The sample validates:
- Required fields
- Email format
- Phone format
- Maximum lengths
- Date type
Invalid messages are rejected before reaching the database.
NATS
↓
Processor
↓
Validator
↓
Business Logic
Failed writes are retried according to the configured retry policy.
Example configuration:
retries: {
1: 10000,
2: 15000,
3: 25000
}Meaning:
- First retry after 10 seconds
- Second retry after 15 seconds
- Third retry after 25 seconds
The subscriber joins the queue group:
"user-service"This allows multiple instances of the service to run concurrently while ensuring each message is processed by only one instance.
Application configuration is located in:
src/config.ts
Configuration includes:
- HTTP server
- MySQL
- NATS
- Retry policy
- Logging
config-plus merges:
- Default configuration
- Environment variables
- Environment-specific configuration
The sample intentionally keeps each component focused on a single responsibility.
Publisher
│
▼
Subscriber
│
▼
Validator
│
▼
Retry Processor
│
▼
Writer
This separation makes every component reusable and independently testable.
This sample demonstrates how the core-ts ecosystem works together.
Application
│
┌────────────────┼────────────────┐
▼ ▼ ▼
logger-core config-plus health-service
│
▼
nats-plus
│
▼
validation-core
│
▼
message-processing
│
▼
mysql2-core
│
▼
MySQL
This sample demonstrates:
- Event-driven architecture
- Type-safe messaging
- Message validation
- Retry processing
- Persistence
- Health monitoring
- Dependency injection
- Composition of reusable libraries
One of the goals of this sample is to demonstrate proper layering.
Responsible for:
- Producer
- Consumer
- Header mapping
- Health checking
Responsible for:
- JSON deserialization
- Validation
- Processing pipeline
- Retry
- Error handling
- Logging
Responsible only for business logic.
Process Order
Save Customer
Import Product
Send Notification
Business services remain independent of NATS.
Instead of embedding business logic inside RabbitMQ consumers,
Consumer
↓
Business Logic
↓
Database
↓
Retry
↓
Validation
the sample separates every concern.
Consumer
↓
Processor
↓
Validation
↓
Retry
↓
Business Logic
↓
Writer
Each layer has a single responsibility.
This makes the application:
- easier to maintain
- easier to test
- easier to replace infrastructure
- easier to extend
This sample demonstrates production-oriented practices including:
- Layered architecture
- Strong typing
- Validation
- Retry processing
- Logging
- Health monitoring
- Infrastructure abstraction
Additional production features such as dead-letter queues, delayed retry queues, metrics, and distributed tracing can be added without changing the application architecture.
MIT