Version 1.0.2, 47 packages

Backend apps in TypeScript, with the parts already decided.

Routing, validation, a database layer, a CLI and 47 packages that share one container and one config file. Typed from route params to response bodies.

$ npx @samveys/console new my-app
A real request to this server
$ curl -i http://samveys.sadata-utility.apps.kb-prod.sadata.dev/_demo/subscribe \
    -H 'content-type: application/json' \
    -d '{"name":"Ada","email":"ada@","role":"backend"}'
HTTP/1.1 422 Unprocessable Entity
content-type: application/problem+json
content-security-policy: default-src 'self'; frame-ancestors 'none'
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: no-referrer
x-request-id: fdcde48b-6e2a-4cc0-b0c0-285e53fa8cef
 
{
  "code": "SVS_E042",
  "title": "Validation failed",
  "status": 422,
  "errors": [{ "pointer": "#/body/email", "detail": "Invalid email address" }]
}

The headers and the error body above come from the framework. The route only declares a schema; Samveys rejects the bad email before the handler runs.

first-party packages
47
HTTP drivers: node, Fastify, Hono
3
database & search engines
10
eval / new Function in templates
0
Autobahn WebSocket cases passed
301

What you get without installing anything else

Each of these is part of the packages a new project starts with.

Typed routing, no regex

Literal segments, :params, optional and wildcard segments. Param names are inferred from the pattern, so a typo in params.id is a compile error, not a 500.

@samveys/http

Validation on every edge

Body, query and params schemas implement Standard Schema v1. The handler receives the parsed output type; failures become 422 Problem Details with JSON pointers.

@samveys/validator

Security headers out of the box

CSP, X-Frame-Options, Referrer-Policy and HSTS over HTTPS, a 1 MiB body limit, a 30 s handler timeout, prototype-less request data and stripped internal headers.

secure by default

Container, plugins, hooks

An IoC container with inject(), providers and a plugin lifecycle. Every feature is a plugin you register in samveys.config.ts — and every plugin can be replaced.

@samveys/core

SSE and WebSocket, zero deps

sse() streams with Last-Event-ID resume and keep-alive pings. The RFC 6455 server runs on the same port with origin allow-lists, auth and schema-validated messages.

realtime

Templates compiled to modules

Blade-like .tpl.html files with layouts, sections and <x-components> compile to ES modules. Expressions are parsed, never evaluated, and constructor access is rejected.

@samveys/template

Tests without a port

createTestApp() boots a fresh app per test with an in-process HTTP client, fake time, event fakes and hook recording — and cleans itself up through await using.

@samveys/testing

The svs CLI

Shipped by @samveys/console and installed per project. new, serve --watch, make:* generators, route:list, route:types, explain SVS_E042, check, build --docker and a REPL. Your own commands use the same defineCommand.

@samveys/console

Tracing built on diagnostics_channel

Every request runs inside samveys:http.request with OpenTelemetry-style span attributes. An APM subscribes without importing a single Samveys module.

observability

How the code looks

Seven short files from a real project. Every API here exists in version 1.0.

Route files load lazily from samveys.config.ts. Param types come from the pattern itself.

app/routes.ts
import { defineController, defineRoutes, get, post } from '@samveys/http'

const users = defineController('users', {
  prefix: '/users',
  routes: {
    show: get('/:id', ({ params }) => ({ id: params.id })),
    create: post('/', ({ response }) => response.created({ created: true })),
  },
})

export default defineRoutes((router) => {
  router.get('/ping', () => 'pong')
  router.controller(users)
})

The HTTP layer

Everything a request touches lives in a few known files. Here is what goes where, then each piece with real code.

