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 JSONgen.ts— the runtime SDK entry point withcreateRadiant()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
| File | Who Writes It | Can You Edit It? |
|---|---|---|
radiant/*.radiant | radiant init creates config.radiant, then you | ✅ Yes — this is the source of truth |
radiant/schema.json | radiant generate | ❌ No — generated |
radiant/gen.ts | radiant generate | ❌ No — generated |
radiant/radiant-sdk.ts | radiant generate | ❌ No — generated |
radiant-types.ts | radiant generate | ❌ No — generated |
src/app.ts | radiant generate (initial scaffold), then you | ✅ Yes |
src/index.ts | radiant generate (initial scaffold), then you | ✅ Yes |
src/access.ts | radiant generate (initial scaffold), then you | ✅ Yes |
src/hooks.ts | You | ✅ Yes (create if needed) |
src/custom-routes.ts | radiant generate (initial scaffold), then you | ✅ Yes (create if needed) |
.env | radiant generate (initial scaffold), then you | ✅ Yes |
Related
- CLI Reference — Commands that create and build this structure
- CLI Reference — What the generated files contain
- Database Sync — How
schema.jsondrives database changes