Search pages, headings and code across the docs.

to navigateto open

A content backend that speaks every locale.

Declare your fields. ohne writes the admin around them - every string, select and relation translatable per locale, with nothing to keep in sync.

Get started
npm create ohne

Why ohne

Everything a web backend needs. Nothing it does not.

ohne is German for without. No bundler, no transpile step, no dependency tree to audit. Node 26 runs the framework and your app as the TypeScript you wrote.

  • Zero dependencies

    The framework has no runtime dependencies. There is no transitive tree to audit and nothing to break on the next install.

  • No build step

    ohne ships .ts source and your app is .ts source. Node strips the types and runs both. What you write is what runs.

    Getting started
  • Collections become tables

    Declare fields in one file. Every boot diffs the live schema against your code and reshapes it, refusing any change that would lose data.

    Collections and fields
  • Typed end to end

    Codegen types your routes, queries, message keys and dashboard fetches from the code you wrote. A typo is a compile error, not a runtime surprise.

    Generated types
  • Routes are files

    api/posts/[id].get.ts answers GET /posts/[id]. Return a value and it serializes. Throw notFound() and it maps to the wire.

    Routes
  • A dashboard with no bundler

    Pages are .ts modules served type-stripped to the browser. Editors get list views, block editing, translations and a media library out of the box.

    Dashboard pages
  • Two kinds of i18n

    Content locales hold one value per field per language. Message catalogs in ICU MessageFormat translate your UI, negotiated per request.

    Translations
  • Accounts, sessions, roles

    Email and password accounts, cookie sessions, and code-defined roles with capability guards. Shipped by the ohne layer; opt out by not stacking it.

    Authentication

In practice

Five files, one running app.

A collection, two routes, a hook and a dashboard page. Each is a plain TypeScript file in a convention directory, and ohne dev watches them all.

collections/Posts.ts
import { defineCollection, field } from 'ohnejs';

export default defineCollection({
  fields: {
    title: field('text', { translatable: true, max: 120 }),
    status: field('select', { choices: ['draft', 'published'], default: 'draft' }),
    author: field('record', { collection: 'Users' }),
    tags: field('records', { collection: 'Tags' }),
    body: field('text', { multiline: true }),
  },
});

Save the file and the API reloads with the table in place. No migration files, no SQL. The sync refuses any change that would lose data.

The parts

One package, every layer of a backend.

ohne stacks as layers. The layer the framework ships brings the pieces below. Add ohne/uploads for files, or write a layer of your own and ship it as a package.

  • Database

    SQLite through node:sqlite in WAL mode, with a fluent typed query builder, transactions, declarative migrations and cluster locks.

    Database guides
  • HTTP API

    File routes, request and response helpers, HTTP errors, middleware, hooks, URL queries and a REST API any collection opts into.

    API guides
  • Authentication

    A Users collection, the /auth endpoints, sessions, useUser, and code-defined roles with capability guards on routes and collections.

    Auth guides
  • Internationalization

    Message catalogs in ICU MessageFormat with per-request language negotiation, beside per-locale content in every collection.

    i18n guides
  • Dashboard

    A browser UI built from h, ref and a router, served with no build step, with an editor for every field type you declare.

    Dashboard guides
  • Uploads

    An optional layer: pluggable storage, an Uploads collection, media fields, image variants by signed URL and a Media page.

    Uploads guides
  • Layers

    Stack projects as packages. A layer ships routes, collections, messages and config, and your app overrides what it needs.

    Layers
  • The CLI

    init scaffolds, dev supervises both servers with reloads, prepare generates the types, serve runs production, sync reconciles the schema.

    The CLI

Principles

The constraint is the product.

ohne holds itself to a few rules. They are why the framework stays small and why an app built on it stays readable.

  1. 01

    Without.

    No runtime dependencies, no bundler, no transpile step. What ships is source, and what runs is what you wrote.

  2. 02

    Built on what Node ships.

    node:sqlite, node:http, node:test. The framework reaches for what Node already has and fills only the gaps.

  3. 03

    Declare once. The rest follows.

    A collection file becomes a table, a typed query surface, a REST endpoint and a dashboard editor. A route file becomes a URL. Nothing is registered twice.

  4. 04

    Loud failures, never silent ones.

    A schema change that would lose data refuses to boot and names what it would lose. An unknown field fails the write. A missing translation reads null, never a fallback you did not ask for.

  5. 05

    Typed from the code, not from a schema file.

    Codegen reads your collections, routes and catalogs and writes the types. The editor knows your field names and message keys before anything runs.

  6. 06

    Trust boundaries stay yours.

    A dependency layer cannot move your ports, point you at its database or force a destructive sync. The own keys of the config belong to your app alone.

Get started

From nothing to a running app in three steps.

  1. 01

    Scaffold

    The scaffold asks for a location, a name, a package manager and git, then writes four files and installs the one dependency.

    npm create ohne
  2. 02

    Run

    One command starts the dashboard on 9000 and the API on 9001, generates the types, and watches every file in the stack.

    npm run dev
  3. 03

    Declare

    Add collections/Posts.ts. The table exists on save, the query builder knows its fields, and the dashboard lists it. The tutorial takes it from there.

Read the guides, then build something without.

Every part of ohne has a guide: the project, the database, the HTTP API, authentication, internationalization, the dashboard and production.