# rAPIdy 💮
**write quickly** 🚀 **write beautifully** 🌸
<a href="https://github.com/daniil-grois/rAPIdy">rAPIdy<a/> is a minimalistic, asynchronous, fast web framework based in aiohttp and pydantic.
```
pip install rapidy
```
Key Features:
* ✏️ easy to write and read code
* 📔 <a href="https://github.com/pydantic/pydantic">pydantic<a/> native support -> **yes** we support **V**1️⃣ and **V**2️⃣❗
* 🚀 <a href="https://github.com/aio-libs/aiohttp">aiohttp<a/> based and native features support - aiohttp one of the fastest and most productive HTTP servers
* 📤 convenient support for basic types of incoming data extraction
## Quickstart
### Create endpoint using RouteTableDef
rAPIdy inherits the basic functionality of aiohttp <a href="https://docs.aiohttp.org/en/stable/web_quickstart.html">quickstart</a>
```python
from rapidy import web
from typing_extensions import Annotated
routes = web.RouteTableDef()
@routes.get('/hello')
async def hello(
request: web.Request,
username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],
password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],
) -> web.Response:
# write you business code here
return web.json_response({'username': username, 'password': password})
app = web.Application()
app.add_routes(routes)
if __name__ == '__main__':
web.run_app(app, port=8000)
```
### Create endpoint using web.<method_name>
```python
from rapidy import web
from typing_extensions import Annotated
async def hello(
request: web.Request,
username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],
password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],
) -> web.Response:
# write you business code here
return web.json_response({'username': username, 'password': password})
app = web.Application()
app.add_routes([web.post('/hello', hello)])
if __name__ == '__main__':
web.run_app(app, port=8000)
```
### Create endpoint using web.view
```python
from rapidy import web
from typing_extensions import Annotated
class Handler(web.View):
async def post(
self,
username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],
password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],
) -> web.Response:
# write you business code here
return web.json_response({'username': username, 'password': password})
app = web.Application()
app.add_routes([web.view('/hello', Handler)])
if __name__ == '__main__':
web.run_app(app, port=8000)
```
## Pydantic native support
```python
from rapidy import web
from typing_extensions import Annotated
async def hello_handler(
request: web.Request,
username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],
password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],
) -> web.Response:
# write you business code here
return web.json_response({'username': username, 'password': password})
app = web.Application()
app.add_routes([web.post('/hello', hello_handler)])
web.run_app(app, port=8000)
```
✅✅✅ Success request validation ✅✅✅
```
curl -X POST \
-H "Content-Type: application/json" -d '{"username": "Max", "password": "myAwesomePass"}' -v \
http://127.0.0.1:8000/hello
< HTTP/1.1 200 OK ... {"username": "Max", "password": "myAwesomePass"}
```
❌❌❌ Request validation failure ❌❌❌
```
curl -X POST \
-H "Content-Type: application/json" -d '{"username": "M", "password": "m"}' -v \
http://127.0.0.1:8000/hello
< HTTP/1.1 422 Unprocessable Entity ...
{
"errors": [
{
"loc": ["body", "username"],
"type": "string_too_short",
"msg": "String should have at least 3 characters",
"ctx": {"min_length": 3}
},
{
"type": "string_too_short",
"loc": ["body", "password"],
"msg": "String should have at least 8 characters",
"ctx": {"min_length": 8}
}
]
}
```
## Choose your path
* You can create APIs the way you want.
* **rAPIdy** supports 3 basic types for defining incoming parameters
* 🌒 param
* Path
* Header
* Cookie
* Query
* BodyJson
* FormDataBody
* MultipartBody
* 🌕 schema
* PathSchema
* HeaderSchema
* CookieSchema
* QuerySchema
* BodyJsonSchema
* FormDataBodySchema
* MultipartBodySchema
* 🌑 raw data (no validate with pydantic)
* PathRaw - **Dict[str, str]**
* HeaderRaw - **Dict[str, str]**
* CookieRaw - **Dict[str, str]**
* QueryRaw - **Dict[str, str]**
* BodyJsonRaw - **Dict[str, Any]**
* FormDataBodyRaw - **Dict[str, Any]**
* MultipartBodyRaw - **Dict[str, Any]**
* TextBody - **str**
* BytesBody - **bytes**
* StreamBody - **aiohttp.streams.StreamReader**
```python
# defining request attributes as param 🌒
from rapidy import web
from typing_extensions import Annotated
async def hello_handler(
request: web.Request,
# path params
path_param: Annotated[str, web.Path],
# headers
host: Annotated[str, web.Header(alias='Host')],
user_agent: Annotated[str, web.Header(alias='User-Agent')],
# cookie
user_cookie1: Annotated[str, web.Cookie(alias='UserCookie1')],
user_cookie2: Annotated[str, web.Cookie(alias='UserCookie2')],
# query params
user_param1: Annotated[str, web.Query(alias='UserQueryParam1')],
user_param2: Annotated[str, web.Cookie(alias='UserQueryParam2')],
# body
username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],
password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],
) -> web.Response:
# write you business code here
# ...
return web.Response()
app = web.Application()
app.add_routes([web.post('/hello/{path_param}', hello_handler)])
```
```python
# defining request attributes as schema 🌕
from pydantic import BaseModel, Field
class PathRequestSchema(BaseModel):
path_param: str
class HeaderRequestSchema(BaseModel):
host: str = Field(alias='Host')
user_agent: str = Field(alias='User-Agent')
class CookieRequestSchema(BaseModel):
user_cookie1: str = Field(alias='UserCookie1')
user_cookie2: str = Field(alias='UserCookie2')
class QueryRequestSchema(BaseModel):
user_cookie1: str = Field(alias='UserQueryParam1')
user_cookie2: str = Field(alias='UserQueryParam1')
class BodyRequestSchema(BaseModel):
username: str = Field(min_length=3, max_length=20)
password: str = Field(min_length=8, max_length=40)
async def hello_handler(
request: web.Request,
path: Annotated[PathRequestSchema, web.PathSchema],
headers: Annotated[HeaderRequestSchema, web.HeaderSchema],
cookies: Annotated[CookieRequestSchema, web.Cookie],
query: Annotated[QueryRequestSchema, web.QuerySchema],
body: Annotated[BodyRequestSchema, web.JsonBodySchema],
) -> web.Response:
```
```python
# defining request attributes as raw 🌑
async def hello_handler(
request: web.Request,
path: Annotated[Dict[str, str], web.PathRaw],
headers: Annotated[Dict[str, str], web.HeaderRaw],
cookies: Annotated[Dict[str, str], web.CookieRaw],
query: Annotated[Dict[str, str], web.QueryRaw],
body: Annotated[Dict[str, Any], web.JsonBodyRaw],
) -> web.Response:
```
```python
# also you may to combine 🌒 🌕 🌑
async def hello_handler(
request: web.Request,
path_param: Annotated[str, web.Path],
headers: Annotated[Dict[str, str], web.HeaderRaw],
body: Annotated[BodyRequestSchema, web.JsonBodySchema],
) -> web.Response:
```
Raw data
{
"_id": null,
"home_page": "",
"name": "rAPIdy",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8,<4.0",
"maintainer_email": "",
"keywords": "rAPIdy,aiohttp,pydantic,api,fast,http server,Daniil Grois",
"author": "Daniil Grois",
"author_email": "daniil.grois@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/14/47/4c8e79afe6c95ff0fbec9ff0537698ea7eeafb3cecdc4ec82ac674430c3a/rapidy-0.1.1.tar.gz",
"platform": null,
"description": "\n# rAPIdy \ud83d\udcae\n**write quickly** \ud83d\ude80 **write beautifully** \ud83c\udf38\n\n<a href=\"https://github.com/daniil-grois/rAPIdy\">rAPIdy<a/> is a minimalistic, asynchronous, fast web framework based in aiohttp and pydantic.\n\n```\npip install rapidy\n```\n\nKey Features:\n* \u270f\ufe0f easy to write and read code\n* \ud83d\udcd4 <a href=\"https://github.com/pydantic/pydantic\">pydantic<a/> native support -> **yes** we support **V**1\ufe0f\u20e3 and **V**2\ufe0f\u20e3\u2757\n* \ud83d\ude80 <a href=\"https://github.com/aio-libs/aiohttp\">aiohttp<a/> based and native features support - aiohttp one of the fastest and most productive HTTP servers\n* \ud83d\udce4 convenient support for basic types of incoming data extraction\n\n## Quickstart\n\n### Create endpoint using RouteTableDef\n\nrAPIdy inherits the basic functionality of aiohttp <a href=\"https://docs.aiohttp.org/en/stable/web_quickstart.html\">quickstart</a>\n\n```python\nfrom rapidy import web\nfrom typing_extensions import Annotated\n\nroutes = web.RouteTableDef()\n\n\n@routes.get('/hello')\nasync def hello(\n request: web.Request,\n username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],\n password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],\n) -> web.Response:\n # write you business code here\n return web.json_response({'username': username, 'password': password})\n\n\napp = web.Application()\napp.add_routes(routes)\n\nif __name__ == '__main__':\n web.run_app(app, port=8000)\n```\n\n### Create endpoint using web.<method_name>\n\n```python\nfrom rapidy import web\nfrom typing_extensions import Annotated\n\n\nasync def hello(\n request: web.Request,\n username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],\n password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],\n) -> web.Response:\n # write you business code here\n return web.json_response({'username': username, 'password': password})\n\n\napp = web.Application()\napp.add_routes([web.post('/hello', hello)])\n\nif __name__ == '__main__':\n web.run_app(app, port=8000)\n```\n\n### Create endpoint using web.view\n\n```python\nfrom rapidy import web\nfrom typing_extensions import Annotated\n\n\nclass Handler(web.View):\n async def post(\n self,\n username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],\n password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],\n ) -> web.Response:\n # write you business code here\n return web.json_response({'username': username, 'password': password})\n\n\napp = web.Application()\napp.add_routes([web.view('/hello', Handler)])\n\nif __name__ == '__main__':\n web.run_app(app, port=8000)\n```\n\n## Pydantic native support\n\n```python\nfrom rapidy import web\nfrom typing_extensions import Annotated\n\n\nasync def hello_handler(\n request: web.Request,\n username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],\n password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],\n) -> web.Response:\n # write you business code here\n return web.json_response({'username': username, 'password': password})\n\n\napp = web.Application()\napp.add_routes([web.post('/hello', hello_handler)])\nweb.run_app(app, port=8000)\n```\n\u2705\u2705\u2705 Success request validation \u2705\u2705\u2705\n```\ncurl -X POST \\\n-H \"Content-Type: application/json\" -d '{\"username\": \"Max\", \"password\": \"myAwesomePass\"}' -v \\\nhttp://127.0.0.1:8000/hello\n\n< HTTP/1.1 200 OK ... {\"username\": \"Max\", \"password\": \"myAwesomePass\"}\n```\n\n\u274c\u274c\u274c Request validation failure \u274c\u274c\u274c\n\n```\ncurl -X POST \\\n-H \"Content-Type: application/json\" -d '{\"username\": \"M\", \"password\": \"m\"}' -v \\\nhttp://127.0.0.1:8000/hello\n\n< HTTP/1.1 422 Unprocessable Entity ...\n{\n \"errors\": [\n {\n \"loc\": [\"body\", \"username\"],\n \"type\": \"string_too_short\",\n \"msg\": \"String should have at least 3 characters\",\n \"ctx\": {\"min_length\": 3}\n },\n {\n \"type\": \"string_too_short\",\n \"loc\": [\"body\", \"password\"],\n \"msg\": \"String should have at least 8 characters\",\n \"ctx\": {\"min_length\": 8}\n }\n ]\n}\n\n```\n\n## Choose your path\n\n* You can create APIs the way you want.\n* **rAPIdy** supports 3 basic types for defining incoming parameters\n * \ud83c\udf12 param\n * Path\n * Header\n * Cookie\n * Query\n * BodyJson\n * FormDataBody\n * MultipartBody\n * \ud83c\udf15 schema\n * PathSchema\n * HeaderSchema\n * CookieSchema\n * QuerySchema\n * BodyJsonSchema\n * FormDataBodySchema\n * MultipartBodySchema\n * \ud83c\udf11 raw data (no validate with pydantic)\n * PathRaw - **Dict[str, str]**\n * HeaderRaw - **Dict[str, str]**\n * CookieRaw - **Dict[str, str]**\n * QueryRaw - **Dict[str, str]**\n * BodyJsonRaw - **Dict[str, Any]**\n * FormDataBodyRaw - **Dict[str, Any]**\n * MultipartBodyRaw - **Dict[str, Any]**\n * TextBody - **str**\n * BytesBody - **bytes**\n * StreamBody - **aiohttp.streams.StreamReader**\n\n```python\n# defining request attributes as param \ud83c\udf12\nfrom rapidy import web\nfrom typing_extensions import Annotated\n\n\nasync def hello_handler(\n request: web.Request,\n # path params\n path_param: Annotated[str, web.Path],\n # headers\n host: Annotated[str, web.Header(alias='Host')],\n user_agent: Annotated[str, web.Header(alias='User-Agent')],\n # cookie\n user_cookie1: Annotated[str, web.Cookie(alias='UserCookie1')],\n user_cookie2: Annotated[str, web.Cookie(alias='UserCookie2')],\n # query params\n user_param1: Annotated[str, web.Query(alias='UserQueryParam1')],\n user_param2: Annotated[str, web.Cookie(alias='UserQueryParam2')],\n # body\n username: Annotated[str, web.JsonBody(min_length=3, max_length=20)],\n password: Annotated[str, web.JsonBody(min_length=8, max_length=40)],\n) -> web.Response:\n # write you business code here\n # ...\n return web.Response()\n\n\napp = web.Application()\napp.add_routes([web.post('/hello/{path_param}', hello_handler)])\n```\n```python\n# defining request attributes as schema \ud83c\udf15\nfrom pydantic import BaseModel, Field\n\nclass PathRequestSchema(BaseModel):\n path_param: str\n\nclass HeaderRequestSchema(BaseModel):\n host: str = Field(alias='Host')\n user_agent: str = Field(alias='User-Agent')\n\nclass CookieRequestSchema(BaseModel):\n user_cookie1: str = Field(alias='UserCookie1')\n user_cookie2: str = Field(alias='UserCookie2')\n\nclass QueryRequestSchema(BaseModel):\n user_cookie1: str = Field(alias='UserQueryParam1')\n user_cookie2: str = Field(alias='UserQueryParam1')\n\nclass BodyRequestSchema(BaseModel):\n username: str = Field(min_length=3, max_length=20)\n password: str = Field(min_length=8, max_length=40)\n\nasync def hello_handler(\n request: web.Request,\n path: Annotated[PathRequestSchema, web.PathSchema],\n headers: Annotated[HeaderRequestSchema, web.HeaderSchema],\n cookies: Annotated[CookieRequestSchema, web.Cookie],\n query: Annotated[QueryRequestSchema, web.QuerySchema],\n body: Annotated[BodyRequestSchema, web.JsonBodySchema],\n) -> web.Response:\n\n```\n\n```python\n# defining request attributes as raw \ud83c\udf11\nasync def hello_handler(\n request: web.Request,\n path: Annotated[Dict[str, str], web.PathRaw],\n headers: Annotated[Dict[str, str], web.HeaderRaw],\n cookies: Annotated[Dict[str, str], web.CookieRaw],\n query: Annotated[Dict[str, str], web.QueryRaw],\n body: Annotated[Dict[str, Any], web.JsonBodyRaw],\n) -> web.Response:\n```\n\n```python\n# also you may to combine \ud83c\udf12 \ud83c\udf15 \ud83c\udf11\nasync def hello_handler(\n request: web.Request,\n path_param: Annotated[str, web.Path],\n headers: Annotated[Dict[str, str], web.HeaderRaw],\n body: Annotated[BodyRequestSchema, web.JsonBodySchema],\n) -> web.Response:\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "rAPIdy - write quickly - write beautifully",
"version": "0.1.1",
"project_urls": null,
"split_keywords": [
"rapidy",
"aiohttp",
"pydantic",
"api",
"fast",
"http server",
"daniil grois"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "6fe20e357e32661ed6fe7f23185f72e8c45a40deffa7aea34c2a1c2cf23543c1",
"md5": "611b4afe6d646983e78fa576c2e52166",
"sha256": "238b7b83210c4eb54b17aa7a037eeb4f013aa0bb5d5ddfc29be7bc4f5d37a9c1"
},
"downloads": -1,
"filename": "rapidy-0.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "611b4afe6d646983e78fa576c2e52166",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<4.0",
"size": 25910,
"upload_time": "2024-02-29T16:46:32",
"upload_time_iso_8601": "2024-02-29T16:46:32.082116Z",
"url": "https://files.pythonhosted.org/packages/6f/e2/0e357e32661ed6fe7f23185f72e8c45a40deffa7aea34c2a1c2cf23543c1/rapidy-0.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "14474c8e79afe6c95ff0fbec9ff0537698ea7eeafb3cecdc4ec82ac674430c3a",
"md5": "9cc7eca82a76fdd6365cdcb09df894fb",
"sha256": "ec8542a70bfad58627b961e66c4cb5e51844c5b0707940c1fd59b4116ac3328c"
},
"downloads": -1,
"filename": "rapidy-0.1.1.tar.gz",
"has_sig": false,
"md5_digest": "9cc7eca82a76fdd6365cdcb09df894fb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<4.0",
"size": 22042,
"upload_time": "2024-02-29T16:46:33",
"upload_time_iso_8601": "2024-02-29T16:46:33.856764Z",
"url": "https://files.pythonhosted.org/packages/14/47/4c8e79afe6c95ff0fbec9ff0537698ea7eeafb3cecdc4ec82ac674430c3a/rapidy-0.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-02-29 16:46:33",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "rapidy"
}