Server API
API reference for @criterionx/server.
createServer
Creates a new Criterion HTTP server.
import { createServer } from "@criterionx/server";
const server = createServer(options);Parameters
| Parameter | Type | Description |
|---|---|---|
options | ServerOptions | Server configuration |
ServerOptions
interface ServerOptions {
/** Decisions to expose via HTTP */
decisions: Decision<any, any, any>[];
/** Default profiles for decisions (keyed by decision ID) */
profiles?: Record<string, unknown>;
/** Enable CORS (default: true) */
cors?: boolean;
/** Middleware hooks for evaluation lifecycle */
hooks?: Hooks;
/** Prometheus metrics configuration */
metrics?: MetricsOptions;
/** OpenAPI spec generation configuration */
openapi?: OpenAPIOptions;
}Returns
Returns a CriterionServer instance.
Example
import { createServer } from "@criterionx/server";
import { myDecision } from "./decisions";
const server = createServer({
decisions: [myDecision],
profiles: {
"my-decision": { threshold: 100 },
},
});
server.listen(3000);CriterionServer
The server instance returned by createServer.
server.listen(port?)
Starts the HTTP server.
server.listen(3000);
// Criterion Server starting on port 3000...
// Decisions: 1
// Docs: http://localhost:3000/docs
// API: http://localhost:3000/decisions| Parameter | Type | Default | Description |
|---|---|---|---|
port | number | 3000 | Port to listen on |
server.handler
Access the underlying Hono app instance for custom middleware.
const server = createServer({ decisions: [...] });
// Add custom middleware
server.handler.use("*", async (c, next) => {
const start = Date.now();
await next();
console.log(`${c.req.method} ${c.req.url} - ${Date.now() - start}ms`);
});
// Add custom routes
server.handler.get("/health", (c) => c.json({ status: "healthy" }));server.metrics
Access the metrics collector (if enabled).
const server = createServer({
decisions: [...],
metrics: { enabled: true },
});
// Access metrics collector
const collector = server.metrics;
if (collector) {
console.log(collector.toPrometheus());
}Returns MetricsCollector | null. Returns null if metrics are not enabled.
HTTP Endpoints
GET /
Health check endpoint.
Response:
{
"name": "Criterion Server",
"version": "0.1.0",
"decisions": 2
}GET /docs
Interactive documentation UI (HTML).
Returns a Swagger-like interface for browsing and testing decisions.
GET /decisions
List all registered decisions.
Response:
{
"decisions": [
{
"id": "transaction-risk",
"version": "1.0.0",
"description": "Evaluate transaction risk",
"meta": { ... }
}
]
}GET /decisions/:id/schema
Get JSON Schema for a decision.
Response:
{
"id": "transaction-risk",
"version": "1.0.0",
"inputSchema": {
"type": "object",
"properties": {
"amount": { "type": "number" }
},
"required": ["amount"]
},
"outputSchema": {
"type": "object",
"properties": {
"risk": { "type": "string", "enum": ["HIGH", "LOW"] }
},
"required": ["risk"]
},
"profileSchema": {
"type": "object",
"properties": {
"threshold": { "type": "number" }
},
"required": ["threshold"]
}
}GET /decisions/:id/endpoint-schema
Get the full endpoint schema including request/response format.
Response:
{
"id": "transaction-risk",
"method": "POST",
"path": "/decisions/transaction-risk",
"requestSchema": {
"type": "object",
"properties": {
"input": { ... },
"profile": { ... }
},
"required": ["input"]
},
"responseSchema": {
"type": "object",
"properties": {
"status": { ... },
"data": { ... },
"meta": { ... }
}
}
}POST /decisions/:id
Evaluate a decision.
Request Body:
{
"input": { ... },
"profile": { ... }
}| Field | Type | Required | Description |
|---|---|---|---|
input | object | Yes | Input data matching the decision's inputSchema |
profile | object | No | Profile to use (overrides default) |
Success Response (200):
{
"status": "OK",
"data": { ... },
"meta": {
"decisionId": "transaction-risk",
"decisionVersion": "1.0.0",
"matchedRule": "rule-id",
"explanation": "Why this rule matched",
"evaluatedAt": "2024-12-29T12:00:00.000Z",
"evaluatedRules": [
{ "ruleId": "rule-1", "matched": true, "explanation": "..." },
{ "ruleId": "rule-2", "matched": false }
]
}
}Error Response (400):
{
"error": "Missing 'input' in request body"
}Utility Functions
toJsonSchema
Convert a Zod schema to JSON Schema.
import { toJsonSchema } from "@criterionx/server";
import { z } from "zod";
const schema = z.object({
name: z.string(),
age: z.number(),
});
const jsonSchema = toJsonSchema(schema);
// {
// type: "object",
// properties: {
// name: { type: "string" },
// age: { type: "number" }
// },
// required: ["name", "age"]
// }extractDecisionSchema
Extract all schemas from a decision as JSON Schema.
import { extractDecisionSchema } from "@criterionx/server";
const schema = extractDecisionSchema(myDecision);
// {
// id: "my-decision",
// version: "1.0.0",
// inputSchema: { ... },
// outputSchema: { ... },
// profileSchema: { ... }
// }TypeScript Types
ServerOptions
interface ServerOptions {
decisions: Decision<any, any, any>[];
profiles?: Record<string, unknown>;
cors?: boolean;
}EvaluateRequest
interface EvaluateRequest {
input: unknown;
profile?: unknown;
}DecisionInfo
interface DecisionInfo {
id: string;
version: string;
description?: string;
meta?: Record<string, unknown>;
}DecisionSchema
interface DecisionSchema {
id: string;
version: string;
inputSchema: JsonSchema;
outputSchema: JsonSchema;
profileSchema: JsonSchema;
}Hooks
interface Hooks {
/** Called before decision evaluation */
beforeEvaluate?: BeforeEvaluateHook;
/** Called after successful evaluation */
afterEvaluate?: AfterEvaluateHook;
/** Called when an error occurs */
onError?: OnErrorHook;
}HookContext
interface HookContext {
/** ID of the decision being evaluated */
decisionId: string;
/** Input data for the decision */
input: unknown;
/** Profile being used */
profile: unknown;
/** Unique request ID for tracing */
requestId: string;
/** Timestamp when evaluation started */
timestamp: Date;
}BeforeEvaluateHook
type BeforeEvaluateHook = (
ctx: HookContext
) => Promise<Partial<HookContext> | void> | Partial<HookContext> | void;Can modify context by returning a partial context object. Return undefined to keep original context. Throw to abort evaluation.
AfterEvaluateHook
type AfterEvaluateHook = (
ctx: HookContext,
result: Result<unknown>
) => Promise<void> | void;Receives the evaluation result. Cannot modify the result. Use for logging, metrics, side effects.
OnErrorHook
type OnErrorHook = (
ctx: HookContext,
error: Error
) => Promise<void> | void;Called when an error occurs during hook execution or evaluation.
MetricsOptions
interface MetricsOptions {
/** Enable metrics collection (default: false) */
enabled?: boolean;
/** Endpoint path for metrics (default: /metrics) */
endpoint?: string;
/** Histogram buckets for latency in seconds */
buckets?: number[];
}MetricsCollector
The MetricsCollector class collects and exports Prometheus metrics.
import { MetricsCollector } from "@criterionx/server";
const collector = new MetricsCollector();
// Increment a counter
collector.increment("my_counter", { label: "value" });
// Observe a histogram value
collector.observe("my_histogram", { label: "value" }, 0.05);
// Export to Prometheus format
console.log(collector.toPrometheus());
// Reset all metrics
collector.reset();Methods:
| Method | Description |
|---|---|
increment(name, labels?, value?) | Increment a counter |
observe(name, labels, value) | Record value in histogram |
getCounter(name, labels?) | Get current counter value |
getHistogram(name, labels) | Get histogram stats (sum, count) |
toPrometheus() | Export metrics in Prometheus format |
reset() | Reset all metrics |
Metric Constants:
import {
METRIC_EVALUATIONS_TOTAL, // "criterion_evaluations_total"
METRIC_EVALUATION_DURATION_SECONDS, // "criterion_evaluation_duration_seconds"
METRIC_RULE_MATCHES_TOTAL, // "criterion_rule_matches_total"
} from "@criterionx/server";OpenAPIOptions
interface OpenAPIOptions {
/** Enable OpenAPI spec generation (default: false) */
enabled?: boolean;
/** Endpoint path for OpenAPI spec (default: /openapi.json) */
endpoint?: string;
/** API info for OpenAPI spec */
info?: Partial<OpenAPIInfo>;
/** Enable Swagger UI (default: true when openapi is enabled) */
swaggerUI?: boolean;
/** Swagger UI endpoint (default: /swagger) */
swaggerEndpoint?: string;
}OpenAPIInfo
interface OpenAPIInfo {
/** API title */
title: string;
/** API version */
version: string;
/** API description */
description?: string;
/** Contact information */
contact?: {
name?: string;
url?: string;
email?: string;
};
/** License information */
license?: {
name: string;
url?: string;
};
}generateOpenAPISpec
Generate an OpenAPI 3.0 specification from decisions.
import { generateOpenAPISpec } from "@criterionx/server";
const spec = generateOpenAPISpec(decisions, {
title: "My API",
version: "1.0.0",
});
console.log(JSON.stringify(spec, null, 2));generateSwaggerUIHtml
Generate HTML for Swagger UI.
import { generateSwaggerUIHtml } from "@criterionx/server";
const html = generateSwaggerUIHtml("/openapi.json");