Skip to content

Project Structure

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:

Terminal window
npx @xeno-js/cli new my-app --core

the CLI asks which integrations you want and then generates the corresponding files.

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.json

The 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.

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.

//Example
const builder = new AppBuilder<MyRegistry>()
.addContext()
.addDb((opts, config) => {
opts.enableSqlLite = false
opts.connectionString = config.getOrThrow('DATABASE_URL');
})
export const XenoApp = await builder.build()

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.

main.ts is the generated application entry point.

It:

  1. loads environment configuration;
  2. calls bootstrap();
  3. resolves the application logger;
  4. starts the application;
  5. handles startup errors.

The generated file intentionally leaves the application-specific logic to the project.

//Example
import '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();

When you select a database during project creation, the CLI adds database-specific files.

The PostgreSQL option generates:

drizzle.config.ts
src/schema.ts

src/schema.ts contains an initial usersTable example and the generated DbSchema type.

The SQLite option also generates:

drizzle.config.ts
src/schema.ts

but 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.

The CLI creates both:

.env
.env.example

The 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.

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.

The CLI does not only write the files.

After generation it:

  1. optionally initializes a Git repository;
  2. runs npm install;
  3. prints the generated project location;
  4. shows the next commands to run.

The generated project is therefore ready to continue from the scaffold immediately after the new command completes.

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:

Terminal window
xeno-js g command CreateUser --core

or:

Terminal window
xeno-js g query FindUser --core

The generators add the corresponding CQRS application components to the project.

For the CLI commands, generation options, and available generators, see:

CLI Overview


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