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

to navigateto open
Start here

Directory structure

Where each kind of file lives, and what ohne does with it.

In ohne, where a file sits decides what it is. A file in collections/ is a collection, a file in api/ is a route, and a file in dashboard/pages/ is a dashboard page. You never register or import these files. ohne finds them by their location.

A new project has none of these directories. You create each one the first time you need it. A project that uses all of them looks like this:

my-app/
├── api/                  HTTP routes
├── blocks/               reusable content shapes
├── boot/                 code that runs once at startup
├── collections/          your data model
├── commands/             your own ohne commands
├── dashboard/
│   ├── boot/             code that runs once when the dashboard loads
│   ├── components/       UI code your pages share
│   ├── pages/            dashboard pages
│   └── tsconfig.json
├── fields/               custom field types
├── messages/             translatable strings
├── middleware/
│   └── global/           middleware that runs on every request
├── migrations/           data moves for schema changes
├── roles/                who may do what
├── skills/               what the assistant can do on request
├── flows/                how the assistant routes a request
├── .data/                the local database
├── .ohne/                generated types
├── .env                  local environment variables
├── ohne.config.ts
├── package.json
└── tsconfig.json

The rest of this page walks through them, one part of the app at a time.

Data

  • collections/ - one file per collection. The file names the collection, so collections/Posts.ts is Posts, and ohne creates its table for you.
  • fields/ - custom field types. fields/sku.ts defines a sku type, which any collection uses as field('sku').
  • blocks/ - blocks, reusable content shapes like a hero or a quote. blocks/Hero.ts defines Hero.
  • migrations/ - migrations. Each file describes one change that has to carry data along, like renaming a field.

API

  • api/ - routes. The file's path is the URL, and a suffix sets the method: api/posts.get.ts is GET /posts.
  • middleware/ - middleware, functions that run before a route's handler. A file under middleware/global/ runs on every request. Any other file runs only on the routes that select it.

Dashboard

Everything under dashboard/ is browser code. ohne serves it to the browser as it is, so treat the whole directory as public. Server code lives outside it, and the two talk over HTTP.

  • dashboard/pages/ - pages. The file's path is the route: dashboard/pages/posts.ts is /posts.
  • dashboard/boot/ - dashboard boot files. Each one runs once in the browser, before the first page renders.
  • dashboard/components/ - the UI code your pages share. This one is only a habit. ohne reads pages/ and boot/, and the rest of dashboard/ is yours to arrange. Pages import it through the app/ prefix.
  • dashboard/tsconfig.json - type-checks the browser code against DOM types, apart from your Node code.

Across the app

  • boot/ - boot files. Each one runs once at startup, before the server opens. This is where you register hooks.
  • roles/ - roles, one per file. roles/editor.ts is the editor role, a named set of permissions you give to users.
  • skills/ - skills, one per file. skills/translate-items.ts is the translate-items skill, a prompt the dashboard assistant runs when someone asks for it.
  • flows/ - flows, one per file. flows/raid-officer.ts is the raid-officer flow, a graph the assistant walks: it decides what a request needs, then runs the matching skill or prompt.
  • commands/ - commands, one per file. commands/seed.ts adds ohne seed, for work outside a request.
  • messages/ - message catalogs, the strings your app translates. One JSON file per language: messages/en.json, messages/de.json.

Files ohne writes

You do not create these, and the scaffold's .gitignore keeps them out of git:

  • .ohne/ - the types ohne generates from your project. They are what makes your queries, routes, and messages typed in the editor. You never edit or import them.
  • .data/ - the local database, a SQLite file that ohne creates on the first boot.

.env is also ignored, but you write that one yourself. It holds local environment variables, such as secrets.

The files at the root are covered in Installation.

Renaming a directory

The names above are defaults. If you would rather keep your routes in routes/, set dirs in your config:

ohne.config.ts
import { defineConfig } from 'ohnejs';

export default defineConfig({
  layers: ['ohnejs/base'],
  dirs: {
    api: 'routes',
  },
});

Layers share the structure

A layer is an ohne project you build on, and it uses these same directories. The ohnejs/base layer in your config has its own collections/, api/, and dashboard/, which is where your app's users, sign-in, and dashboard come from.

When your project and a layer define the same thing, like a route with the same URL or a collection with the same name, yours wins.

Next, Your first app fills some of these directories: a collection, two routes, and a dashboard page.