Skip to content

ndulomk/ndulojs

Repository files navigation

NduloJS

An opinionated TypeScript backend framework built on Elysia.

NduloJS enforces Clean Architecture structurally — dependency injection, explicit error handling, and layered modules — so your codebase stays consistent as it grows.


Features

  • Result pattern — no try/catch. Errors are typed values: Result<T, AppError>
  • DI container — functional, type-safe, zero decorators
  • HTTP adapter — Elysia under the hood, abstracted behind a clean interface
  • Structured logger — three channels (app, http, error) with daily rotation
  • CLI — scaffold modules, submodules, and full projects in seconds

Install

# New project
npx ndulojs create my-app
cd my-app && bun install && bun dev

# Existing project
bun add @ndulojs/core

Quick start

import { createApp, createContainer, Ok, Err, ErrorFactory } from '@ndulojs/core';

const app = await createApp({ port: 3000 });

app.get('/health', () => Ok({ status: 'ok' }));

app.listen(3000);

Result pattern

Handlers return Ok(value) or Err(error). The framework maps errors to HTTP status codes automatically — no middleware needed.

app.get('/users/:id', async ({ params }) => {
  const user = await db.findById(params.id);
  if (!user) return Err(ErrorFactory.notFound('User not found', 'User', params.id));
  return Ok(user);
});
Error type Status
notFound 404
unauthorized 401
forbidden 403
validation 422
conflict 409
business 400
database 500
externalService 502
tooManyRequests 429

Dependency injection

import { createContainer } from '@ndulojs/core';

const container = createContainer()
  .register('Config',         ()  => loadConfig())
  .register('Database',       (c) => createDatabase(c.resolve('Config')))
  .register('UserRepository', (c) => createUserRepository(c.resolve('Database')))
  .register('UserService',    (c) => createUserService(c.resolve('UserRepository')));

const userService = container.resolve('UserService');
  • Singleton by default
  • Scoped and transient scopes available
  • Circular dependency detection with a clear error message

CLI

# New project
npx ndulojs create my-app

# Generate a module
ndulo generate module farms

# Generate a submodule
ndulo generate module farms --sub members

# Add a single file to an existing module
ndulo add controller farms
ndulo add service farms

generate module creates the full Clean Architecture structure:

src/modules/farms/
├── events/farm.events.ts
├── application/
│   ├── dtos/farm.dto.ts
│   ├── ports/farm.port.ts
│   └── services/farm.service.ts
└── infrastructure/
    ├── persistence/farm.repository.ts
    └── http/controllers/farm.controller.ts

Logger

import { createLogger } from '@ndulojs/core';

const logger = createLogger({ pretty: true }); // dev
const logger = createLogger({ dir: 'logs', retainDays: 30 }); // prod

logger.app.info('Server started');
logger.http.info({ method: 'GET', path: '/users', status: 200 }, 'Request');
logger.error.error({ err }, 'Unhandled exception');

// Per-request context
const log = logger.context({ requestId, userId });
log.app.info('Processing request');

Project structure

src/
├── modules/
│   └── {module}/
│       ├── events/
│       ├── application/
│       │   ├── dtos/
│       │   ├── ports/
│       │   └── services/
│       └── infrastructure/
│           ├── persistence/
│           └── http/controllers/
└── index.ts

Documentation


Contributing

See CONTRIBUTING.md.


License

MIT

About

A TypeScript backend framework that enforces good architecture from day one.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors