MCPcopy Index your code
hub / github.com/avoidwork/tenso

github.com/avoidwork/tenso @17.3.2

Chat with this repo
repository ↗ · DeepWiki ↗ · release 17.3.2 ↗ · + Follow
263 symbols 799 edges 122 files 121 documented · 46%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Tenso

Lightweight HTTP REST API framework with built-in hypermedia support

npm version Node.js Version License Build Status

🚀 Features

  • REST/Hypermedia: Automatic hypermedia link generation and pagination
  • Multiple Authentication: Basic, Bearer Token, JWT, OAuth2, SAML support
  • Flexible Routing: Express-style routing with middleware support
  • Content Negotiation: Automatic serialization for JSON, XML, YAML, CSV, HTML
  • Security Built-in: CORS, CSRF tokens, rate limiting, security headers
  • Session Management: Memory or Redis-based sessions
  • EventSource Streams: Built-in server-sent events support
  • File Serving: Static file serving with directory browsing
  • Prometheus Metrics: Built-in metrics collection
  • TypeScript Support: Full TypeScript definitions included

📦 Installation

# npm
npm install tenso

# yarn
yarn add tenso

# pnpm
pnpm add tenso

🚀 Quick Start

Basic Server

import {tenso} from "tenso";

export const app = tenso();

app.get("/", "Hello, World!");
app.start();

REST API with Routes

import {tenso} from "tenso";
import {randomUUID as uuid} from "crypto";

const initRoutes = {
    "get": {
        "/": ["reports", "uuid"],
        "/reports": ["tps"],
        "/reports/tps": (req, res) => res.error(785, Error("TPS Cover Sheet not attached")),
        "/uuid": (req, res) => res.send(uuid(), 200, {"cache-control": "no-cache"})
    }
};

export const app = tenso({initRoutes});
app.start();

Using the Class

import {Tenso} from "tenso";

class MyAPI extends Tenso {
  constructor() {
    super({
      auth: {
        protect: ["/api/private"]
      },
      defaultHeaders: {
        "x-api-version": "1.0.0"
      }
    });

    this.setupRoutes();
  }

  setupRoutes() {
    this.get("/api/health", this.healthCheck);
    this.post("/api/users", this.createUser);
  }

  healthCheck(req, res) {
    res.json({status: "healthy", timestamp: new Date().toISOString()});
  }

  createUser(req, res) {
    // Handle user creation
    res.status(201).json({message: "User created"});
  }
}

const api = new MyAPI();

📖 Table of Contents

🛤️ Creating Routes

Routes are loaded as a module, with each HTTP method as an export, affording a very customizable API server.

You can use res to: - res.send(body[, status, headers]) - res.redirect(url) - res.error(status[, Error])

Route Types

Protected Routes

Protected routes require authorization for access and will redirect to authentication endpoints if needed.

Unprotected Routes

Unprotected routes do not require authorization for access and will exit the authorization pipeline early to avoid rate limiting, CSRF tokens, & other security measures. These routes are the DMZ of your API! You must secure these endpoints with alternative methods if accepting input!

Exit Routes

As of 17.2.0 you can have routes exit the middleware pipeline immediately by setting them in the exit Array. This differs from unprotect as there is no request body handling.

Reserved Route

