responder
responder copied to clipboard
Need more example code
Hello,
is it possible to get more examples for using awesome responder? Maybe best practices for converting existing Flask often used method calls and alike. Thanks!
Mark
I'd be interested in a example file-structure of a project using this framework
Here's an example for how we are currently structuring a small service were I work (to be deployed as a docker container to kubernetes):
<project_root>/
├── service/
│ ├── apis/
│ │ ├── __init__.py
│ │ ├── <resource1>.py
│ │ └── <resource2>.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── <resource1>.py
│ │ └── <resource2>.py
│ └── app.py
├── test/
│ ├── __init__.py
│ ├── <resource1>.py
│ └── <resource2>.py
├── .env
├── .gitignore
├── Dockerfile
├── k8s-deployment.yaml
├── Pipfile
├── Pipfile.lock
└── README.md
The <resourceX>.py
files in service/apis
contain the class based views implementing the different GET, POST, PUT, etc... endpoints for the different resources the service is concerned with.
The corresponding files in service/models
contain the marshmallow schemas to go with that.
In service/app.py
this is then tied together like this:
import responder
from service.apis.resource1 import Resource1View
from service.apis.resource2 import Resource2View
from service.models.resource1 import Resource1Schema
from service.models.resource2 import Resource2Schema
api = responder.API(
title="My Service",
version="1.0",
openapi="3.0.0",
docs_route='/docs',
)
api.add_schema('Resource1', Resource1Schema)
api.add_schema('Resource2', Resource2Schema)
api.add_route('/route/to/resource1', Resource1View)
api.add_route('/route/to/resource2', Resource2View)
if __name__ == "__main__":
api.run(port=5000, address='0.0.0.0')
This works for us. YMMV. Feel free to use this however you like. I'd also appreciate feedback, if you think this doesn't make sense or could be improved.
Makes perfect sense to me!
@mmanhertz thank you for the example. I might orientate from it on this project https://github.com/Serkan-devel/Nebelscheibe but there's nothing there yet
@mmanhertz how would you import the schemas for the swagger documentation in the above example?
When all the endpoints are in one views.py
, adding a decorator before the route adds it to the swagger doc as well.
@here0to0learn if you look at my example for service/app.py
there are the lines
api.add_schema('Resource1', Resource1Schema)
api.add_schema('Resource2', Resource2Schema)
They are essentially using the decorator function, but instead of decorating the marshmallow schemas, they are passed in as arguments. The result is the same.
Right. I tried your example with the following contents of respective files (Assuming the schema is correctly setup):
Content of apis.resource1
class Resource1View:
def on_request(req, resp):
resp.media({"hello": "world"})
Maybe i am missing something here, but this does not get picked up by the swagger docs. I wonder if the names matter and everything should match everywhere? I get the following result in the swagger page: No operations defined in spec! The schema model however, gets picked up fine and is displayed correctly at the bottom. The endpoints themselves are not picked up though.
When all the endpoints are in single views.py, the endpoint would look like following:\
class Resource1View:
@app.route('/path/to/resource1') # this line adds the endpoint to swagger
def on_request(req, resp):
resp.media({"hello": "world"})
and this would let swagger pickup the endpoint to display properly.
@here0to0learn I think you are missing the docstring, as in the example from the responder docs. Here's how we do it in our project:
class Resource1View:
"""An endpoint for resource 1
---
get:
description: Get a random pet
responses:
200:
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/Resource1'
"""
def on_request(req, resp):
resp.media(<Actually return a JSON dump of resource1 here>)
Any valid OpenAPI schema in the docstring will be added to the swagger UI as far as I can tell.
Apologies for not adding that part. The schema mentioned in the the example and here is a response schema. I am talking about the request schema for the resource1
endpoint.
If you have all endpoints in views.py, i added the schema to a request like the following:
class Resource1View:
"""An endpoint for resource 1
---
get:
description: Get a random pet
schema: Resource1Schema
responses:
200:
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/Resource1'
"""
def on_request(req, resp):
resp.media(<Actually return a JSON dump of resource1 here>)
However, with the proposed file structure, this does not seem to work. I tried to import the schema class in resource1view
again or reference the already added schema with $ref = #/components/blah
but neither solved the problem. I hope i am explaining the issue correctly.
More over, the endpoints themselves registering with swagger docs does not have anything to do with the schema itself. The docstring should be enough to register an endpoint to the swagger docs (yaml file basically). E.g. the above content i posted in app.resource1
with the docstring should show up as an endpoint in swagger docs but doesn't.
Strange, it does work flawlessly for us. The one difference I can see: we are actually using the on_get
and on_post
functions instead of on_request
... don't know if that's relevant, though.
I will try to hack together a quick minimal working example repo tonight, if I have the time.
Tried that as well. It did not work :(
Can someone give an example how do we use on_get, on_post and on_put with responder?
I'm trying to use same route for 2 diff methods GET and POST, but I get following error (looks like I can not use same route for diff routes:
Traceback (most recent call last):
File "api.py", line 23, in
Below is the sample code I'm trying:
@api.route("/{anything}") ==> intention is that GET request should go here def mock_service(req, res, *, anything): res.text = "GET request successfully executed"
@api.route("/{anything}") ==> intention is that POST request should go here async def on_post(self, req, resp, *, anything): data = await req.media() // do something with data
@Saravana9210
You need to put the methods in a class and put the route decorator on that class, something like this:
@api.route("/{anything}")
class AnyApi:
def mock_service(req, res, *, anything):
res.text = "GET request successfully executed"
async def on_post(self, req, resp, *, anything):
data = await req.media()
// do something with data
PS: You can format your code examples nicely by enclosing them in triple back-ticks ```
I'd like to see an example, where responder is used with jinja2-tamplates
How does one inplement authentication without mounting a flask app as asked here ?
@Serkan-devel Open to suggestions from the community here on recommended ways.
I think you have several options (note using an HTTPS connection when outside of an isolated development environment):
- Basic HTTP headers per HTTP Headers
req.headers["authorization"] # reverse the base64 encoding and check against your system
- HTTP Headers with custom parameters (username and password)
req.headers["username"]
req.headers["password"]
Personally, I'm using how on_request is called before all other on_{method}s and class based views to check headers (application type, credentials, etc.) for my use case. The general organization of the class is like the above comments and examples in the documentation. I'm inheriting from a base class that handles the header checks and authorization, so that all my defined routes execute the base classes on_request to handle authorization,etc.
@Saravana9210 (note the example linked in the next paragraph includes the custom params for authorization as stated above, but is another class based example. The difference between @mmanhertz 2nd example is that "on_request" is implemented as well (as that is called before all methods, if they are defined in the class).
An example of the custom parameter option and a base_class for an api service in one of my projects is here: base_service.py and inheriting class that overrides route level authorization and executes routes.
Notes:
- the project is currently being developed, but the link is some example code to potentially answer your question (and have no dependency on Flask).
- Flask is in the requirements.txt, but that is not a dependency for the linked example code. It is just a preliminary design choice that that I'm choosing to mount a Flask app in my project to have Responder just be an API service (to take advantage of some of the background task features
p.s. Four ~'s also works for formatting code blocks
This thread has been very helpful in deciding how to architect apps at a larger scale! Thanks for the examples :)
can someone please clarify for me the difference between the decorator @api.route() and api.add_route()... ive been working on splitting a project structure with the main in an app.py and routes located in service files. in order to initialize these existing @api.route() would i need to use api.add_route in the app.py?
thanks in advance.
update: i ended up going with an object oriented route via dependency injection and injected api as a constructor parameter to my classes.
I just started working on Blueprint for Responder that is similar to Flask Blueprint.
The main
import responder
from bp1 import simple_page
api = responder.API(debug=True)
api.register_blueprint(simple_page, url_prefix='/pages')
if __name__ == '__main__':
api.run()
The blueprint
from responder.blueprints import Blueprint
simple_page = Blueprint('simple_page', __name__, template_folder='templates')
@simple_page.route("/{greeting}")
async def greet_world(req, resp, *, greeting):
resp.text = f"{greeting}, world!"
it works like this
$ curl http://127.0.0.1:5042/pages/hello
hello, world!