Getting Started

Project Structure

Understand the standard directory layout and organization of an AxilJS application.

6 min readDocumentationEdit this page

Directory layout

An AxilJS project uses a predictable structure that separates application configuration, modules, database migrations, and background jobs.

text
my-api/
├── src/
│   ├── main.ts
│   ├── config/
│   │   └── app.config.ts
│   ├── modules/
│   │   └── users/
│   │       ├── controllers/
│   │       │   └── users.controller.ts
│   │       ├── services/
│   │       │   └── users.service.ts
│   │       ├── entities/
│   │       │   └── user.entity.ts
│   │       └── users.test.ts
│   ├── migrations/
│   │   └── 001_create_users.ts
│   └── jobs/
├── package.json
├── tsconfig.json
├── axil.lock
├── .env
├── .env.example
└── .gitignore

Note

The structure is a starting point rather than a restriction. AxilJS supports modular applications, MVC applications, monoliths, and distributed service architectures.

Source directory

The src/ directory contains the application source code.

text
src/
├── main.ts
├── config/
├── modules/
├── migrations/
└── jobs/

src/main.ts

main.ts is the application entry point. It creates the AxilJS application and starts the HTTP server.

typescript
import { Application } from '@axiljs/core'
import { config } from './config/app.config'
 
const app = new Application({
  server: {
    port: config.PORT
  }
})
 
app.listen()

For applications that require middleware and additional configuration, the entry point can initialize those services before calling listen().

Configuration

src/config/app.config.ts

Application configuration can be centralized in the config/ directory.

typescript
import { loadEnv, defineConfig } from '@axiljs/config'
 
loadEnv()
 
export const config = defineConfig({
  PORT: {
    type: 'number',
    default: 3000
  },
 
  HOST: {
    type: 'string',
    default: '127.0.0.1'
  },
 
  NODE_ENV: {
    type: 'string',
    default: 'development'
  },
 
  JWT_SECRET: {
    type: 'string',
    required: true
  }
})

Using defineConfig() gives application settings a consistent configuration boundary and allows environment values to be validated before the application starts.

Modules

The modules/ directory contains application features.

For example, a users module can be organized as:

text
src/modules/users/
├── controllers/
│   └── users.controller.ts
├── services/
│   └── users.service.ts
├── entities/
│   └── user.entity.ts
└── users.test.ts

Each layer has a specific responsibility.

DirectoryResponsibility
controllers/HTTP request handling and route logic
services/Business logic and application operations
entities/Database entities and domain models
*.test.tsTests for the module

This structure keeps HTTP concerns separate from business logic and persistence concerns.

Controllers

Controllers handle incoming HTTP requests and return responses.

typescript
import { Request, Response } from '@axiljs/core'
 
export function getUser(
  req: Request,
  res: Response
) {
  res.success(
    { id: req.params.id },
    'User fetched'
  )
}
typescript
res.HTTP_200_OK(users, 'Users fetched');
 
res.HTTP_201_CREATED(user, 'User created');
 
res.HTTP_400_BAD_REQUEST('Invalid request');
 
res.HTTP_401_UNAUTHORIZED('Invalid token');
 
res.HTTP_403_FORBIDDEN('Permission denied');
 
res.HTTP_404_NOT_FOUND('User not found');
 
res.HTTP_409_CONFLICT('Email already exists');
 
res.HTTP_500_INTERNAL_SERVER_ERROR('Database error');
 
res.HTTP_503_SERVICE_UNAVAILABLE('Service unavailable');
 
res.HTTP_204_NO_CONTENT();
 
res.HTTP_405_METHOD_NOT_ALLOWED();
 
res.HTTP_406_NOT_ACCEPTABLE();
 
res.HTTP_415_UNSUPPORTED_MEDIA_TYPE();
 
res.HTTP_429_TOO_MANY_REQUESTS();
 
res.HTTP_422_UNPROCESSABLE_ENTITY();

Controllers should remain focused on transport-level concerns. Business operations can be delegated to services.

