typestack / routing-controllers

Create structured, declarative and beautifully organized class-based controllers with heavy decorators usage in Express / Koa using TypeScript and Routing Controllers Framework.
MIT License
4.36k stars 392 forks source link

Proposal: Using 3rd party, blazingly fast router #272

Open MichalLytek opened 6 years ago

MichalLytek commented 6 years ago

For now we rely on the routing of the underlying driver (express or koa). However express and koa routers are really slow, as you can see in benchmarks: https://github.com/delvedor/router-benchmark

find-my-way | lookup dynamic route x 2,647,488 ops/sec ±0.93%  (89 runs sampled)
koa-router  | lookup dynamic route x   296,380 ops/sec ±1.06%  (85 runs sampled)
express     | lookup dynamic route x   116,762 ops/sec ±53.99% (41 runs sampled)

As we handle the route concatention and param injection by themselves, we could perform the routing at the (nomen omen) routing-controllers level using the find-my-way router library: https://github.com/delvedor/find-my-way

We are getting close to the final API and functionality, so we should consider now also optimization and performance of the framework 😉

pleerock commented 6 years ago

Why find-my-way ? It does not look popular and future is seamless and unpredictable for this lib, there are tons similar libraries most of which are not maintained anymore. What it does inside what makes it so quick? They all use embedded node server functionality, right? Then why can't we do same (even if exactly same) what those library do?

From beginning I wanted to create separate fast-as-possible driver for routing-controllers. Not based on express koa or anything else. Native, dependency-less, ultra-fast solution for routing-controllers.

MichalLytek commented 6 years ago

Why find-my-way ? It does not look popular and future is seamless and unpredictable for this lib, there are tons similar libraries most of which are not maintained anymore.

I've just read the article about fastify framework and they are using the find-my-way. Looks like it's very fast so it makes sense to make a big refactor as we may gain nice performance improvements.

What it does inside what makes it so quick?

It use different approach than regexing path, as described in readme - it internally uses an highly performant Radix Tree (aka compact Prefix Tree). Some weird paths like /hello/:my-:world.html are not supported yet but 99% of the use cases of normal rest apis are covered.

Then why can't we do same (even if exactly same) what those library do?

I'm not an expert in tree-like algorithm, as well as in optimization 😜 If you have knowledge to create a fast router and maintain it you can do it, I'm not able to do this so I need to use 3rd party libs.

pleerock commented 6 years ago

If you have knowledge to create a fast router and maintain it you can do it,

okay maybe some day. If you can't then we can simply add new driver. But I think its better to add it fastify, not find-my-way. This will give us more advantage (and probably almost zero disadvantage) over find-my-way

MichalLytek commented 6 years ago

If you can't then we can simply add new driver.

I don't think that it's necessary. Fastify is a express-like framework with no special features, just with renamed res to reply and others small features like built-in logger, lifecycle hooks. They even maintained compatibility with express middlewares. But as you said:

It does not look popular and future is seamless and unpredictable for this lib, there are tons similar libraries most of which are not maintained anymore.

The only pros of fastify is that it's fast thanks to fast router and json serialization library that use schema - later we could think about JSON schema stringify too. I just wanted to speed up existing apps with koa or express, not to introduce third driver which would complicate developing features.

I've even done some benchmarks of the frameworks: chart

So it looks like it makes sense to think about custom router or even "dropping both Koa and Express in favour of packages like find-my-way and connect since it will give you the most flexibility in developing routing-controllers and not being reliant on the API of express or koa, or both.". But for this we would have to cover all the remaining use cases that users are now forced to use bare req/res/ctx methods.

NoNameProvided commented 6 years ago

We are getting close to the final API and functionality

There will be things to improve always ;)

It does not look popular and future is seamless and unpredictable for this lib,

@pleerock the first commit was 5 months ago, so its young thats why it's not popular. With this magnitude of difference I think it will definitely get some traction.

Some weird paths like /hello/:my-:world.html are not supported yet but 99% of the use cases of normal rest apis are covered.

@19majkel94 this looks good, but we should not add it if it makes some regression. But we should keep an eye for this it looks promising.

If you can't then we can simply add new driver. But I think its better to add it fastify, not find-my-way. This will give us more advantage

I agree with that, it clears that for fastify/fastify performance is top 1 priority. If we can switch we should switch and enjoy the free performance gains with every release. Of course for this we need some preparation and check if we wont have any regression.

pleerock commented 6 years ago

"dropping both Koa and Express in favour of packages like find-my-way and connect since it will give you the most flexibility in developing routing-controllers and not being reliant on the API of express or koa, or both."

don't even think about it. Express and koa under the hood of routing-controllers is not reasonless. With express and koa users who want to use routing-controllers will use it only because it supports thing they already know and like with all its features and middlewares. If routing-controllers go with its own (or any other non popular solution) from beginning it could not achieve even 10% of its current popularity. We can't dictate rules what to use for users yet.

