Module
Managing Modules
Antelopejs modules are self-contained units that implement and expose functionality through interfaces. In this guide, you'll learn how to create and configure modules.
Creating a Module
Making a new module is easy with the CLI:
ajs module init <module-name>
The CLI will ask you to pick a template and choose which interfaces you want to use.
Module Lifecycle
Here's how a module moves through its lifecycle:
The Four Phases
- Construct
- Your module gets its settings
- You set up resources and connections
- But nothing is active yet
- Start
- Connections go live
- Listeners turn on
- Your module starts working
- Stop
- Pause operations
- Keep your state intact
- Ready to start again
- Destroy
- Close all connections
- Release all resources
- Clean up completely
When hot reloading happens, the system will destroy the old version and construct the new one, letting you update code without stopping everything.
Module Structure
In Antelopejs, your business logic lives in modules. An Antelopejs project is primarily composed of an antelope.json
file and one or more modules that work together. Let's explore how modules are structured and how they connect through the project configuration.
Each module in Antelopejs follows a consistent pattern:
module-name/
├── .antelope/ # Generated automatically by the CLI
│ ├── interface.d # Contains definition files for interfaces your module imports
├── src/
│ ├── index.ts # Main entry point
│ ├── interfaces/ # Interfaces this module provides
│ │ └── 1/ # Version 1
│ │ └── index.ts
│ └── implementations/ # Implementations of the interfaces
│ │ └── 1/ # Version 1
│ │ └── index.ts
├── dist/
├── package.json
└── tsconfig.json
Source Files
Let's examine the key files in a module:
src/index.ts
This is the entry point for your module. It exports these important lifecycle functions:
export function construct(config: any): void {
// Set things up when module is loaded
}
export function start(): void {
// Start the module's functionality
}
export function stop(): void {
// Pause operation
}
export function destroy(): void {
// Clean up resources
}
These functions are called automatically by the Antelopejs runtime at appropriate times.
src/interfaces/
This directory contains the interfaces your module provides to others:
interfaces/
└── payment/ # Interface name
├── 1/ # Version 1
│ └── index.ts # Interface code
└── 2/ # Version 2
└── index.ts # Updated interface code
Each interface has its own folder with versioned implementations. These interfaces are exposed for other modules to consume. For a detailed explanation of how to design and export interfaces, see the Export Interfaces chapter.
src/implementations/
This is where you implement interfaces that you export in your src/interfaces/
directory:
implementations/
└── database/ # Interface name
└── dev/ # Version
└── index.ts # Your implementation
The implementations directory contains the actual code that fulfills the contract defined by your interfaces. For in-depth information about this pattern and how the proxy system works, refer to the Proxy System chapter.
Configuration Files
Each module has its own configuration files:
antelope.module.json
This file tells Antelopejs about your module configuration. You can configure your module in two ways:
Option 1: Separate configuration file (antelope.module.json
)
{
"imports": ["database@beta", "logger@1"],
"importsOptional": ["debug-tools@dev"],
"exportsPath": "dist/interfaces",
"baseUrl": "dist",
"paths": {
"@/*": ["*"]
},
"defaultConfig": {
"port": 3000,
"debug": false,
"timeout": 5000
}
}
Option 2: Configuration in package.json
Alternatively, you can store the same configuration in the antelopeJs
key within your package.json
:
{
"name": "payment-module",
"version": "1.0.0",
"main": "dist/index.js",
"antelopeJs": {
"imports": ["database@beta", "logger@1"],
"importsOptional": ["debug-tools@dev"],
"exportsPath": "dist/interfaces",
"baseUrl": "dist",
"paths": {
"@/*": ["*"]
},
"defaultConfig": {
"port": 3000,
"debug": false,
"timeout": 5000
}
}
}
antelope.module.json
and the antelopeJs
key in package.json
are present, the antelope.module.json
file will be used.The configuration options define:
Setting | What it does |
---|---|
imports | Lists interfaces you want to use from other modules |
importsOptional | Lists optional interfaces that won't cause errors if unavailable |
exportsPath | Where your interfaces are located |
baseUrl | Where module files are located |
paths | Path mappings for your code (like in TypeScript) |
defaultConfig | Default configuration values used when this module is added to a project |
Import Formats
Imports can be specified in two formats:
Simple string format:
{
"imports": ["api@beta", "redis@beta"]
}
Object format with additional options:
{
"imports": [
"api@beta",
{
"name": "redis@beta",
"git": "https://github.com/custom/interfaces.git",
"skipInstall": true
}
]
}
The object format supports these fields:
Field | Description |
---|---|
name | Interface name and version (required) |
git | Custom git repository URL for the interface |
skipInstall | Skip automatic installation of interface files |
Optional Imports
Optional imports are interfaces your module can use if available, but won't cause startup errors if missing. This is useful for:
- Development-only interfaces (like debugging tools)
- Feature enhancements that aren't core to your module
- Interfaces that might not be available in all environments
tsconfig.json
Standard TypeScript configuration for your module:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"moduleResolution": "node",
"esModuleInterop": true,
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": "./src",
"paths": {
"@ajs.local/*": ["interfaces/*"],
"@ajs/*": ["../.antelope/interfaces.d/*"],
"@/*": ["*"]
}
},
"include": ["src/**/*"]
}
The paths
mappings in your tsconfig.json serve important purposes:
@ajs.local/*
: Maps to your module's own interfaces, allowing you to import interfaces your module exports@ajs/*
: Maps to interfaces imported from other modules (in.antelope/interfaces.d/
)@/*
: A shorthand for importing files from your source directory
Importing Interfaces
To use features from other modules, you need to import their interfaces. Here's how:
Step 1: Tell Your Module What to Import
Add the interface to your module configuration. You can do this in either your antelope.module.json
file or in the antelopeJs
section of your package.json
:
Using antelope.module.json
:
{
"imports": ["api@beta"]
}
Using package.json
:
{
"antelopeJs": {
"imports": ["api@beta"]
}
}
You can also manage imports with these commands:
#### Add an interface import
ajs module imports add api@beta
#### List current imports
ajs module imports list
#### Remove an interface import
ajs module imports remove api@beta
For more CLI commands, check out the Module CLI guide.
Step 2: Use It in Your Code
Once configured, you can use the interface in your code like this:
// Import what you need from the API interface
import { Controller, Get, Post, HTTPResult, Parameter } from "@ajs/api/beta";
// Create a controller for users
class UsersController extends Controller("/users") {
// GET /users - List all users
@Get("/")
async listUsers() {
return {
users: [
{ id: "user1", name: "John Doe" },
{ id: "user2", name: "Jane Smith" },
],
};
}
// GET /users/:id - Get a specific user
@Get(":id")
async getUser(@Parameter("id") userId: string) {
// Return user data (simplified example)
return { id: userId, name: "John Doe" };
}
// POST /users - Create a new user
@Post("/")
async createUser() {
// Return created status with new resource
return new HTTPResult(201, { id: "new-user", name: "New User" });
}
}
Typical Module Development Workflow
When developing a module:
- Define interfaces in
src/interfaces/
if your module provides functionality to others - Create implementations in
src/implementations/
for interfaces your module export - Write your core business logic in
src/index.ts
and additional files - Build your module with TypeScript (
tsc
or your preferred build tool) - Configure it in your project's
antelope.json
(see the Project chapter for details)