Search pages, headings and code across the docs, examples and tutorials.

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 startedOpen the demo
$ npm create ohne

How it works

Simple on the inside, too.

What happens when you run your code, stack a layer, declare a collection, or ask for another language.

  1. Node strips the types from your .ts files and runs them. The dashboard gets the same files, type-stripped on request. No bundler, no dist folder.

    Getting started
  2. A layer is a package of collections, routes and messages. Stack layers under your app. The closer layer wins, and your app wins over all.

    Layers
  3. Each collection is a table, and a list of relations gets its own link table. A read costs the same few queries for two rows or two hundred.

    Collections
  4. Translated fields sit beside the record, one row per language. A read joins the language you ask for. A missing one reads null, never a guess.

    Translations
What you write is what runs The usual way builds app.ts into dist/ before it runs. With ohne, Node strips the types from app.ts and runs it directly. the usual wayapp.tsbuilddist/runohneapp.tstitle: string = 'Hi'runrunNode strips the types

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({
  api: true,
  fields: {
    title: field('text', { translatable: true, max: 120 }),
    status: field('select', { choices: ['draft', 'published'], default: 'draft' }),
    author: field('record', { collection: 'Users' }),
    body: field('text', { multiline: true }),
  },
});

Save the file and the API reloads with the table in place. No migration files, no SQL, and the sync refuses any change that would lose data. api: true serves it under /collections/posts, behind role checks.

The parts

One package, every layer of a backend.

Everything below ships in ohnejs. Stack ohnejs/uploads for files or ohnejs/ai for an assistant, or write a layer of your own.

Get started

Three steps to your first collection.

  1. 01

    Scaffold

    Answer a few prompts. You get a new project with its one dependency installed.

    $ npm create ohne
  2. 02

    Run

    Starts the API and the dashboard. Open localhost:9000 to see it. Types regenerate and the API reloads on every save.

    $ npm run dev
  3. 03

    Add a collection

    Create collections/Posts.ts and list its fields. On save, ohne creates the table, your queries know its fields, and the dashboard lists Posts. Build your first app

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.