If we integrate this new thing I think it should be a new driver. People who don't need express/koa middlewares and want to be extra-fast will use it. Others who needs express features/infrastructure/middlewares/community will use express driver.

MichalLytek commented 6 years ago

People who don't need express/koa middlewares and want to be extra-fast will use it.

It has compatibility with express middlewares and the api is 90% the same, so it's not at much different framework that drops compatibility.

With express and koa users who want to use routing-controllers will use it only because it supports thing they already know and like with all its features and middlewares.

But what is the difference for routing-controllers users that use decorators abstraction? They even might don't remember what driver they are using until they need to do something that can't be done in routing-controller way, so they need to inject bare req/res and do it on their own. We can provide support for connect middlewares to get access to the big base of middlewares, so we wouldn't stand aloof alone.

pleerock commented 6 years ago

They even might don't remember what driver they are using until they need to do something that can't be done in routing-controller way, so they need to inject bare req/res and do it on their own.

Don't judge by only yourself. People may use Req and Res decorators and some underlying express functionality. People in 90% percent use express middlewares.

I think this idea to use a new ultra-fast alternative to express is good, but I think it must be a separate driver. Those who don't need express functionality and want to use ultra-fast thing instead will just use this new driver.

MichalLytek commented 6 years ago

People in 90% percent use express middlewares.

And fastify/potential future version of routing-controllers could use them too as they are compliant to connect middleware spec.

So if users can use 3rd party middlewares and we fill the gaps of express methods (so all can be done with decorators, no need for req/res), the underlying driver will be transparent so we could replace it with our custom router and better integrate the logic into the stack, right?

pleerock commented 6 years ago

and we fill the gaps of express methods

but we can't cover every use case with decorators. Also we don't know how users use express in their apps. There can be lot of different usages. Maybe we can cover some of them but it seems risky to simply remove express from the lib.

Conversation in this issue and "drop koa support" overlap, so I'll repeat my concerns here again:

I understood what you want. Your goal is to bring routing-controllers into the next level. I like this goal, I would like to do it as well. However in my opinion next level is something greater then removing express in favour of fastify or some router. Next level is to have a complete standalone framework just like express or koa or fastify are. I see next level as a separate framework, not called routing-controllers, without any dependencies. It should be fast, efficient, with decorators, typescript friendly, and simply great. Im sure all together we can do it. However at the moment Im really very very busy with typeorm (thats what is real pain, not one-file koa driver) and I can't do it right now. I suggest you to add a new driver if you want to use it so much and in the future once I get rid of all planned typeorm features and issues and I'll return back to this issue and we'll do a new really great brand-new framework based on what you gonna do in this new driver.

I don't want to change concept of routing-controllers (just like its name) even in version 1.0.0. I want it to support all exist drivers and even new drivers. It should already be complete library and I don't afraid of many new issues including issues with koa, since framework is almost complete I don't think we gonna have of them.

MichalLytek commented 6 years ago

I've done second test with routing-controllers using express: chart_2

Code (click to extend): ```ts import "core-js/es7/reflect"; import { createExpressServer, JsonController, Controller, Get, Params } from "routing-controllers"; @JsonController() class PathController { @Get("/path/:one/:two") pathParamWithJson(@Params() params: any) { return { one: params.one, two: params.two, } } } @Controller() class SimpleController { @Get("/") simpleHello() { return "Hello world!"; } } const app = createExpressServer({ controllers: [PathController, SimpleController], }) app.listen(3000, () => console.log(`routing-controllers on 3000`)) ```

So we had 20% overhead now for things that doesn't involve class-validator, transforming and middlewares.

I think we should expose generic createServer function to even make drivers as extensions modules - fastify or restify doesn't have to be for now here in codebase. The usage would be simple:

import { FastifyDriver } from "fastify-routing-controllers";
import { createServer } from "routing-contollers";

const app = createServer(FastifyDriver, {
    controllers,
    midddlewares,
});

https.createServer(cert, app).listen(3000, () => console.log("routing-controllers fastify is live on 3000"));
pleerock commented 6 years ago

We have overhead not because of extra drivers we have, but because of code that makes things to work we have. We need to optimize code as we can to reduce overhead. I don't think its a good idea to extract all drivers into separate packages because it will make things much more complicated.

MichalLytek commented 6 years ago

Express and koa can be built-in drivers but we just a bit modify current code to make it work with 3rd-party drivers. To simplify the code they may use the same generic api covered under the hood by existing createExpressServer function for compatibility :wink: I will create a PR today to show you how it may looks.

We have overhead not because of extra drivers we have, but because of code that makes things to work we have

We have some overhead because of drivers abstraction - we can't do some things directly and fast as we could when working directly with node.js req/res. Of course it's not the root of 20% but one of the main factors.

github-actions[bot] commented 4 years ago

Stale issue message

iamchathu commented 2 years ago

@MichalLytek Any update on this?