0. Getting started
Status: Runnable. Everything on this page works with the packages on npm.
This chapter creates an Avi.js project with npm create avi, tours the generated files and adds a feature with the CLI.
You need Node.js 22.12 or newer. Check with node --version.
Create a project
npm create avi@latest my-api
cd my-api
npm install
npm run devListening on http://localhost:3000Open http://localhost:3000/users. npm run dev recompiles and restarts on every change.
Without a name, npm create avi@latest asks for the name and the template. There are two:
| Template | Command | For |
|---|---|---|
| modular (default) | npm create avi@latest my-api | Feature modules, decorators |
| minimal | npm create avi@latest my-api -- --template minimal | Plain routes and services |
Both run on the same engine; chapter 9 compares them.
What you get
The modular template:
my-api/
├── src/
│ ├── main.ts starts the app
│ ├── app.module.ts the root module
│ ├── common/
│ │ └── health.controller.ts GET /health
│ └── modules/users/
│ ├── users.module.ts
│ ├── users.controller.ts GET /users, GET /users/:id, POST /users
│ ├── users.service.ts
│ ├── users.test.ts
│ └── dto/
├── package.json
└── tsconfig.json| Script | What it does |
|---|---|
npm run dev | Compile, then recompile and restart on every change |
npm test | Compile and run the tests with node --test |
npm run build | Compile to dist/ |
npm start | Run the compiled app |
npm run check | Check every static inject list against its class |
Add a feature
The CLI creates files and registers them for you. One command gives you a module, a service and a controller with working CRUD routes:
npx avi g resource postshttp://localhost:3000/posts now lists, creates, updates and deletes posts, kept in memory until you connect a database.
Instead of a resource, you can create the pieces one at a time and write the routes yourself:
npx avi g module posts
npx avi g service posts
npx avi g controller postsCREATE src/modules/posts/posts.module.ts
UPDATE src/app.module.ts
CREATE src/modules/posts/posts.service.ts
UPDATE src/modules/posts/posts.module.ts
CREATE src/modules/posts/posts.controller.ts
UPDATE src/modules/posts/posts.module.tsnpm run dev picks the change up, and http://localhost:3000/posts answers []. The controller injects the service it found next to it:
@Controller('/posts')
export class PostsController {
static inject = [PostsService] as const;
constructor(private readonly posts: PostsService) {}
@Get()
findAll() {
return this.posts.findAll();
}
}static inject lists the constructor's dependencies in order. That list is how Avi.js wires classes together without reflect-metadata, and TypeScript checks it against the constructor.
Add Avi.js to an existing project
npm install @avijs/coreimport { createAvi, defineController } from '@avijs/core';
const app = createAvi().register({
controllers: [
defineController({
routes: [{ method: 'GET', path: '/', handler: () => ({ hello: 'world' }) }],
}),
],
});
await app.listen(3000);Avi.js is ESM. Use "type": "module" and "module": "NodeNext" in tsconfig.json, and leave experimentalDecorators off: Avi.js uses standard decorators.
Next
Chapter 1: Your first app walks through a small app line by line. To work on Avi.js itself, see CONTRIBUTING.md.