@kenzuya/nestelia
v1.11.4
Published
Modular framework with decorators, dependency injection, modules and lifecycle hooks for Elysia
Maintainers
Readme
📑 Table of Contents
🎯 What is nestelia?
nestelia brings the developer experience of NestJS to the Elysia ecosystem. Write familiar decorator-based controllers, wire up services via dependency injection, and structure your app into modules — all while running on Bun's blazing-fast HTTP engine.
If you know NestJS, you already know nestelia.
import "reflect-metadata";
import { Controller, Get, Injectable, Module, createElysiaApplication } from "@kenzuya/nestelia";
@Injectable()
class GreetingService {
greet(name: string) {
return `Hello, ${name}!`;
}
}
@Controller("/")
class AppController {
constructor(private readonly greeting: GreetingService) {}
@Get("/hello/:name")
hello(@Param("name") name: string) {
return this.greeting.greet(name);
}
}
@Module({
controllers: [AppController],
providers: [GreetingService],
})
class AppModule {}
const app = await createElysiaApplication(AppModule);
app.listen(3000, () => console.log("🦊 Listening on http://localhost:3000"));✨ Features
🏗️ Decorators
@Controller, @Get, @Post, @Body, @Param, @Query, @Headers, and the full Express-style routing surface you expect.
💉 Dependency Injection
Constructor-based DI with singleton, transient, and request scopes. Circular dependency detection and auto-wiring out of the box.
📦 Modules
Encapsulate controllers, providers, and imports. Re-use logic across apps with clean boundaries and explicit exports.
⚡ Lifecycle Hooks
OnModuleInit, OnApplicationBootstrap, OnModuleDestroy, and BeforeApplicationShutdown for deterministic startup & teardown.
🛡️ Guards, Interceptors & Pipes
Composable request pipeline. Auth guards, logging interceptors, validation pipes — all via decorators.
🌐 Exception Handling
Built-in HTTP exceptions (BadRequestException, NotFoundException, etc.) and custom @Catch() filters.
📝 Swagger / OpenAPI
Automatic OpenAPI documentation generated from decorators. Zero config for basic specs, full control when you need it.
⏰ Task Scheduling
Cron jobs, intervals, and timeouts via @Cron(), @Interval(), @Timeout() decorators.
🚀 Microservices
Redis, RabbitMQ, and TCP transports with @MessagePattern and @EventPattern handlers.
🚀 Quick Start
# 1. Create a new project
bun init -y
# 2. Install dependencies
bun add @kenzuya/nestelia elysia reflect-metadata
# 3. Enable decorators in tsconfig.json
# "experimentalDecorators": true
# "emitDecoratorMetadata": truesrc/main.ts
import "reflect-metadata";
import { Controller, Get, Module, createElysiaApplication } from "@kenzuya/nestelia";
@Controller()
class AppController {
@Get()
index() {
return { message: "Hello from nestelia!" };
}
}
@Module({
controllers: [AppController],
})
class AppModule {}
const app = await createElysiaApplication(AppModule);
app.listen(3000);
console.log("🦊 Server running at http://localhost:3000");bun run src/main.ts
# → 🦊 Server running at http://localhost:3000📦 Ecosystem
All packages are published under the single @kenzuya/nestelia package with subpath exports.
| Import | Description |
|--------|-------------|
| @kenzuya/nestelia | Core framework — DI, decorators, modules, lifecycle |
| @kenzuya/nestelia/scheduler | Cron jobs, intervals, timeouts |
| @kenzuya/nestelia/microservices | Redis, RabbitMQ, TCP transports |
| @kenzuya/nestelia/apollo | Apollo GraphQL code-first |
| @kenzuya/nestelia/passport | Passport.js authentication integration |
| @kenzuya/nestelia/testing | Isolated test modules with provider overrides |
| @kenzuya/nestelia/cache | Response caching with @CacheKey(), @CacheTTL() |
| @kenzuya/nestelia/rabbitmq | Advanced RabbitMQ messaging patterns |
| @kenzuya/nestelia/graphql-pubsub | Redis-backed PubSub for GraphQL subscriptions |
📚 Examples
Guard
import { Injectable, CanActivate, ExecutionContext } from "@kenzuya/nestelia";
@Injectable()
class AuthGuard implements CanActivate {
canActivate(ctx: ExecutionContext) {
const request = ctx.switchToHttp().getRequest();
return request.headers.authorization === "Bearer secret";
}
}
@Controller()
class AppController {
@Get("/protected")
@UseGuards(AuthGuard)
protected() {
return { secret: "🎉 You passed!" };
}
}Interceptor
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@kenzuya/nestelia";
import { map } from "rxjs/operators";
@Injectable()
class TransformInterceptor implements NestInterceptor {
intercept(_ctx: ExecutionContext, next: CallHandler) {
return next.handle().pipe(map(data => ({ data, timestamp: Date.now() })));
}
}GraphQL Subscription
import { Resolver, Query, Subscription } from "@kenzuya/nestelia/apollo";
import { PubSub } from "@kenzuya/nestelia/graphql-pubsub";
@Resolver()
class NotificationResolver {
constructor(private readonly pubsub: PubSub) {}
@Query()
notifications() {
return [];
}
@Subscription(() => String, { topics: "NEW_NOTIFICATION" })
newNotification() {
return this.pubsub.asyncIterator("NEW_NOTIFICATION");
}
}🧪 Testing
import { Test } from "@kenzuya/nestelia/testing";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
describe("AppController", () => {
let controller: AppController;
beforeAll(async () => {
const module = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
controller = module.get(AppController);
});
it("should return hello", () => {
expect(controller.hello("World")).toEqual("Hello, World!");
});
});📖 Documentation
Full guides, API references, and recipes are available at nestelia.dev.
Run the docs locally:
bun install
bun run docs:devPackage Release (Buildkite)
The Buildkite pipeline publishes @kenzuya/nestelia to
https://registry.npmjs.org using the version committed in package.json.
Releases are started manually from Buildkite for the main branch.
One-time setup
- Create a Buildkite pipeline for this repository and set its branch filter to
main. - Create an npm granular access token with read/write access to
@kenzuya/nestelia(or permission to create it under@kenzuyafor the first release). Enable Bypass 2FA for unattended CI publishing if required by the package's publishing policy. - Add
NPM_TOKENas a secured Buildkite environment variable. Do not expose it in build logs or regular pipeline configuration. - Enable Buildkite-hosted Linux agents for the pipeline. The release step pins
Bun to
1.4.2and expects Node.js/npm on the hosted agent.
Publish a release
- Update
package.jsonto an unpublished version, then commit and merge it tomain. The pipeline does not change versions or create commits or Git tags. - Open the pipeline in Buildkite, choose New Build, select
main, and start the build. This starts the release job directly; there is no second approval step. - The job verifies npm authentication and rejects an already-published version.
Only an HTTP
404from the version lookup allows it to continue; other registry errors stop the release. - Dependencies are installed with
bun install --no-save, followed by tests, type checking, and a build. If all checks pass, the package is published with public access and npm's defaultlatesttag. Use stable versions for this workflow.
Pushes, pull requests, schedules, tags, and runs on non-default branches do not
publish a release. The pipeline condition requires both a Buildkite UI trigger
and the main branch. Releases are serialized so two manual runs cannot publish
concurrently. The token configuration uses a temporary file outside the project
and is removed when the job exits.
There is currently no root Bun lockfile, so dependency versions are resolved at
release time without saving a lockfile. The package's files list includes the
build output even though dist is ignored by Git.
🤖 Claude Code Skill
A first-party Claude Code skill is available for nestelia. It provides scaffolding templates, decorator usage patterns, and framework best practices directly inside your AI assistant.
npx skills add nestelia/nestelia