samveys.config.ts Registers http({ port, routes, middleware, securityHeaders, … }) with every other plugin.
bin/server.ts createApp(config) → app.run() → createHttpServer(app) → server.listen(). svs serve runs this file.
app/routes.ts defineRoutes((router) => …): the route file http() loads lazily. Add more files to the routes array.
app/controllers/*.ts defineController(name, { prefix, use, routes }): named routes grouped under one prefix.
app/middleware/*.ts (ctx, next) => Response: global, group, controller or route level.

Routes and params

Patterns are literal, :param, :param? and a trailing * wildcard. No regular expressions, so a pattern always means one thing.

app/routes.ts
import { defineRoutes } from '@samveys/http'

export default defineRoutes((router) => {
  router.get('/ping', () => 'pong')                    // text/plain
  router.get('/users/:id', ({ params }) => ({ id: params.id }))
  router.get('/posts/:slug?', ({ params }) => ({ slug: params.slug ?? 'latest' }))
  router.get('/files/*path', ({ params }) => ({ path: params.path }))
  router.delete('/users/:id', () => undefined)         // 204 No Content
})
  • params.id is typed from the pattern; params.nope is a compile error.
  • Static routes match before params, params before wildcards.
  • A regex pattern is SVS_E045, a duplicate method + pattern is SVS_E046, both at boot.

This server’s routes, live

Read from router.list(): the same router svs route:list prints. Add a route and it shows up here.

MethodPatternParamsValidated
GET / — —
GET /docs — —
GET /docs/:section/:slug section, slug —
GET /api — —
GET /api/:package package —
GET /ecosystem — —
GET /benchmarks — —
GET /support — —
GET /assets/:file file —
POST /_demo/subscribe — body
GET /_demo/stream — —
GET /_demo/explain — query
GET /ping — —
GET /health/live — —
GET /health/ready — —

Errors are Problem Details

Every framework error is application/problem+json (RFC 9457) with a stable code and the requestId.

StatusCodeWhen
404 SVS_E040 No route matches the path.
405 SVS_E041 The path exists for other methods; Allow lists them.
422 SVS_E042 params, query or body failed its schema.
413 SVS_E043 The body is over bodyLimit (1 MiB by default).
400 SVS_E047 Unparseable body, unsafe param (e.g. encoded ../) or redirect.
415 SVS_E048 A body schema got a content type it cannot parse.
503 SVS_E044 The handler ran past requestTimeout; ctx.signal aborted.
500 SVS_E049 An unhandled error; details only with debug on.
500 SVS_E050 Handler output failed the response schema.

The svs command

21 commands built in, 23 more from packages, and your own written with the same defineCommand().

pnpm svs about
$ pnpm svs about
property      value
------------  ---------------
Application   samveys-landing
Environment   local
Debug         true
API Version   1.0.0
Node Version  v26.13.0
Plugins       4
Bindings      10

Real output of svs in this project. Add --json to any command for one machine-readable document.

Built-in commands

21 commands

Project

3
  • svs new <name> [--kit=api] [--dir <dir>] Create a project from a starter kit.
  • svs add <package> [--dry-run] [--yes] Install a package and apply its recipe to samveys.config.ts.
  • svs upgrade --to <version> [--dry-run] Move @samveys/* to a new major and run the codemods.

Run and ship

3
  • svs serve [--watch] [--entry <file>] Run bin/server.ts from .ts with .env loaded; --watch restarts on change.
  • svs build [--out dist] [--docker] [--sea] Compile to plain JS in dist/; --docker writes a Dockerfile, --sea one binary.
  • svs start [--permission --allow-write storage] Run the production build, optionally under the Node permission model.

Generators

6
  • svs make:controller <Name> app/controllers/<name>.ts and its test.
  • svs make:middleware <name> app/middleware/<name>.ts and its test.
  • svs make:command <name:part> app/commands/<name>.ts and its test.
  • svs make:provider <Name> app/providers/<name>.ts and its test.
  • svs make:test <name> tests/<name>.test.ts booting the app with createTestApp.
  • svs make:plugin <name> [--dir <dir>] A publishable plugin package skeleton.

Inspect and debug

9
  • svs route:list [--json] Every mounted route with its name and middleware.
  • svs route:types Write .samveys/types/routes.d.ts for typed route().
  • svs about Environment, versions, plugin and binding counts.
  • svs plugin:list Plugins in boot order with hooks, extensions and bindings.
  • svs config:show [path] Resolved configuration with secrets redacted.
  • svs env:check Validate environment variables against their definitions.
  • svs check [--deploy] [--skip <id>] System and deployment checks (APP_KEY, debug, database…).
  • svs explain <SVS_Exxx> What an error code means and how to fix it.
  • svs repl A REPL with the booted app, container and db.

Added by packages

23 commands

Register the plugin in samveys.config.ts and its commands appear in svs --help.

PackageCommands
@samveys/database migratemigrate:rollbackmigrate:statusdb:seeddb:wipedb:import-laravel
@samveys/orm make:modelmake:migrationmodel:showmodel:prune
@samveys/queue queue:workqueue:failedqueue:retry
@samveys/scheduler schedule:runschedule:list
@samveys/openapi generate:openapigenerate:clientopenapi:diff
@samveys/env-crypt env:encryptenv:decrypt
@samveys/auth make:auth
@samveys/inspector inspector:prune
@samveys/mcp mcp

Your own commands

Write your own command

app/commands/report-daily.ts
import { defineCommand } from '@samveys/console'

export const reportDailyCommand = defineCommand('report:daily', {
  description: 'Print yesterday’s signups',
  args: [{ name: 'team', description: 'Team slug', required: false }],
  flags: {
    days: { type: 'number', description: 'How many days back', default: 1 },
    dry: { type: 'boolean', description: 'Do not send the email', short: 'd' },
  },
  async run({ args, flags, output, app }) {
    const booted = await app()                 // boots samveys.config.ts only when asked
    const db = booted.container.make('db')     // the same binding request handlers use
    const since = new Date(Date.now() - flags.days * 86_400_000).toISOString()
    const signups = await db.from('users').where('created_at', '>', since).count()
    output.table([{ team: args.team ?? 'all', days: flags.days, signups }])
    output.result({ signups })                 // what --json prints
    return 0                                   // exit code
  },
})
  • svs make:command report:daily writes this file and a test.
  • Names are lowercase parts joined by ":" (SVS_E061 otherwise).
  • output.table() / success() / warn() / error() / result(); --json prints one JSON document.

Live: svs explain inside an HTTP request

GET /_demo/explain?code=… runs createKernel().run(['explain', code, '--json']) from @samveys/console in this server process. The code is validated by a query schema first.

svs explain
$ svs explain SVS_E044 --json
// press Run

One app behind both

bin/server.ts serves requests and svs runs commands, but both boot the same samveys.config.ts.

pnpm devsvs serve → bin/server.ts
createHttpServer(app)listens, runs the kernel per request
samveys.config.ts createApp → register → boot
pluginscontainerrouteshooks
pnpm svs <command>createKernel().run(argv)
await app()boots once, shuts down after

One config

svs and bin/server.ts both load samveys.config.ts and boot the same plugins.

Same routes

http() mounts routes on app:booted, so svs route:list shows exactly what the server serves.

Same container

A command’s app() and a request handler resolve the same bindings: db, cache, mail…

Plugins add both

One plugin can add routes, middleware, ctx.* fields and svs commands at once.

The life of a request

Every request takes the same path through the kernel, whichever driver carries it: node:http, Fastify or Hono.

  1. Request

    Internal x-samveys-* and x-middleware-subrequest headers are stripped; a request id is assigned.

  2. http:request hook

    Plugins may intercept and short-circuit with their own Response before routing.

  3. Router

    Static routes before params before wildcards. No match → 404 SVS_E040.

  4. Middleware

    Global → group → controller → route, then unwound in reverse after next() resolves.

  5. Validation

    params, query and body are checked together; all issues come back in one 422.

  6. Handler

    Runs with ctx.signal, aborted on client disconnect or timeout (503 SVS_E044).

  7. Response schema

    Output is validated and unknown keys stripped; invalid output is 500 SVS_E050.

  8. Finalize

    Security headers merged, then zstd → brotli → gzip compression, streamed, never buffered.

Try it against this server

The form and the stream below call routes on the server that rendered this page.

Validation → Problem Details

POST /_demo/subscribe with a schema.object() body. Leave a field empty or type a bad email to get a 422.

No request yet
// the response body appears here

Server-Sent Events

GET /_demo/stream streams this process’s metrics once a second through sse(), with event ids for resume.

uptime
—
RSS
—
heap
—
event id
—
Served by the fastify driver on Node v26.13.0
event: metrics …

The rest of the site

  • Docs

    Guides, tutorial and every package README, in one place.

  • API

    Every public export of every package, with its signature.

  • Ecosystem

    All packages, their versions and the commands they add.

  • Benchmarks

    Throughput and latency, measured with a published method.

  • Support

    Versions, security reports, troubleshooting and contacts.

From an empty laptop to a running API

Nothing installed yet? Start at step 1. Every step says where to run it.

What is svs, exactly?

svs is not a program you already have. It is the command that ships inside the npm package @samveys/console. On a fresh machine, typing svs gives “command not found”. Get it through npx @samveys/console to create a project; after pnpm install every project has its own copy in node_modules/.bin, so you run it as pnpm svs ….

  1. Check Node and pnpm

    once per machine
    $ node -v && pnpm -v

    Node must be 26.13.0 or newer, pnpm 12. No pnpm? Run npm i -g pnpm@12 (Node 26 no longer bundles Corepack).

  2. Point @samveys/* at its registry

    once per machine
    $ npm config set @samveys:registry https://registry.sadata.dev/api/packages/sadata/npm/

    Adds one line to ~/.npmrc. The registry is readable without a token. Skip this and npx answers 404, because @samveys/* is not on the public npm registry.

  3. Create the project

    any folder
    $ npx @samveys/console new my-app

    There is no global svs on a fresh machine. npx downloads @samveys/console, the package that ships the svs command, and runs its new command once.

  4. Install and test

    inside my-app/
    $ cd my-app && pnpm install && pnpm test

    Installs @samveys/console as a devDependency. From here on, svs lives in node_modules/.bin of this project.

  5. Run the dev server

    inside my-app/
    $ pnpm dev

    Same as pnpm svs serve --watch: loads .env, sets APP_ENV=local, restarts on file changes. Open http://localhost:3000/ping → pong.

  6. Ship it

    inside my-app/
    $ pnpm svs build --docker

    dist/ plus a multi-stage, non-root Dockerfile. Configuration stays in the environment, never in the build.

How to call svs, depending on where you are

Where you areRunExample
Outside a project (no svs yet)npx @samveys/console <command>npx @samveys/console new my-app
Inside a projectpnpm svs <command>pnpm svs make:model Note --migration
Inside a project, with npmnpx svs <command>npx svs route:list
Bare svs everywhere (optional)npm i -g @samveys/consolesvs new my-app

npx answers 404? Step 2 is missing: @samveys/* is not on the public npm registry, only on https://registry.sadata.dev/api/packages/sadata/npm/.

Common questions

I typed svs and got “command not found”. Why?

svs is not a global program; it is the bin of the @samveys/console package. Outside a project, run it through npx @samveys/console …. Inside a project it is a devDependency, so use pnpm svs … or npx svs …. If npx says 404, run the one-time npm config set @samveys:registry step first. Want a bare svs everywhere? npm i -g @samveys/console.

Which Node.js version do I need?

Node.js 26 LTS (26.13.0 or newer). There are no polyfills or compatibility paths; devEngines makes installs fail on older versions on purpose.

Do I have to compile TypeScript during development?

No. Development runs .ts files directly on Node 26 with erasable syntax only. svs build emits dist/ when you deploy.

Can I keep Fastify or Hono?

Yes. @samveys/fastify and @samveys/hono are HTTP drivers: they carry the transport while the Samveys kernel still owns routing, middleware and errors. All three drivers pass the same 29-case conformance suite.

How safe are the templates?

Templates are compiled to ES modules and loaded with import(). Expressions are a parsed subset — no assignments, no new, no functions — and constructor, prototype and __* access is refused at compile time and at run time.

How do I add an optional package?

Install it, then register its plugin in samveys.config.ts. svs add ships recipes for http, validator and testing today.

Who can use Samveys?

It is proprietary and internal: employees and contractors of PT Teknologi Sada Indonesia, for the organization’s work only.

Start a project

One command creates the folders, the config, a test and a Dockerfile.

$ npx @samveys/console new my-app