Use blacksheep create
with the following options to create an example API:
β¨ Project name: soquestion
π Project template: api
π€ Use controllers? Yes
π Use OpenAPI Documentation? Yes
π§ Library to read settings essentials-configuration
π© App settings format YAML
This will generate a simple API based on BlackSheep, with the endpoints defined in app/controllers/examples.py
:
"""
Example API implemented using a controller.
"""
from typing import List, Optional
from blacksheep.server.controllers import Controller, get, post
class ExamplesController(Controller):
@classmethod
def route(cls) -> Optional[str]:
return "/api/examples"
@classmethod
def class_name(cls) -> str:
return "Examples"
@get()
async def get_examples(self) -> List[str]:
"""
Gets a list of examples.
Lorem Ipsum Dolor Sit amet
"""
return list(f"example {i}" for i in range(3))
@post()
async def add_example(self, example: str):
"""
Adds an example.
"""
When you start the API (don't forget to create and activate a virtual environment before you do the pip install ...
) with python dev.py
and navigate to http://localhost:44777/docs you can see the OpenAPI documentation.
According to the documentation you can use the docstring to specify the endpoint description.
Is it somehow possible to also add documentation for the responses?
According to the documentation you can use the @docs
decorator, but that only works in a simple file where @docs
is defined beforehand. In the generated API @docs
is defined in app/docs/__init.py__
, but I can't find a way to use this inside the example.py
.
The generated app/docs/__init.py__
looks like this:
"""
This module contains OpenAPI Documentation definition for the API.
It exposes a docs object that can be used to decorate request handlers with additional
information, used to generate OpenAPI documentation.
"""
from blacksheep import Application
from blacksheep.server.openapi.v3 import OpenAPIHandler
from openapidocs.v3 import Info
from app.docs.binders import set_binders_docs
from app.settings import Settings
def configure_docs(app: Application, settings: Settings):
docs = OpenAPIHandler(
info=Info(title=settings.info.title, version=settings.info.version),
anonymous_access=True,
)
# include only endpoints whose path starts with "/api/"
docs.include = lambda path, _: path.startswith("/api/")
set_binders_docs(docs)
docs.bind_app(app)
Given how documentation handler is defined in BlackSheep example, you cannot easily use that particular instance. The reason being docs
is local to configure_docs
function, therefore it cannot be used outside of it. However, that documentation decorator is simply an instance of OpenAPIHandler
class, so you can move its definition outside of that function, and use it throughout your project freely. Here's a "patch" for the example project to show the naΓ―ve approach (sorry, but SO doesn't support patch/diff syntax highlight) with docs
renamed to docs_handler
to better distinguish it from app.docs
module:
diff --git a/app/controllers/examples.py b/app/controllers/examples.py
index 4bb984d..41989e5 100644
--- a/app/controllers/examples.py
+++ b/app/controllers/examples.py
@@ -5,6 +5,8 @@ from typing import List, Optional
from blacksheep.server.controllers import Controller, get, post
+from app.docs import docs_handler
+
class ExamplesController(Controller):
@classmethod
@@ -15,6 +17,7 @@ class ExamplesController(Controller):
def class_name(cls) -> str:
return "Examples"
+ @docs_handler(responses={200: "OK response", 404: "No example found"})
@get()
async def get_examples(self) -> List[str]:
"""
diff --git a/app/docs/__init__.py b/app/docs/__init__.py
index 0b3d0f1..f478864 100644
--- a/app/docs/__init__.py
+++ b/app/docs/__init__.py
@@ -9,18 +9,21 @@ from blacksheep.server.openapi.v3 import OpenAPIHandler
from openapidocs.v3 import Info
from app.docs.binders import set_binders_docs
-from app.settings import Settings
+from app.settings import load_settings, Settings
+settings = load_settings()
+
+docs_handler = OpenAPIHandler(
+ info=Info(title=settings.info.title, version=settings.info.version),
+ anonymous_access=True,
+)
+
def configure_docs(app: Application, settings: Settings):
- docs = OpenAPIHandler(
- info=Info(title=settings.info.title, version=settings.info.version),
- anonymous_access=True,
- )
# include only endpoints whose path starts with "/api/"
- docs.include = lambda path, _: path.startswith("/api/")
+ docs_handler.include = lambda path, _: path.startswith("/api/")
- set_binders_docs(docs)
+ set_binders_docs(docs_handler)
- docs.bind_app(app)
+ docs_handler.bind_app(app)
I am not particularly happy about project structure here, to be honest, I even thought about moving that handler instance to a separate module and making it a singleton class. But it should get you going, and you can adapt it to you real use-case as you see fit.