npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

monirail

v1.0.12

Published

Monitor Railway services and notify your team when anomalies are detected

Readme

Monirail

Monitor Railway services and notify your team when anomalies are detected

Deploy on Railway

[!NOTE] This must be used with the Bun runtime

Quick Start

import { monitor, source, notify, watch } from "monirail";

// Create a Slack notifier
const slack = notify({
	type: "slack",
	webhookUrl: process.env.SLACK_WEBHOOK_URL,
});

// Monitor for errors in your logs
const errorMonitor = monitor({
	name: "Error Alert",
	type: "match",
	source: source({ type: "environment_logs" }),
	filter: "@level:error",
	notify: [slack],
});

// Monitor CPU usage
const cpuMonitor = monitor({
	name: "High CPU",
	type: "threshold",
	source: source({
		type: "metrics",
		aggregate: "avg",
		measure: "CPU_USAGE",
	}),
	value: 80,
	notifyOn: "above",
	notify: [slack],
});

// Check monitors every minute
watch(1, [errorMonitor, cpuMonitor]);

API Documentation

monitor(options)

Creates a monitor that runs background checks and triggers notifications when conditions are met. Monitors are stateful—they track whether they've been triggered to avoid sending duplicate notifications.

Returns: Monitor — An object with a check() method and thenable interface (can be awaited directly).


Match

Continuously filter log events and trigger notifications when a match is found.

monitor({
	name: "Error Alert",
	description: "Alerts when errors appear in logs",
	type: "match",
	source: source({ type: "environment_logs" }),
	filter: "@level:error",
	timeWindow: 5,
	notify: [slack],
});

| Parameter | Type | Required | Default | Description | | ------------- | -------------------------------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------ | | name | string | Yes | - | A unique name for the monitor. Used as a key to store state in the database. | | description | string | No | - | A description of the monitor. | | type | "match" | Yes | - | The monitor type. | | source | Promise<SourceEnvironmentLogs \| SourceHttpLogs> | Yes | - | The data source to query. Must be environment_logs or http_logs. | | filter | string | Yes | - | The filter query to match events against. Uses Railway's log filter syntax. | | timeWindow | number | No | 5 | The duration in minutes to sample events over, starting from the current time. | | notify | Notifier[] | Yes | - | An array of notifiers to send alerts to. |


Threshold

Trigger notifications when a metric or count of events crosses a threshold.

// Alert when average CPU exceeds 80%
monitor({
	name: "High CPU",
	type: "threshold",
	source: source({
		type: "metrics",
		aggregate: "avg",
		measure: "CPU_USAGE",
	}),
	value: 80,
	notifyOn: "above",
	notify: [slack],
});

// Alert when error count exceeds 10 in 5 minutes
monitor({
	name: "Error Spike",
	type: "threshold",
	source: source({ type: "environment_logs" }),
	filter: "@level:error",
	value: 10,
	notifyOn: "above",
	notify: [slack],
});

| Parameter | Type | Required | Default | Description | | ---------------- | ------------------------------------------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | name | string | Yes | - | A unique name for the monitor. Used as a key to store state in the database. | | description | string | No | - | A description of the monitor. | | type | "threshold" | Yes | - | The monitor type. | | source | Promise<SourceMetrics \| SourceEnvironmentLogs \| SourceHttpLogs> | Yes | - | The data source to query. | | value | number | Yes | - | The threshold value to compare against. | | notifyOn | "above" \| "above_or_equal" \| "below" \| "below_or_equal" | Yes | - | The comparison operator. Determines when the threshold is considered crossed. | | notifyOnNoData | boolean | No | true | Whether to trigger a notification when no data is returned from the source. | | timeWindow | number | No | 5 | The window of time in minutes to aggregate over. | | filter | string | Cond. | - | The filter query for log sources. Required when using environment_logs or http_logs sources. Uses Railway's log filter syntax. | | notify | Notifier[] | Yes | - | An array of notifiers to send alerts to. |


Availability

Trigger notifications when an HTTP service is unavailable. Makes an HTTP request to the service's static URL and checks if the response is successful.

monitor({
	name: "API Health",
	description: "Alerts when the API is down",
	type: "availability",
	source: source({ type: "service", service: "my-api" }),
	path: "/health",
	notify: [pagerduty],
});

| Parameter | Type | Required | Default | Description | | ------------- | ------------------------ | -------- | ------- | ---------------------------------------------------------------------------- | | name | string | Yes | - | A unique name for the monitor. Used as a key to store state in the database. | | description | string | No | - | A description of the monitor. | | type | "availability" | Yes | - | The monitor type. | | source | Promise<SourceService> | Yes | - | The service data source. Must be a service source. | | path | string | No | "/" | The path to check for availability. | | notify | Notifier[] | Yes | - | An array of notifiers to send alerts to. |


Custom

Trigger notifications when a custom condition is met. Use this for complex logic that doesn't fit the other monitor types.

