Project Structure

A Radiant project has a specific directory layout. This page explains what goes where and why.

Directory Layout

my-app/
├── radiant/                    # DSL files (source of truth)
│   ├── config.radiant          # Framework configuration
│   ├── collections.radiant     # Collection definitions (can be split)
│   ├── globals.radiant         # Global definitions (optional)
│   ├── schema.json             # Compiled schema (generated)
│   ├── gen.ts                  # Runtime entry point (generated)
│   └── radiant-sdk.ts          # Client SDK (generated)
├── src/                        # Your TypeScript application code
│   ├── app.ts                  # App initialization (adapter, plugins)
│   ├── index.ts                # Server entry point (starts the server)
│   ├── access.ts               # Access control rules
│   ├── hooks.ts                # Lifecycle hooks (optional)
│   ├── custom-routes.ts        # Custom API routes (optional)
│   └── cron.ts                 # Cron job definitions (optional)
├── public/                     # Static assets
│   └── index.html              # Landing page
├── radiant-types.ts            # Generated TypeScript types (don't edit)
├── .env                        # Environment variables
├── package.json
└── tsconfig.json

The radiant/ Directory

This is where your .radiant DSL files live. The compiler discovers all .radiant files recursively in this directory.

Splitting Schema Files

You can split your schema across multiple files. The compiler merges them:

radiant/
  config.radiant          # config {} block
  users.radiant           # collection users { ... }
  posts.radiant            # collection posts { ... }
  comments.radiant        # collection comments { ... }
  settings.radiant        # global siteSettings { ... }

Each file can contain any number of top-level blocks. The only constraint is that config {} should appear once across all files.

Generated Artifacts in radiant/

Generated by radiant generate alongside your .radiant files:

  • schema.json — the compiled schema as JSON
  • gen.ts — the runtime SDK entry point with createRadiant()
  • radiant-sdk.ts — the client SDK

Never edit these files — they are overwritten on every build.

The src/ Directory

Your application code — TypeScript files that import the generated runtime and add your business logic.

app.ts

The app initialization file. Creates the Radiant app instance with a database adapter:

import { createRadiant } from "../radiant/gen";
import { sqlite } from "@codesordinatestudio/radiant-plugin-sqlite";

export const app = createRadiant({
  adapter: sqlite({ url: process.env.DATABASE_URL! }),
});

index.ts

The server entry point. Imports all modules (so they register with the app) and starts the server:

import { app } from "./app";
import "./access";
import "./custom-routes";
import "./hooks";  // if you have hooks

app.router.get("/", () => {
  return new Response(Bun.file("public/index.html"));
});

app.start({ port: 3000 }).catch(console.error);

access.ts

Access control rules — per-collection permissions for read, create, update, delete:

import { app } from "./app";

app.access("todos", {
  read: (ctx) => true,
  create: (ctx) => ctx.user?.role === "admin",
  update: (ctx) => ctx.user?.id === ctx.data.author || ctx.user?.role === "admin",
  delete: (ctx) => ctx.user?.role === "admin",
});

hooks.ts

Lifecycle hooks for collections — run before/after CRUD operations:

import { app } from "./app";

app.hooks("todos", {
  beforeCreate: async (ctx) => {
    if (!ctx.data.author) ctx.data.author = ctx.user?.id || "anonymous";
    return ctx.data;
  },
  afterCreate: async (ctx) => {
    console.log("Todo created:", ctx.data.id);
  },
});

custom-routes.ts

Custom API routes beyond the auto-generated CRUD endpoints:

import { app } from "./app";
import { t } from "@codesordinatestudio/radiant-bun";

app.router.get(
  "/greeting",
  () => ({ greeting: "hello from radiant" }),
  {
    response: t.Object({
      greeting: t.String(),
    }),
  }
);

The public/ Directory

Static assets served by the Bun server. The initial radiant generate scaffold creates a landing page at public/index.html.

radiant-types.ts

Generated TypeScript types for all collections and globals. Lives at the project root so it's easy to import from src/:

import type { Todos, TodosCreate, TodosWhereClause } from "../radiant-types";

Never edit this file — it is overwritten on every build.

.env

Environment variables. Created by the initial radiant generate scaffold with:

JWT_SECRET=<auto-generated>
DATABASE_URL=file:./temp/radiant.sqlite

Loaded automatically by radiant db:sync and by the runtime.

package.json

The initial radiant generate scaffold creates a package.json with:

{
  "scripts": {
    "dev": "bun run --hot src/index.ts",
    "build": "bun build src/index.ts --outdir dist --target bun"
  },
  "dependencies": {
    "@codesordinatestudio/radiant-bun": "latest",
    "@codesordinatestudio/radiant-plugin-sqlite": "latest"
  }
}

What Gets Generated vs What You Write

FileWho Writes ItCan You Edit It?
radiant/*.radiantradiant init creates config.radiant, then you✅ Yes — this is the source of truth
radiant/schema.jsonradiant generate❌ No — generated
radiant/gen.tsradiant generate❌ No — generated
radiant/radiant-sdk.tsradiant generate❌ No — generated
radiant-types.tsradiant generate❌ No — generated
src/app.tsradiant generate (initial scaffold), then you✅ Yes
src/index.tsradiant generate (initial scaffold), then you✅ Yes
src/access.tsradiant generate (initial scaffold), then you✅ Yes
src/hooks.tsYou✅ Yes (create if needed)
src/custom-routes.tsradiant generate (initial scaffold), then you✅ Yes (create if needed)
.envradiant generate (initial scaffold), then you✅ Yes