Project Structure
Introduction
Section titled “Introduction”When you create a project with the Xeno CLI, the CLI generates a concrete starting project rather than an abstract architecture template.
For a Core project:
npx @xeno-js/cli new my-app --corethe CLI asks which integrations you want and then generates the corresponding files.
A new Core project
Section titled “A new Core project”With the default Core scaffold and no database selected, the project starts approximately like this:
my-app/├── src/│ ├── bootstrap.ts│ ├── main.ts│ └── registry.ts├── .env├── .env.example├── .gitignore├── package.json├── readme└── tsconfig.jsonThe exact project changes according to the options selected during creation.
If a database is enabled, for example, the CLI also generates:
my-app/├── src/│ ├── bootstrap.ts│ ├── main.ts│ ├── registry.ts│ └── schema.ts├── drizzle.config.ts└── ...The current Core scaffold supports PostgreSQL through Drizzle and SQLite/libSQL.
src/bootstrap.ts
Section titled “src/bootstrap.ts”bootstrap.ts is the composition root generated by the CLI.
It creates an AppBuilder, configures the selected Xeno modules, and builds the application container.
The generated file can include:
- request context;
- middleware configuration;
- application pipelines;
- database configuration;
- cache configuration;
- authentication;
- logging;
- custom service registration.
The selected CLI options determine which parts are added to the builder.
For example, a project with a database selected receives the corresponding .addDb(...) configuration.
//Exampleconst builder = new AppBuilder<MyRegistry>() .addContext() .addDb((opts, config) => { opts.enableSqlLite = false opts.connectionString = config.getOrThrow('DATABASE_URL'); })
export const XenoApp = await builder.build()src/registry.ts
Section titled “src/registry.ts”registry.ts contains the application’s Xeno registry type.
The generated registry extends XenoRegistry and, when a database is selected, can also include the generated database schema type.
It is the place where application-specific injection tokens can be declared.
For example:
import { DbSchema } from './schema'export interface MyRegistry extends XenoRegistry<DbSchema> { USER_REPOSITORY: IUserRepository}The CLI generates the registry itself; application-specific contracts are added as the project grows.
src/main.ts
Section titled “src/main.ts”main.ts is the generated application entry point.
It:
- loads environment configuration;
- calls
bootstrap(); - resolves the application logger;
- starts the application;
- handles startup errors.
The generated file intentionally leaves the application-specific logic to the project.
//Exampleimport 'dotenv/config';import { XenoApp } from './bootstrap';import { TOKENS } from '@xeno-js/core';
/** * Main Application Entry Point */async function main() { try { console.info('⏳ Bootstrapping my-xeno-app application...');
// Initialize the dependency injection container and infrastructure modules const container = await XenoApp();
const logger = container.resolve(TOKENS.LOGGER) logger.info('✅ Application started successfully!');
// TODO: Implement your logic here
} catch (error) { console.error('❌ Critical error during startup:', error); process.exit(1); }}
main();Database files
Section titled “Database files”When you select a database during project creation, the CLI adds database-specific files.
PostgreSQL
Section titled “PostgreSQL”The PostgreSQL option generates:
drizzle.config.tssrc/schema.tssrc/schema.ts contains an initial usersTable example and the generated DbSchema type.
SQLite
Section titled “SQLite”The SQLite option also generates:
drizzle.config.tssrc/schema.tsbut uses the SQLite Drizzle primitives and a SqliteSchema type.
The schema is a starting point that you can replace or extend with the tables required by your application.
Environment files
Section titled “Environment files”The CLI creates both:
.env.env.exampleThe generated variables depend on the selected integrations.
For example, a project using PostgreSQL receives DATABASE_URL, while a project using Redis receives the corresponding Redis configuration variables.
.env is ignored by the generated .gitignore, while .env.example is kept as a reference for the required configuration.
package.json
Section titled “package.json”The CLI generates the project’s package.json.
The base Core project includes:
@xeno-js/core;dotenv;- TypeScript;
tsx.
Optional dependencies are added according to the choices made during scaffolding.
For example:
- Drizzle and database packages when a database is selected;
- Axios when HTTP client support is selected;
- Cockatiel when resilience support is selected;
- Pino or Sentry for logging and observability;
- Redis support;
- Supabase authentication;
- Zod validation.
The generated project also includes scripts for starting and building the application, with database scripts added when database support is enabled.
What happens after generation
Section titled “What happens after generation”The CLI does not only write the files.
After generation it:
- optionally initializes a Git repository;
- runs
npm install; - prints the generated project location;
- shows the next commands to run.
The generated project is therefore ready to continue from the scaffold immediately after the new command completes.
Adding application code
Section titled “Adding application code”The initial project is intentionally small.
The CLI does not generate a complete business domain or a large hierarchy of empty folders.
You start with the composition root and configuration files, then add application components as needed.
For example:
xeno-js g command CreateUser --coreor:
xeno-js g query FindUser --coreThe generators add the corresponding CQRS application components to the project.
CLI documentation
Section titled “CLI documentation”For the CLI commands, generation options, and available generators, see:
Support Us
Section titled “Support Us”Xeno.JS is an MIT-licensed open source project. It can grow thanks to the support of these awesome people. If you’d like to join them, please read more at support section
