sparckles / Robyn

Robyn is a Super Fast Async Python Web Framework with a Rust runtime.
https://robyn.tech/
BSD 2-Clause "Simplified" License
4.26k stars 220 forks source link

include_router or register_blueprint #453

Closed mn3711698 closed 1 year ago

mn3711698 commented 1 year ago

If you are building an application or a web API, it's rarely the case that you can put everything on a single file.

When can we provide something like flask's register_blueprint or FastAPI's include_router

Noborita9 commented 1 year ago

Hii, I think this is totally necessary so I am going to start looking what it takes to add a Router-Like functionallity.

sansyrox commented 1 year ago

Hi @mn3711698 @Noborita9 ,

We already have something called views(https://sansyrox.github.io/robyn/#/features?id=views) .

So, we don't need something like a extra router or a blueprint to organise our code.

I will be happy to clarify any queries that may come up πŸ˜„

Noborita9 commented 1 year ago

I am aware of the existance of this views, but I just don't find them totally resolving the problem of separating code well. Let me explain: with views you can get one function per method and assing them to a route, which is great for restful design. But let's say I want to have them being restfull with an :id path on GET, DELETE, PATCH but not POST, how could I separe them.

That's my understanding of views, please let me know if I am not using them right or if there's something I should know. Thanks! πŸ˜„

sansyrox commented 1 year ago

@Noborita9 , you can do something like the following

@app.view("/sync/view/decorator/:id")
def sync_decorator_view():
    def get(request: Request):
        id = request.params["id"]
        return f"Hello, world! {id}"

    def post(request: Request):
        body = request.body
        return {"status_code": 200, "body": body}
Noborita9 commented 1 year ago

Okk, it's better than I thought, But I would have to do many views for one Model(it's the aproach I like the best). I really think that a router-like pattern covers more cases with a higher simplicity. Also, I took some time to watch the code( At least python-side) and I think it's possible to just extend the actual router.routes list to add more functinality.

I am not really aware of how the routes are handled by the rust implementation so I am not sure if it's possible nor easy. I would like to keep discussing this because I really like the project but I am also in love how are Flask/FastAPI organized in term of routing.

If you have some time later I would like to have a call talking about this, Because I really want to understand how it works and how can it get even better πŸ˜„

sansyrox commented 1 year ago

Okk, it's better than I thought, But I would have to do many views for one Model(it's the aproach I like the best). I really think that a router-like pattern covers more cases with a higher simplicity.

@Noborita9 , I am not sure if I understand this. What do you mean by many views for one model ? I am trying to understand the usecase we are missing. Maybe we will be able to solve it in a better way

If you have some time later I would like to have a call talking about this, Because I really want to understand how it works and how can it get even better

I will be happy to talk more about it πŸ˜„

mn3711698 commented 1 year ago

app.register_blueprint(myapi, url_prefix="/api") app.register_blueprint(apivid, url_prefix="/api")

''' start.py '''

''' myapi.py

''' from flask import Blueprint, request, jsonify from basic.showhtml import showapi

myapi = Blueprint("api", name)

@myapi.route('/template/suya', methods=['GET', 'POST', 'OPTIONS']) def api_template_suya():
return showapi(request, 'template', 'suya')

@myapi.route('/template/yipin', methods=['GET', 'POST', 'OPTIONS']) def api_template_yipin(): return showapi(request, 'template', 'yipin')

@myapi.route('/template/miaoqu', methods=['GET', 'POST', 'OPTIONS']) def api_template_miaoqu():
return showapi(request, 'template', 'miaoqu')

@myapi.route('/template/boli', methods=['GET', 'POST', 'OPTIONS']) def api_template_boli(): return showapi(request, 'template', 'boli')

@myapi.route('/login/wechat', methods=['GET', 'POST', 'OPTIONS']) def login_wechat(): return showapi(request, 'login', 'wechat')

@myapi.route('/register/wechat/detail', methods=['GET', 'POST', 'OPTIONS']) def register_wechat_detail():
return showapi(request, 'register', 'wechat_detail')

@myapi.route('/register/wechat/offiaccount', methods=['GET', 'POST', 'OPTIONS']) def register_wechat_offiaccount():
return showapi(request, 'register', 'wechat_offiaccount')

@myapi.route('/check/token', methods=['GET', 'POST', 'OPTIONS']) def check_token():
return showapi(request, 'check', 'token')

…………………… more