The /assets/* route is reserved for the HTML browsable interface assets; please do not try to reuse this for data.

Advanced Routing Examples

// Middleware for all requests
export const always = {
  "/": (req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
  }
};

// Protected routes with authentication
export const get = {
  "/admin": (req, res) => {
    // Only accessible after authentication
    res.json({admin: true});
  }
};

// Route parameters
export const get = {
  "/users/:id": (req, res) => {
    const userId = req.params.id;
    res.json({id: userId});
  }
};

🔧 Request and Response Helpers

Request Helpers

Tenso decorates req with helpers such as: - req.allow - Allowed HTTP methods - req.csrf - CSRF token information - req.ip - Client IP address - req.parsed - Parsed URL information - req.private - Private route flag - req.body - Request payload for PATCH, PUT, & POST requests - req.session - Session data when using local authentication

Response Helpers

Tenso decorates res with helpers such as: - res.send() - Send response with optional status and headers - res.status() - Set response status - res.json() - Send JSON response - res.redirect() - Send redirect response - res.error() - Send error response

🎛️ Extensibility

Tenso is extensible and can be customized with custom parsers, renderers, & serializers.

Parsers

Custom parsers can be registered with server.parser('mimetype', fn); or directly on server.parsers. The parameters for a parser are (arg).

Tenso has built-in parsers for: - application/json - application/x-www-form-urlencoded - application/jsonl - application/json-lines - text/json-lines

Renderers

Custom renderers can be registered with server.renderer('mimetype', fn);. The parameters for a renderer are (req, res, arg).

Tenso has built-in renderers for: - application/javascript - application/json - application/jsonl - application/json-lines - text/json-lines - application/yaml - application/xml - text/csv - text/html

Serializers

Custom serializers can be registered with server.serializer('mimetype', fn);. The parameters for a serializer are (arg, err, status = 200, stack = false).

Tenso has two default serializers which can be overridden: - plain - for plain text responses - custom - for standard response shape

{
  "data": null,
  "error": null,
  "links": [],
  "status": 200
}

📤 Responses

Responses will have a standard shape and will be UTF-8 by default. The result will be in data. Hypermedia (pagination & links) will be in links: [{"uri": "...", "rel": "..."}, ...], & also in the Link HTTP header.

Page size can be specified via the page_size parameter, e.g. ?page_size=25.

Sort order can be specified via the order-by parameter which accepts [field ]asc|desc & can be combined like an SQL 'ORDER BY', e.g. ?order_by=desc or ?order_by=lastName%20asc&order_by=firstName%20asc&order_by=age%20desc

🌐 REST / Hypermedia

Hypermedia is a prerequisite of REST and is best described by the Richardson Maturity Model. Tenso will automatically paginate Arrays of results, or parse Entity representations for keys that imply relationships, and create the appropriate Objects in the link Array, as well as the Link HTTP header. Object keys that match this pattern: /_(guid|uuid|id|uri|url)$/ will be considered hypermedia links.

For example, if the key user_id was found, it would be mapped to /users/:id with a link rel of related.

Tenso will bend the rules of REST when using authentication strategies provided by passport.js, or CSRF if enabled, because they rely on a session. Session storage is in memory or Redis. You have the option of a stateless or stateful API.

Hypermedia processing of the response body can be disabled as of 10.2.0 by setting req.hypermedia = false and/or req.hypermediaHeader via middleware.

⚙️ Configuration

Tenso provides comprehensive configuration options to customize every aspect of your server. All configuration options are optional - provide only what you need to override the sensible defaults.

Core Server Configuration

Option Type Default Description
host string "0.0.0.0" Server host address to bind to
port number 8000 Server port number to listen on
title string "tenso" Application title for branding and display
version string auto Framework version (auto-detected)
silent boolean false Suppress console output and logging
maxListeners number 25 Maximum number of event listeners

Content & Response Configuration

Option Type Default Description
mimeType string "application/json" Default MIME type for responses
charset string "utf-8" Default character encoding for responses
jsonIndent number 0 JSON response indentation level (0 = minified)
digit number 3 Number of decimal places for numeric formatting
renderHeaders boolean true Include headers in rendered output responses
time boolean true Include timing information in response headers
etags boolean true Enable ETag generation for response caching

Default Headers

{
  defaultHeaders: {
    "content-type": "application/json; charset=utf-8",
    "vary": "accept, accept-encoding, accept-language, origin"
  }
}

CORS Configuration

Option Type Default Description
origins Array ["*"] Allowed CORS origins
corsExpose string "cache-control, content-language, content-type, expires, last-modified, pragma" CORS exposed headers

Caching Configuration

Option Type Default Description
cacheSize number 1000 Maximum number of items in memory cache
cacheTTL number 300000 Cache time-to-live in milliseconds (5 minutes)

Request Handling

Option Type Default Description
maxBytes number 0 Maximum request body size in bytes (0 = unlimited)
catchAll boolean true Enable catch-all route handling for unmatched requests
exit Array [] Exit handlers to execute on server shutdown

Pagination & Hypermedia

Option Type Default Description
pageSize number 5 Default pagination page size
hypermedia.enabled boolean true Enable hypermedia links in responses
hypermedia.header boolean true Include hypermedia links in response headers

Static File Serving

Option Type Default Description
autoindex boolean false Enable automatic directory indexing for static files
webroot.root string "www/" Document root directory for static files
webroot.static string "/assets" Static assets directory path
webroot.template string "template.html" Template file path for rendered responses

Authentication Configuration

The auth object controls all authentication-related settings:

Basic Authentication

{
  auth: {
    basic: {
      enabled: false,        // Enable basic authentication
      list: []              // Array of "username:password" strings
    }
  }
}

Bearer Token Authentication

{
  auth: {
    bearer: {
      enabled: false,        // Enable bearer token authentication
      tokens: []            // Array of valid bearer tokens
    }
  }
}

JWT Authentication

{
  auth: {
    jwt: {
      enabled: false,        // Enable JWT authentication
      auth: null,           // Custom JWT authentication function
      audience: "",         // JWT audience claim
      algorithms: [         // Allowed JWT signing algorithms
        "HS256", "HS384", "HS512"
      ],
      ignoreExpiration: false, // Ignore JWT expiration
      issuer: "",           // JWT issuer claim
      scheme: "Bearer",     // JWT authentication scheme
      secretOrKey: ""       // JWT secret or private key
    }
  }
}

OAuth2 Authentication

{
  auth: {
    oauth2: {
      enabled: false,        // Enable OAuth2 authentication
      auth: null,           // Custom OAuth2 authentication function
      auth_url: "",         // OAuth2 authorization URL
      token_url: "",        // OAuth2 token URL
      client_id: "",        // OAuth2 client ID
      client_secret: ""     // OAuth2 client secret
    }
  }
}

SAML Authentication

{
  auth: {
    saml: {
      enabled: false,        // Enable SAML authentication
      auth: null            // Custom SAML authentication function
    }
  }
}

Authentication Routes & Protection

{
  auth: {
    delay: 0,             // Authentication delay in milliseconds
    protect: [],          // Routes requiring authentication (regex patterns)
    unprotect: [],        // Routes excluded from authentication
    uri: {
      login: "/auth/login",    // Login endpoint URI
      logout: "/auth/logout",  // Logout endpoint URI
      redirect: "/",          // Post-authentication redirect URI
      root: "/auth"           // Authentication root URI
    },
    msg: {
      login: "POST 'username' & 'password' to authenticate"
    }
  }
}

Secu

Extension points exported contracts — how you extend this code

TensoRequest (Interface)
(no doc)
types/core.d.ts
PrometheusMiddlewareConfig (Interface)
(no doc)
types/middleware.d.ts
RateLimitState (Interface)
(no doc)
types/tenso.d.ts
TensoResponse (Interface)
(no doc)
types/core.d.ts
PrometheusMiddleware (Interface)
(no doc)
types/middleware.d.ts
BasicAuthConfig (Interface)
(no doc)
types/core.d.ts
BearerAuthConfig (Interface)
(no doc)
types/core.d.ts
JWTAuthConfig (Interface)
(no doc)
types/core.d.ts

Core symbols most depended-on inside this repo

scheme
called by 43
src/utils/scheme.js
json
called by 40
src/parsers/json.js
sanitize
called by 37
www/assets/js/app.js
plain
called by 37
src/renderers/plain.js
indent
called by 35
src/utils/indent.js
id
called by 34
src/utils/id.js
isEmpty
called by 33
src/utils/isEmpty.js
hasBody
called by 29
src/utils/hasBody.js

Shape

Function 191
Method 37
Interface 25
Class 10

Languages

TypeScript100%

Modules by API surface

types/core.d.ts24 symbols
benchmarks/hypermedia.js21 symbols
benchmarks/rate-limiting.js20 symbols
benchmarks/auth.js19 symbols
src/tenso.js18 symbols
benchmarks/memory.js18 symbols
www/assets/js/dom-router.min.js17 symbols
benchmarks/load-test.js14 symbols
benchmarks/renderers.js10 symbols
benchmarks/serializers.js9 symbols
benchmarks/parsers.js6 symbols
www/assets/js/app.js5 symbols

For agents

$ claude mcp add tenso \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact