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.
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.
Node strips the types from your
Getting started.tsfiles and runs them. The dashboard gets the same files, type-stripped on request. No bundler, nodistfolder.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.
LayersEach 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.
CollectionsTranslated fields sit beside the record, one row per language. A read joins the language you ask for. A missing one reads
Translationsnull, never a guess.
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.
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.
- 01
Scaffold
Answer a few prompts. You get a new project with its one dependency installed.
$ npm create ohne - 02
Run
Starts the API and the dashboard. Open
localhost:9000to see it. Types regenerate and the API reloads on every save.$ npm run dev - 03
Add a collection
Create
collections/Posts.tsand 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.