Skip to main content

Jobnik Worker Boilerplate

A production-ready TypeScript boilerplate for building distributed task workers using the MapColonies Jobnik SDK.

Features

  • Type-safe task handling with full TypeScript support for job and stage definitions
  • Built-in observability with distributed tracing, Prometheus metrics, and structured logging
  • Production-ready containerization with multi-stage Docker builds and Helm charts
  • Dependency injection using tsyringe for clean, testable architecture
  • Health checks and graceful shutdown with @godaddy/terminus
  • Comprehensive testing setup with Vitest for unit and integration tests

Prerequisites

  • Node.js >= 24.0.0
  • Connection to a Jobnik Job Manager API instance

Installation

npm install

Configuration

Configure the worker by editing files in the config/ directory:

  • default.json - Base configuration
  • development.json - Development overrides
  • production.json - Production overrides
  • test.json - Test environment settings
  • local.json - Local overrides (not committed to version control)

Set the Jobnik Job Manager API URL via Helm values or environment variables.

Quick Start

Development Mode

npm run start:dev

Development mode enables offline config mode and source map support for better debugging.

Production Build

npm run build
npm start

Running Tests

# Run all tests with coverage
npm test

# Watch mode for development
npm run test:watch

Customizing the Boilerplate

This boilerplate includes example "logistics" code to demonstrate task handling. Follow these steps to adapt it to your use case:

1. Remove Example Code

Delete the example logistics implementation:

rm -rf src/logistics src/seeder.ts tests/logistics.spec.ts

Remove the seeder call from src/index.ts:

// REMOVE THESE LINES:
const sdk = container.resolve<LogisticsSDK>(SERVICES.JOBNIK_SDK);
await seedData(sdk.getProducer());

2. Define Your Job and Stage Types

Create a new types file (e.g., src/yourDomain/types.ts):

import type { IJobnikSDK } from '@map-colonies/jobnik-sdk';

export interface YourJobTypes {
jobType1: {
data: { /* your job data schema */ };
userMetadata: { /* your job metadata */ };
};
}

export interface YourStageTypes {
stage1: {
data: { /* stage data schema */ };
userMetadata: { /* stage metadata */ };
task: {
data: { /* task data schema */ };
userMetadata: { /* task metadata */ }
};
};
}

export type YourSDK = IJobnikSDK<YourJobTypes, YourStageTypes>;

3. Create Task Handler

Create your manager (e.g., src/yourDomain/manager.ts):

import { injectable } from 'tsyringe';
import type { Task, TaskHandlerContext } from '@map-colonies/jobnik-sdk';

@injectable()
export class YourManager {
public async handleYourTask(
task: Task<YourStageTypes['stage1']['task']>,
context: TaskHandlerContext<YourJobTypes, YourStageTypes, 'jobType1', 'stage1'>
): Promise<void> {
context.logger.info({ msg: 'Processing task', taskId: task.id });

// Your task processing logic here

await context.updateStageUserMetadata({ /* updated metadata */ });
}
}

4. Update Worker Configuration

Modify src/worker.ts to use your new types and handler:

import { YourManager } from './yourDomain/manager';
import type { YourSDK } from './yourDomain/types';

export const workerBuilder: FactoryFunction<IWorker> = (container: DependencyContainer) => {
const sdk = container.resolve<YourSDK>(SERVICES.JOBNIK_SDK);
const manager = container.resolve(YourManager);

const worker = sdk.createWorker<'jobType1', 'stage1'>(
'stage1',
manager.handleYourTask.bind(manager),
config.get('jobnik.worker')
);

return worker;
};

5. Rename Throughout the Project

Update references to jobnik-worker-boilerplate in:

  • package.json - name, description, author
  • helm/Chart.yaml - name, description
  • helm/values.yaml - mclabels, configManagement.name, image.repository
  • helm/templates/_helpers.tpl - all template definitions
  • helm/templates/*.yaml - review all template files for hardcoded references

6. Update Package Metadata

Edit package.json:

{
"name": "your-worker-name",
"description": "Your worker description",
"author": "Your Team"
}

Observability

Metrics

Prometheus metrics are exposed on the /metrics endpoint (default port 8080). Key metrics include:

  • Worker task processing duration
  • Task success/failure rates
  • Active task count
  • Custom application metrics

Tracing

Distributed tracing can be enabled in config/default.json:

{
"telemetry": {
"tracing": {
"isEnabled": true,
"url": "http://your-otlp-collector:4318/v1/trace"
}
}
}

Logging

Structured JSON logging is provided by @map-colonies/js-logger. Configure log level:

{
"telemetry": {
"logger": {
"level": "info",
"prettyPrint": false
}
}
}

Deployment

Kubernetes/Helm

Deploy using Helm:

helm install your-worker-name ./helm

Documentation

note

This page was generated from a remote source. you can find it on https://github.com/MapColonies/jobnik-worker-boilerplate/blob/master/README.md