monitor({
	name: "Deployment Check",
	type: "custom",
	source: source({ type: "service", service: "my-api" }),
	check: async () => {
		const deployments = await railway.listDeployments({
			/* ... */
		});
		const crashed = deployments.edges.filter(
			(d) => d.node.status === "CRASHED",
		);
		return {
			triggered: crashed.length > 0,
			crashedCount: crashed.length,
			deployments: crashed.map((d) => d.node.id),
		};
	},
	notify: [slack],
});

| Parameter | Type | Required | Default | Description | | ------------- | ------------------------------------------------------------------------------------ | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- | | name | string | Yes | - | A unique name for the monitor. Used as a key to store state in the database. | | description | string | No | - | A description of the monitor. | | type | "custom" | Yes | - | The monitor type. | | source | Promise<SourceMetrics \| SourceEnvironmentLogs \| SourceHttpLogs \| SourceService> | Yes | - | The data source to use. | | check | () => Promise<{ triggered: boolean } & Record<string, unknown>> | Yes | - | A function that returns whether the monitor is triggered. Any additional properties are included in the notification payload as data. | | notify | Notifier[] | Yes | - | An array of notifiers to send alerts to. |


source(options)

Creates a data source for monitors to query. Returns a Promise that resolves to the configured source.

Returns: Promise<Source> — A source object with environment info, services, and a fetch() method for querying data.


Environment Logs

Use Railway environment logs as a data source. Can filter to specific services or query all services in the environment.

// All services in the current environment
source({ type: "environment_logs" });

// Specific services only
source({
	type: "environment_logs",
	services: ["api", "worker"],
});

// Different environment
source({
	type: "environment_logs",
	environment: "production",
	services: ["api"],
});

| Parameter | Type | Required | Default | Description | | ------------- | -------------------- | -------- | ------------------- | -------------------------------------------------------------------------------------------------------- | | type | "environment_logs" | Yes | - | The source type. | | environment | string | No | Current environment | The ID or name of the Railway environment to query. Defaults to the environment monirail is deployed to. | | services | string[] | No | All services | An array of service IDs or names to filter logs to. If omitted, logs from all services are included. |


HTTP Logs

Use Railway service HTTP request/response logs as a data source.

source({
	type: "http_logs",
	service: "api",
});

| Parameter | Type | Required | Default | Description | | ------------- | ------------- | -------- | ------------------- | -------------------------------------------------------------------------------------------------------- | | type | "http_logs" | Yes | - | The source type. | | environment | string | No | Current environment | The ID or name of the Railway environment to query. Defaults to the environment monirail is deployed to. | | service | string | Yes | - | The ID or name of the Railway service to query HTTP logs from. |


Metrics

Use Railway metrics (CPU, memory, network, disk) as a data source.

// Average CPU usage across the environment
source({
	type: "metrics",
	aggregate: "avg",
	measure: "CPU_USAGE",
});

// Max memory for a specific service
source({
	type: "metrics",
	service: "api",
	aggregate: "max",
	measure: "MEMORY_USAGE_GB",
});

// Sum of network egress
source({
	type: "metrics",
	aggregate: "sum",
	measure: "NETWORK_EGRESS_GB",
});

| Parameter | Type | Required | Default | Description | | ------------- | -------------------------------------------------------------------------------------------------------------- | -------- | ------------------- | ------------------------------------------------------------------------------------------------------------- | | type | "metrics" | Yes | - | The source type. | | environment | string | No | Current environment | The ID or name of the Railway environment to query. Defaults to the environment monirail is deployed to. | | service | string | No | All services | The ID or name of a specific Railway service to query metrics from. | | aggregate | "sum" \| "avg" \| "max" \| "min" | Yes | - | How to aggregate metric values over the time window. sum intelligently handles gaps from sleeping services. | | measure | "CPU_USAGE" \| "MEMORY_USAGE_GB" \| "NETWORK_INGRESS_GB" \| "NETWORK_EGRESS_GB" \| "EPHEMERAL_DISK_USAGE_GB" | Yes | - | The metric to measure. |


Service

Use a Railway service as a data source. Primarily used for availability monitors.

source({
	type: "service",
	service: "api",
});

| Parameter | Type | Required | Default | Description | | ------------- | ----------- | -------- | ------------------- | ----------------------------------------------------------------------------------------------- | | type | "service" | Yes | - | The source type. | | environment | string | No | Current environment | The ID or name of the Railway environment. Defaults to the environment monirail is deployed to. | | service | string | Yes | - | The ID or name of the Railway service. |


notify(options)

Creates a notifier that sends alerts to a specific channel. Notifications include rich formatting with links to your Railway dashboard, logs, and services.

Returns: Notifier — An object with a send(payload) method.


Slack

Send a message to a Slack channel using the Incoming Webhook integration.

notify({
	type: "slack",
	webhookUrl: process.env.SLACK_WEBHOOK_URL,
});

| Parameter | Type | Required | Description | | ------------ | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | | type | "slack" | Yes | The notifier type. | | webhookUrl | string | Yes | The Slack Incoming Webhook URL. Get one from the Slack App Directory. |


Discord

Send a message to a Discord channel using a Webhook. Messages are formatted as rich embeds with color-coding (red for triggered, green for resolved).

