Skip to content

Simnaril

Simnaril is a TypeScript library for simulating HTTP services in tests and local development. Each simulated service stores its state in memory and runs in the same Node.js process as your application.

Application code uses its normal URLs and HTTP client. Tests set up the service’s state before calling the application and inspect that state afterward.

Simnaril requires Node.js 24 or later:

Terminal window
pnpm add -D @kensio/simnaril

The package provides the building blocks for your simulations. You define the resources and behavior that your application needs from each external service.

Create an API, add a resource, and register the API under the origin your application calls:

import { SimApi, SimEnvironment } from "@kensio/simnaril";
interface Widget {
id: string;
name: string;
}
const api = new SimApi();
const widgets = api.resource<Widget>({ path: "/widgets" });
widgets.seed({ id: "widget-1", name: "First widget" });
using environment = new SimEnvironment();
environment.register("https://api.example.com", api);
const response = await fetch("https://api.example.com/widgets/widget-1");
const widget = (await response.json()) as Widget;
console.log(widget.name); // "First widget"

widgets.seed() stores an entity that the API can return. The fetch() call reaches that API inside the current Node.js process. HTTP updates to the resource also change the state available through widgets.get().

The using declaration disposes the environment when the current scope ends. Requests to origins outside every active simulation fail by default.

Object What it does
SimEnvironment Connects outgoing HTTP requests to registered services and manages interception.
SimApi Defines routes, middleware, and error responses for one service.
SimResource Stores entities and provides methods to read and change their state.
RestResource Adds HTTP routes to a SimResource and exposes the same state methods.

api.resource() creates the state and HTTP routes together. You can also create a SimResource separately and expose it through more than one API.

Put service definitions in a factory function. A test calls the factory, seeds the resources it needs, and runs application code. The same factory can start the simulated services in your Node.js development entry point.