Services

Services contain reusable application and business logic.

typescript
export class UserService {
  async findById(id: number) {
    // Database or business operation
  }
 
  async create(data: {
    name: string
    email: string
  }) {
    // Create user
  }
}

Keeping business logic in services makes it easier to reuse the same operations from HTTP handlers, jobs, event handlers, or other application modules.

Entities

Entities represent database-backed application models.

typescript
import {
  Entity,
  Column,
  PrimaryKey
} from '@axiljs/orm'
 
@Entity('users')
export class User {
  @PrimaryKey()
  id: number
 
  @Column({
    type: 'string',
    length: 255
  })
  name: string
 
  @Column({
    type: 'string',
    unique: true
  })
  email: string
}

The exact entity organization depends on how your application uses @axiljs/orm.

Migrations

Database migrations live under:

text
src/migrations/
└── 001_create_users.ts

A migration defines a database schema change and its rollback operation.

typescript
import { Migration } from '@axiljs/orm'
 
const migration: Migration = {
  async up(db) {
    await db.query(`
      CREATE TABLE users (
        id SERIAL PRIMARY KEY,
        name VARCHAR(255) NOT NULL,
        email VARCHAR(255) UNIQUE NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      )
    `)
  },
 
  async down(db) {
    await db.query(
      'DROP TABLE IF EXISTS users'
    )
  }
}
 
module.exports = migration

AxilJS ORM migrations are designed to work with the supported database drivers and their corresponding SQL syntax.

Jobs

The jobs/ directory is intended for background work and scheduled application tasks.

text
src/jobs/
├── send-welcome-email.ts
├── cleanup-expired-data.ts
└── generate-daily-report.ts

Jobs can be connected to the AxilJS queue and scheduler capabilities when background processing or scheduled execution is required.

Environment files

.env

Local environment-specific values belong in .env.

env
PORT=3000
HOST=127.0.0.1
NODE_ENV=development
 
JWT_SECRET=your-secret-minimum-32-characters
 
DATABASE_URL=postgres://user:password@localhost:5432/mydb

.env.example

The .env.example file documents the environment variables required by the application without exposing real credentials.

env
PORT=3000
HOST=127.0.0.1
NODE_ENV=development
 
JWT_SECRET=replace-with-a-secure-secret
 
DATABASE_URL=postgres://user:password@localhost:5432/mydb

Warning

Never commit .env files containing secrets to version control. Commit .env.example with placeholder values instead.

package.json

package.json defines the project's dependencies, metadata, and npm scripts.

A typical project may include:

json
{
  "name": "my-api",
  "private": true,
  "scripts": {
    "dev": "axil run",
    "build": "axil build",
    "test": "axil test"
  }
}

The exact generated configuration may vary with the AxilJS project setup.

tsconfig.json

AxilJS applications use TypeScript. The tsconfig.json file controls TypeScript compilation.

json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "CommonJS",
    "strict": true,
    "esModuleInterop": true
  }
}

Use the generated configuration as the baseline for an AxilJS project and extend it only when your application requires additional compiler settings.

axil.lock

The axil.lock file records AxilJS package-management information used by the project.

Keep this file under version control so project installations remain consistent.

.gitignore

The generated .gitignore should exclude local and generated files such as:

gitignore
node_modules/
dist/
.env
*.log

Do not ignore source files, migrations, or other project files that are required to build and deploy the application.

Scaling the structure

As an application grows, modules can be expanded independently:

text
src/
├── config/
├── modules/
│   ├── auth/
│   ├── users/
│   ├── products/
│   ├── orders/
│   └── payments/
├── migrations/
├── jobs/
└── main.ts

This structure works well for modular monoliths and can also provide a clear boundary for extracting services later.

Tip

Keep application code organized around business capabilities rather than allowing controllers/, services/, and database code to become unrelated global directories.

Next steps

Help improve the documentation

AxilJS is open source and documentation improvements are welcome.

AxilJS DocumentationMIT License · Built by SyntaxilitY