DEV Community

Cover image for Building Production ML APIs in Node.js with HazelJS
Muhammad Arslan
Muhammad Arslan

Posted on

Building Production ML APIs in Node.js with HazelJS

A comprehensive guide to the HazelJS ML Starter—decorator-based machine learning, sentiment analysis, and scalable inference in TypeScript


Introduction

Machine learning in Node.js has matured significantly. With TensorFlow.js, ONNX Runtime, and a growing ecosystem, JavaScript/TypeScript developers can now build and serve ML models without leaving their preferred runtime.

HazelJS is a modern, decorator-first Node.js framework that brings this capability to production with @hazeljs/ml—a module for model registration, training pipelines, inference, and lifecycle management. In this post, we’ll walk through the HazelJS ML Starter—a real-world sentiment analysis API that demonstrates the full stack.


Why ML in Node.js?

Traditionally, ML has lived in Python. For many teams, however, Node.js is the primary backend. Running inference in the same process as your API reduces latency, simplifies deployment, and keeps your stack consistent. Use cases like:

  • Sentiment analysis for reviews and support tickets
  • Fraud detection on transactional data
  • Recommendation models in e-commerce
  • Content moderation for user-generated content

can all be served directly from a Node.js API. @hazeljs/ml is designed for exactly this pattern: register models, train (or load) them, and expose them via REST or any transport.


What is HazelJS?

HazelJS is a TypeScript-first Node.js framework inspired by NestJS and Fastify. It provides:

  • Decorator-based APIs for controllers, services, and modules
  • Dependency injection and modular architecture
  • Integrated packages for AI, gRPC, Kafka, Prisma, and more
  • Production-ready defaults for health checks, shutdown, and observability

The @hazeljs/ml package adds machine learning primitives on top of this foundation. You can explore the full framework on the HazelJS homepage and GitHub repository.


Architecture of the HazelJS ML Starter

The HazelJS ML Starter implements three widely-used models plus shared infrastructure:

Component Responsibility
SentimentClassifier Sentiment (positive/negative/neutral) for reviews and feedback
SpamClassifier Binary spam/ham for email, SMS, content moderation
IntentClassifier Multi-class intent routing for chatbots and support
MLModule Registers models and ML services via MLModule.forRoot()
MLController REST endpoints for predict, batch predict, train, metrics
PipelineService Preprocessing pipeline (normalize, filter) per model
ModelRegistry Versioned model storage and lookup
MetricsService Evaluation metrics for A/B testing and monitoring

The starter repository includes a working example you can clone and run locally.


Getting Started

Prerequisites

Installation

# Clone or navigate to the starter
cd hazeljs-ml-starter

# Install dependencies
npm install

# Build
npm run build

# Start the server
npm start
Enter fullscreen mode Exit fullscreen mode

The API is available at http://localhost:3000. For development with hot reload, use npm run dev. Full setup instructions are in the starter README.


The @Model, @Train, and @Predict Decorators

@hazeljs/ml uses three core decorators to define ML models:

@Model

Registers a class as an ML model with metadata for the Model Registry:

@Model({
  name: 'sentiment-classifier',
  version: '1.0.0',
  framework: 'custom',
  description: 'Text sentiment classification',
  tags: ['nlp', 'production'],
})
@Injectable()
export class SentimentClassifier {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

The ML package documentation covers all options.

@Train

Marks the training method. Options include pipeline name, batch size, and epochs:

@Train({ pipeline: 'sentiment-preprocessing', epochs: 1, batchSize: 32 })
async train(data: SentimentTrainingData): Promise<TrainingResult> {
  // Build word weights from labeled samples
  return { accuracy: 0.95, loss: 0.05 };
}
Enter fullscreen mode Exit fullscreen mode

The Train decorator source shows the full signature.

@Predict

Marks the inference method for real-time and batch prediction:

@Predict({ batch: true, endpoint: '/predict' })
async predict(input: unknown): Promise<SentimentPrediction> {
  // Score text, return sentiment + confidence
  return { sentiment: 'positive', confidence: 0.92, scores: {...} };
}
Enter fullscreen mode Exit fullscreen mode

The Predict decorator is used by both PredictorService and BatchService.


REST API Walkthrough

Single Prediction

Use the model parameter to switch between models (default: sentiment-classifier).

# Sentiment
curl -X POST http://localhost:3000/ml/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "This product is amazing! I love it."}'

# Spam
curl -X POST http://localhost:3000/ml/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "Win a free iPhone!", "model": "spam-classifier"}'

# Intent
curl -X POST http://localhost:3000/ml/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "I want a refund.", "model": "intent-classifier"}'
Enter fullscreen mode Exit fullscreen mode

Sentiment response:

{
  "result": {
    "sentiment": "positive",
    "confidence": 0.85,
    "scores": { "positive": 2.5, "negative": 0.2, "neutral": 0.3 }
  }
}
Enter fullscreen mode Exit fullscreen mode

Batch Prediction

curl -X POST http://localhost:3000/ml/predict/batch \
  -H "Content-Type: application/json" \
  -d '{"texts": ["Great!", "Terrible.", "Its okay."], "batchSize": 32}'
Enter fullscreen mode Exit fullscreen mode

The BatchService handles batching and concurrency.

Training

curl -X POST http://localhost:3000/ml/train \
  -H "Content-Type: application/json" \
  -d '{
    "samples": [
      {"text": "Love this!", "label": "positive"},
      {"text": "Hate it.", "label": "negative"},
      {"text": "Its fine.", "label": "neutral"}
    ]
  }'
Enter fullscreen mode Exit fullscreen mode

Training data flows through the PipelineService for normalization and validation.

Metrics and Model Listing

# Model evaluation metrics
curl "http://localhost:3000/ml/metrics?model=sentiment-classifier"

# List registered models
curl http://localhost:3000/ml/models

# Model versions
curl http://localhost:3000/ml/models/sentiment-classifier/versions
Enter fullscreen mode Exit fullscreen mode

MetricsService supports A/B testing and comparison of model versions.


Training Pipeline and Data Preparation

Before training, data can be preprocessed via the PipelineService. In the starter, we register a sentiment-preprocessing pipeline in ml.bootstrap.ts:

  1. normalize – Trim and lowercase text; normalize labels
  2. filter-invalid – Remove samples with empty text or invalid labels

This pattern extends to richer ETL. @hazeljs/data provides @Pipeline, @Transform, and @Validate for complex data pipelines. See the Data package for integration examples.


Programmatic Training

You can train outside the HTTP API using the train-with-sample-data script:

npm run train:sample
Enter fullscreen mode Exit fullscreen mode

This loads src/data/sample-training-data.json, trains the model, and runs a quick prediction. Useful for:

  • Initial model setup
  • CI/CD training jobs
  • Batch retraining

The starter data file includes 20 labeled samples that typically yield ~95% training accuracy.


Extending to Production

1. Integrate TensorFlow.js or ONNX

The starter uses a bag-of-words model for clarity. For production NLP, you can:

  • Load a TensorFlow.js or ONNX model
  • Keep the same @Model, @Train, @Predict interface
  • Swap the internal logic without changing controllers or clients

The @hazeljs/ml types support framework: 'tensorflow' | 'onnx' | 'custom'.

2. Model Persistence

Implement save/load in train() and on construction:

  • Save weights to ./models/ or object storage
  • Load on startup when the model is first resolved
  • Use ModelRegistry for versioning

3. Metrics and A/B Testing

Use MetricsService.recordEvaluation() after validation runs. compareVersions() helps compare model versions for rollout decisions.

4. Data Pipelines

Combine @hazeljs/ml with @hazeljs/data for:


Summary

The HazelJS ML Starter shows how to build a production-oriented ML API in Node.js with:

  • Three widely-used models: SentimentClassifier (reviews), SpamClassifier (moderation), IntentClassifier (chatbots)
  • Decorator-based models via @hazeljs/ml
  • REST API for prediction, batch inference, and training
  • Training pipeline for data preprocessing
  • Model registry and metrics for versioning and monitoring

You can use it as a template for sentiment analysis, spam detection, intent routing, or any ML workload that fits the same pattern.


Links and Resources


This blog post was created for the HazelJS ML Starter. For questions and contributions, visit the HazelJS GitHub repository or community.

Top comments (0)