DEV Community

Dan T.
Dan T.

Posted on

Odi - server-side framework

Introduction

Hi guys!

I am currently working on the server-side TypeScript framework. My team already widely use this framework in various e-commerce and ERP projects. Structure and semantic were inspired by other popular frameworks like Spring and ASP.NET.

List of main goals:

  1. Testable, supportable, scalable
  2. Minimalistic, idiomatic, clear
  3. Intuitive, readable, understandable
  4. Powerful


Currently, there are a lot of possibilities:

  1. Describing routing using controllers and decorators
  2. Powerful, full-typed Dependency Injection
  3. Incoming data validation (using AJV) with various set of decorators
  4. TypeORM integration (Repositories injection, Transactional support using CLS)
  5. Extendable JWT-based authentication

All parts of the framework are fully typed and integrated with the whole infrastructure.

For the best performance, under the hood, Fastify is used. The framework is declarative, but also avoids using decorators were it's possible. So, it's keep everything simple, clean and minimalistic.

Overview

Controller

Controllers serve as a simple yet powerful routing mechanism in a minimalistic style.

    @Controller('foo') 
    export class FooController extends IController {      

        @RoutePatch('{id}')     
        bar(id: string, payload: FooDTO) {         
            ...some updates..          
            return Ok();     
        } 

        @Get index() {
            return 'Foo';
        }
    } 
Enter fullscreen mode Exit fullscreen mode

So, as you see, there no need to provide any additional param decorators for injection data from the HTTP request. It's just a small controller overview, there are a lot of other possibilities.You can read more in docs.

Dependency Injection

Odi has powerful dependency injection mechanism out of the box.
(Let's imagine that we already have FooRepository)

    //foo.service.ts
    @Service()
    export class FooService {

        @Autowired()
        repository: FooRepository;

        public getFoo(id: string) {
            return this.repository.findOne(id);
        }
    }


    //foo.controller.ts
    @Controller('foo')
    export class OrderController extends IController {

        @Autowired()
        fooService: OrderService;

        @Get async '{id}' (id: string) {
            const foo = this.fooService.getFoo(id);

            if(!foo)
                return NotFound();

            return foo;
        }
    } 
Enter fullscreen mode Exit fullscreen mode

As you can see, all dependencies will be automatically provided to all application components.

Currently, Odi support 3 ways of injection:

  • By constructor
  • By property
  • By method

Classes that are not Odi components can participate in DI. You can simply define behaviour with preset properties and constructor args.


    class Pet {
        ...
    }


    define(Pet)
        .set('default', {
            constructorArgs: [...],        
            props: {...},
            type: 'singleton'
        })
        .set('special', {
            constructorArgs: [...],        
            props: {...},
            type: 'scoped'
        });

Enter fullscreen mode Exit fullscreen mode

DTO

It's a common scenario when web server should validate data before processing. DTO can optimize and automate this process.

    @Data()
    export class TodoDTO {

        @MaxLength(80)
        title: string;

        @IsOptional()
        @MaxLength(255)
        desctiption: string;

}
Enter fullscreen mode Exit fullscreen mode

Then, DTO class should be added as an argument for the controller method

    @Controller('todo')
    export class TodoController extends IController {

        @Autowired()
        todoService: TodoService;   

        @Post async index(payload: TodoDTO) {
            ...
        }
    }
Enter fullscreen mode Exit fullscreen mode

And it's all! Odi will automatically inject the validated request body in this argument. If there are some errors during validation, 400 status code will be sent with errors description.

Odi provides a wide set for DTO description, supporting nested DTOs, arrays, enums and etc.

To Sum up

It was a small overview of some features. If you interested in more, check the Docs.

Coming Soon

  1. AOP
  2. GRPC integration
  3. GraphQL
  4. CLI
  5. OpenAPI
  6. and more...

Links

GitHub logo Odi-ts / odi

🌪🌌 Opinionated, Declarative, Idiomatic framework for building scalable, supportable and reliable enterprise applications.

TypeScript framework for creating enterprise-grade (web) applications with simple and minimalistic API, that allows you to focus on business logic. Based on declarative and imperative programming, inspiried by ASP.NET / Spring.

Check Docs for more details.

Odi provides feature set for creation of easy supportable and scalable web applications.

Features Overview:

  • MVC
  • Full-typed DI / IoT
  • Authentication
  • WebSockets
  • TypeORM integration
  • GraphQL
  • AOP
  • SSR

For future updates check Roadmap
Got an idea, proposal or feature request? Feel free to Submit it!

Edit Odi

🚀 Getting Started

  1. Install npm package
    npm install odi --save

  2. Install reflect-metadata
    npm install reflect-metadata --save

  3. Import reflect-metadata (for example in index.ts):
    import "reflect-metadata";

  4. Enabled the following settings in tsconfig.json

    "emitDecoratorMetadata":  true, 
    "experimentalDecorators":  true
    Enter fullscreen mode Exit fullscreen mode

🌪 Overview

Controller

Controllers serve as a simple yet powerful routing mechanism in a minimalistic style.

@Controller('foo')
export class FooController extends IController {      
        
    @RoutePatch('{id}'
Enter fullscreen mode Exit fullscreen mode

Docs

Also, we aim to support Deno in the future.

Difference between Nestjs

Basically, there only a few common things with Nestjs: MVC Pattern and declarative style. But there are a lot of differences, like the whole infrastructure, decorators use, dependency injection and many others. To sum up differences:

  • Nestjs is heavily using decorators, Odi reduces this usage to the minimum (To keep code clean and readable).
  • Odi provides built-in validation out of the box for HTTP data via AJV.
  • DI/IoT behavior is very different. There is much more magic in Odi, in few words :)
  • Authentication out of the box, that fully integrated with other framework modules.
  • Controller and WebSockets processing

In the future, it will be the big difference between all integrations and technologies, as Odi was designed from the beginning in another way than Nest.

In my sight, Nestjs is more about Spring, but our framework is more ASP :)

P.S

From the beginning, the framework was designed as opensource. I really need your feedback, it's very important for me!

P.P.S

My small kitten Nancy asking for stars on github 😄😄

Top comments (12)

Collapse
 
triodjangopiter profile image
Pavel Ravvich

Yes, this is very interesting idea. This something like Spring clone for TS. But where spring-data. I see Spring MVC and lombok. Fool tools bunch for Postgress, MySQL, Mongo and Redis and this framework will be the brilliant.

Collapse
 
dantsk profile image
Dan T.

Hi! Currently, there are no plans to implement custom ORM. But Odi is integrated with TypeORM, and provide a wide set of possibilities. Also, Transactional methods are supported!

Collapse
 
covicake profile image
Fernando Andrés García Hernández

I can't find anything on the docs regarding that integration with TypeORM. There are three empty entries regarding 'database' (Settings, Repository, Transactions) and I can't find any example on the web either.

Can you point me in the right direction? :) Thanks

Collapse
 
tiguchi profile image
Thomas Werner

Having spent the last couple of years with Java server development this is definitely something I will look into. Just recently I started a small Node.JS API project using Express and found route setup and request payload validation really cumbersome and too verbose. Odi looks like a promising alternative!

Collapse
 
gregra81 profile image
Greg Rashkevitch

Hi Dan,
Have you considered using + contributing to Loopback - v4.loopback.io/ ?

Collapse
 
dantsk profile image
Dan T.

Hi! Thanks for your question. Loopback got a design like Nestjs, Ts.Ed, Routing-controllers. Shortly, differences between Odi and Nestjs are similar to differences between Odi and Loopback :)

Contributing will fail, as design goals of loopback is not completable with Odi goals.

Collapse
 
gregra81 profile image
Greg Rashkevitch • Edited

Well, from taking a closer at Odi and at Loopback 4, I see only advantages in Loopback 4.

  • Regarding routing - it works very similar with decorators (Odi has decorators in the controller)
  • Regarding DI - Loopback does it explicitly, like any other framework I know (like Spring), Odi does too much magic - harder to debug (and test?)
  • Regarding all the future features you plan to develop - these exist today in Loopback 4
  • Also, Loopback has an LTS support and it's backed by IBM with people that are getting paid just to maintain and support the framework

In summary - I do not see any reason to take risks and go for a new framework that has only disadvantages over Loopback 4
Take my word for it and consider moving to Loopback 4 and contributing (or any other NodeJS framework)

Thread Thread
 
dantsk profile image
Dan T.

It’s defenitily good arguments. But we have interesting things to show in the future that will differentiate odi from loopback. Anyway, it’s great to have a choice between technologies.

Collapse
 
jay97 profile image
Jamal Al

I love the idea but what's with all the decorators. I hate the syntax. It adds a little unnecessary complexity. I would just use HOF.

Collapse
 
dantsk profile image
Dan T.

Thanks a lot!

Collapse
 
reegodev profile image
Matteo Rigon

Starred as soon as I saw fastify being used as router

Collapse
 
larsklopstra profile image
Lars Klopstra ⚡

Looks sexy!