Quickstart
This walks through the smallest possible Blixis app: one service, one controller, one module, running on a real socket. It mirrors the shape of the framework’s own examples/hello-api reference app — see the hello-api Walkthrough for the full CRUD version.
1. Create the app
Section titled “1. Create the app”mkdir hello-quickstart && cd hello-quickstartpnpm initpnpm add @blixis-io/core @blixis-io/di @blixis-io/http zodpnpm add -D typescript @types/nodemkdir srcIn package.json, add "type": "module" and these scripts:
{ "type": "module", "scripts": { "build": "tsc -p tsconfig.json", "start": "node dist/main.js" }}{ "compilerOptions": { "target": "es2023", "lib": ["es2023"], "types": ["node"], "module": "nodenext", "moduleResolution": "nodenext", "strict": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "useDefineForClassFields": false, "esModuleInterop": true, "skipLibCheck": true, "outDir": "dist", "rootDir": "src" }, "include": ["src"]}Note there’s no tsx/ts-node step here: Node’s own TypeScript support strips types but doesn’t transform decorators (they’re runtime JavaScript, not type syntax), so every app in this framework builds with tsc first and runs the compiled output. See Installation for why the two decorator compiler flags are non-negotiable.
2. Write a service
Section titled “2. Write a service”import { Injectable } from "@blixis-io/di";
@Injectable()export class HelloService { greet(name: string): string { return `Hello, ${name}!`; }}@Injectable() isn’t optional decoration — TypeScript only emits the constructor’s parameter types (design:paramtypes) for a decorated class, so it’s what makes this class resolvable at all. See Dependency Injection.
3. Write a controller
Section titled “3. Write a controller”import { Controller, Get, Param } from "@blixis-io/http";import { HelloService } from "./hello.service.js";
@Controller("hello")export class HelloController { constructor(private readonly hello: HelloService) {}
@Get(":name") greet(@Param("name") name: string) { return { message: this.hello.greet(name) }; }}this.hello was never assigned by hand — the container built it from HelloService’s own @Injectable() registration. See Routing & Controllers.
4. Wire up the module
Section titled “4. Wire up the module”import { Module } from "@blixis-io/core";import { HelloController } from "./hello.controller.js";import { HelloService } from "./hello.service.js";
@Module({ providers: [HelloService], controllers: [HelloController],})export class AppModule {}5. Boot it
Section titled “5. Boot it”import { createHttpApplication } from "@blixis-io/http";import { AppModule } from "./app.module.js";
const app = await createHttpApplication(AppModule);await app.listen(3000);
console.log("Listening on http://localhost:3000");pnpm run build && pnpm start6. Try it
Section titled “6. Try it”curl http://localhost:3000/hello/world{ "message": "Hello, world!" }What just happened
Section titled “What just happened”createHttpApplication(AppModule)walked the module graph, registeredHelloServiceandHelloControllerin aContainer, resolved everything, and ran anyOnModuleInithooks (none here).app.listen(3000)bound a realnode:httpserver and translated every incoming request into a Web-standardRequest, then translated the returnedResponseback onto the socket.@Get(":name")onHelloControllercombined with the controller’s"hello"prefix to registerGET /hello/:nameon the internal router;@Param("name")pulled the:namesegment out of the matched route and handed it togreet()as a plain string.
Next steps
Section titled “Next steps”- Add request validation, a second route, and a guard: Build Your First API.
- Understand what
Injectable/Inject/tokens actually do under the hood: Dependency Injection. - See the full CRUD version of this same shape: the hello-api Walkthrough.