''' apivid.py ''' from flask import Blueprint, request, jsonify from basic.showhtml import showapi_n

apivid = Blueprint("apivid", name)

@apivid.route('/overall/anonym/trace', methods=['GET', 'POST', 'OPTIONS']) def overall_anonym_trace():
return showapi_n(request, 'overall', 'anonym_trace')

@apivid.route('/plugin/communitys/category/list', methods=['GET', 'POST', 'OPTIONS']) def plugin_communitys_category_list():
return showapi_n(request, 'communitys', 'category_list')

@apivid.route('/plugin/communitys/category/video/list', methods=['GET', 'POST', 'OPTIONS']) def plugin_communitys_category_video_list(): return showapi_n(request, 'communitys', 'category_video_list')

@apivid.route('/plugin/video/resource/list', methods=['GET', 'POST', 'OPTIONS']) def plugin_video_resource_list():
return showapi_n(request, 'video', 'resource_list')

@apivid.route('/plugin/video/resource/details', methods=['GET', 'POST', 'OPTIONS']) def plugin_video_resource_details():
return showapi_n(request, 'video', 'resource_details')

@apivid.route('/plugin/video/resource/urls', methods=['GET', 'POST', 'OPTIONS']) def plugin_video_resource_urls():
return showapi_n(request, 'video', 'resource_urls')

@apivid.route('/plugin/video/resource/buy', methods=['GET', 'POST', 'OPTIONS']) def plugin_video_resource_buy():
return showapi_n(request, 'video', 'resource_buy')

@apivid.route('/plugin/video/resource/tags/list', methods=['GET', 'POST', 'OPTIONS']) def plugin_video_resource_tags_list(): return showapi_n(request, 'video', 'tags_list')

@apivid.route('/plugin/video/resource/status', methods=['GET', 'POST', 'OPTIONS']) def plugin_video_resource_status():
return showapi_n(request, 'video', 'resource_status')

@apivid.route('/plugin/video/resource/guess/list', methods=['GET', 'POST', 'OPTIONS']) def plugin_video_resource_guess_list():
return showapi_n(request, 'video', 'resource_guess_list')

@apivid.route('/login/random/account', methods=['GET', 'POST', 'OPTIONS']) def login_random_account():
return showapi_n(request, 'login', 'random_account')

@apivid.route('/plugin/cartoon/resource/list', methods=['GET', 'POST', 'OPTIONS']) def plugin_cartoon_resource_list():
return showapi_n(request, 'cartoon', 'resource_list')

………………meor

mn3711698 commented 1 year ago

There are three py files above me. I like to put different function paths in different files for easy search or classification. In a saas project, it is necessary to distinguish different functions with different py files. Today, I tried to use robyn in a new project with only a few request paths. There are few request paths, but the amount of data is relatively large, and it accomplished my goal very well. I am looking to replace flask with robyn

sansyrox commented 1 year ago

but the amount of data is relatively large, and it accomplished my goal very well. I am looking to replace flask with robyn

@mn3711698 , firstly statements like these make my day. I love it ❀️

I had a call with @Noborita9 and he proposed a few suggestions regarding routers I would love to add it as a feature in Robyn.

Hopefully, it will be a part of the repo soon πŸ˜„

sansyrox commented 1 year ago

@Noborita9 , I was able to come up with the implementation approach for this. Would you be interested in implementing this?

Noborita9 commented 1 year ago

Sure, how do we get started with it?

sansyrox commented 1 year ago

@Noborita9 , it will be something like this

from robyn import SubRouter # this will be inheriting from a common interface to Robyn and will have the same methods as the Robyn class

router = SubRouter( prefix='/sub-router')

@router.get('/router')
def fx():
....

app.include_router(router) # this function will be mutating the main router's internal routes like you suggested in our meeting.
Noborita9 commented 1 year ago

I don't think a router should have start methods or did I misunderstood something

sansyrox commented 1 year ago

@Noborita9 , traditionally you would've been correct.

But I had this idea recently where we can allow multiple microservices to run from the same codebase and then if the developers feel they need a change, they can extract the exact codebase.

Can also change the internal network layer in the future πŸ˜‰

Noborita9 commented 1 year ago

Okk, That seems alright to me!

Noborita9 commented 1 year ago

Hi!, I made the PR for it, sorry it took me long. I was busy with school and I didn't know how to build it locally to test πŸ˜… Normal Router is working fine, I didn't test middleware and I still don't know how web_socket works so I left it behind for now