notify({
	type: "discord",
	webhookUrl: process.env.DISCORD_WEBHOOK_URL,
});

| Parameter | Type | Required | Description | | ------------ | ----------- | -------- | ----------------------------------------------------------------------------------- | | type | "discord" | Yes | The notifier type. | | webhookUrl | string | Yes | The Discord Webhook URL. Create one in your Discord server's Integrations settings. |


PagerDuty

Send events to PagerDuty using the Events API v2. Automatically handles incident creation and resolution with deduplication.

notify({
	type: "pagerduty",
	routingKey: process.env.PAGERDUTY_ROUTING_KEY,
	severity: "critical",
});

| Parameter | Type | Required | Default | Description | | ------------ | ---------------------------------------------- | -------- | ---------------------- | --------------------------------------------------------------------------------------- | | type | "pagerduty" | Yes | - | The notifier type. | | routingKey | string | Yes | - | The PagerDuty Events API v2 integration key (routing key). | | severity | "critical" \| "error" \| "warning" \| "info" | Yes | - | The severity of the incident. | | component | string | No | Service name | The component of the incident. Defaults to the service name that triggered the monitor. | | group | string | No | RAILWAY_PROJECT_NAME | The logical grouping of the incident. | | client | string | No | RAILWAY_SERVICE_NAME | The name of the monitoring client. | | clientUrl | string | No | Railway service URL | A URL to the monitoring client. Defaults to the monirail service URL in Railway. |


Webhook

Send a JSON payload to any HTTP endpoint via POST request.

notify({
	type: "webhook",
	url: "https://example.com/webhook",
});

| Parameter | Type | Required | Description | | --------- | ----------- | -------- | --------------------------------------- | | type | "webhook" | Yes | The notifier type. | | url | string | Yes | The URL to send the webhook payload to. |

Payload Schema:

type NotificationPayload = {
	type: "match" | "threshold" | "availability" | "custom";
	monitor: {
		name: string;
		description?: string;
		source: { type: string; environment: Environment; services: Service[] };
		// Additional fields vary by monitor type
	};
	triggered: boolean;
	timestamp: Date;
	// Additional fields vary by monitor type (value, threshold, url, duration, data, etc.)
};

Custom

Send notifications through your own custom logic. Use this for email, SMS, or any other notification channel.

import sendgrid from "@sendgrid/mail";

notify({
	type: "custom",
	send: async (payload) => {
		await sendgrid.send({
			to: "[email protected]",
			from: "[email protected]",
			subject: `[${payload.triggered ? "ALERT" : "RESOLVED"}] ${payload.monitor.name}`,
			text: `Monitor ${payload.monitor.name} ${payload.triggered ? "triggered" : "resolved"} at ${payload.timestamp}`,
		});
	},
});

| Parameter | Type | Required | Description | | --------- | ------------------------------------------------- | -------- | ------------------------------------------------------------------ | | type | "custom" | Yes | The notifier type. | | send | (payload: NotificationPayload) => Promise<void> | Yes | A function that sends the notification. Receives the full payload. |


check(monitors)

Run all monitors once in parallel and send notifications for any that are triggered. Logs errors for any monitors that fail.

import { check } from "monirail";

await check([errorMonitor, cpuMonitor, availabilityMonitor]);

| Parameter | Type | Required | Description | | ---------- | ----------- | -------- | ------------------------------ | | monitors | Monitor[] | Yes | An array of monitors to check. |

Returns: Promise<void>


watch(interval, monitors)

Continuously run monitors at a specified interval. Returns a stop function for graceful shutdown.

import { watch } from "monirail";

const stop = watch(1, [errorMonitor, cpuMonitor]);

// Graceful shutdown
process.on("SIGINT", () => {
	stop();
	process.exit(0);
});

| Parameter | Type | Required | Description | | ---------- | ----------- | -------- | --------------------------------------- | | interval | number | Yes | The interval in minutes between checks. | | monitors | Monitor[] | Yes | An array of monitors to watch. |

Returns: () => void — A function that stops the watch loop when called.

[!WARNING] Do not use watch() in a cron job. Use check() instead for one-off executions.


Environment Variables

| Variable | Required | Default | Description | | -------------------------- | -------- | ------------------------------------------ | ------------------------------------------ | | RAILWAY_API_TOKEN | Yes | - | Your Railway API token | | RAILWAY_PROJECT_ID | Yes | - | Railway project ID | | RAILWAY_PROJECT_NAME | Yes | - | Railway project name | | RAILWAY_ENVIRONMENT_ID | Yes | - | Railway environment ID | | RAILWAY_ENVIRONMENT_NAME | Yes | - | Railway environment name | | RAILWAY_SERVICE_ID | Yes | - | Railway service ID | | RAILWAY_SERVICE_NAME | Yes | - | Railway service name | | RAILWAY_API_URL | No | https://backboard.railway.app/graphql/v2 | Railway API URL | | SQLITE_DB_FILE | No | /data/monirail.sqlite | Path to SQLite database for monitor state | | LOG_FORMAT | No | pretty | Log format: pretty or json | | NODE_ENV | No | development | Environment: development or production |