Express Basic

A minimal Express app with NjiraAI enforcement and tracing.

This example shows a simple Express server that enforces policies before processing chat messages.

Full code

import express from "express";
import { NjiraAI } from "@njiraai/sdk";

const app = express();
app.use(express.json());

const njira = new NjiraAI({
  apiKey: process.env.NJIRA_API_KEY!,
  projectId: process.env.NJIRA_PROJECT_ID!,
  mode: "active",
  failStrategy: "fail_closed",
});

// Add middleware for context propagation and trace flushing
app.use(njira.middleware.express());

app.post("/chat", async (req, res) => {
  const { message } = req.body;

  // Pre-enforcement: check input before LLM call
  const pre = await njira.enforcePre({
    input: message,
    metadata: { endpoint: "/chat", source: "user" },
  });

  if (pre.verdict === "block") {
    return res.status(403).json({
      error: "Message blocked by policy",
      reasons: pre.reasons,
      traceId: pre.traceId,
    });
  }

  // Use modified input if policy rewrote it
  const effectiveInput = pre.verdict === "modify" ? pre.modifiedInput : message;

  // Start a span for the LLM call
  const spanId = njira.trace.startSpan({
    name: "llm-call",
    type: "llm",
    input: { prompt: effectiveInput },
  });

  try {
    // Simulate LLM response
    const llmResponse = `I received: "${effectiveInput}"`;

    njira.trace.endSpan(spanId, { output: llmResponse });

    // Post-enforcement: check output before returning
    const post = await njira.enforcePost({
      output: llmResponse,
      metadata: { endpoint: "/chat" },
    });

    if (post.verdict === "block") {
      return res.status(403).json({
        error: "Response blocked by policy",
        reasons: post.reasons,
        traceId: post.traceId,
      });
    }

    const finalResponse =
      post.verdict === "modify" ? post.modifiedOutput : llmResponse;

    res.json({ response: finalResponse, traceId: pre.traceId });
  } catch (error) {
    njira.trace.error(spanId, error as Error);
    res.status(500).json({ error: "Internal error" });
  }
});

app.listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});

Run it

cd sdks/typescript/examples/express-basic
pnpm install
NJIRA_API_KEY=your-key NJIRA_PROJECT_ID=your-project pnpm dev

Test it

curl -X POST http://localhost:3000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello, world!"}'

What's happening

  1. Middleware sets up request context and ensures traces flush on response
  2. enforcePre checks the input against configured policies
  3. Span tracks the LLM call duration and I/O
  4. enforcePost validates the output before returning to the user