Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

https://github.com/dutradda/apidaora

HTTP/REST API using dataclasses and TypedDict annotation for python
https://github.com/dutradda/apidaora

Last synced: 3 months ago
JSON representation

HTTP/REST API using dataclasses and TypedDict annotation for python

Lists

README

        

# apidaora



apidaora


OpenAPI / HTTP / REST API using dataclasses and TypedDict annotation for python

---

**Documentation**: https://dutradda.github.io/apidaora

**Source Code**: https://github.com/dutradda/apidaora

---

## Key Features

- Declaration of request/response as dataclasses and dicts using typing annotations
- Input data validation with [jsondaora](https://github.com/dutradda/jsondaora)
- One of the [fastest](#benchmark) python api framework
- Can run on any asgi server

## Requirements

- Python 3.8+
- [jsondaora](https://github.com/dutradda/jsondaora) for json validation/parsing
- [orjson](https://github.com/ijl/orjson) for json/bytes serialization (jsondaora dependency)

## Instalation
```
$ pip install apidaora
```

## Simple example

```python
from apidaora import appdaora, route

@route.get('/hello')
def hello_controller(name: str) -> str:
return f'Hello {name}!'

app = appdaora(hello_controller)

```

Running the server (needs uvicorn [installed](https://www.uvicorn.org)):

```bash
uvicorn myapp:app
```

```
INFO: Started server process [16220]
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

```

Quering the server (needs curl [installed](https://curl.haxx.se/docs/install.html)):

```bash
curl -i localhost:8000/hello?name=World

```

```
HTTP/1.1 200 OK
date: Thu, 1st January 1970 00:00:00 GMT
server: uvicorn
content-type: application/json
content-length: 14

"Hello World!"

```

## Basic example

```python
from typing import TypedDict

from jsondaora import IntegerField, StringField, jsondaora

from apidaora import Header, appdaora, route

class Integer(IntegerField, minimum=18):
...

class String(StringField, max_length=100):
...

class Age(Header, type=Integer):
...

@jsondaora
class You(TypedDict):
name: str
last_name: str
location: str
age: int

@jsondaora
class ReqBody(TypedDict):
last_name: str

@jsondaora
class HelloOutput(TypedDict):
hello_message: str
about_you: You

@route.put('/hello/{name}')
async def hello_controller(
name: str, location: String, age: Age, body: ReqBody
) -> HelloOutput:
you = You(
name=name,
location=location.value,
age=age.value.value,
last_name=body['last_name'],
)
return HelloOutput(
hello_message=await hello_message(name, location.value), about_you=you
)

async def hello_message(name: str, location: str) -> str:
return f'Hello {name}! Welcome to {location}!'

app = appdaora(hello_controller)

```

Running the server:

```bash
uvicorn myapp:app
```

```
INFO: Started server process [16220]
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

```

Quering the server:

```bash
curl -i -X PUT localhost:8000/hello/Me?location=World \
-H 'x-age: 32' \
-d '{"last_name":"My Self"}'

```

```
HTTP/1.1 200 OK
date: Thu, 1st January 1970 00:00:00 GMT
server: uvicorn
content-type: application/json
content-length: 123

{"hello_message":"Hello Me! Welcome to World!","about_you":{"name":"Me","location":"World","age":32,"last_name":"My Self"}}

```

## Example for more request/response details

```python
from http import HTTPStatus
from typing import Dict

from jsondaora import jsondaora

from apidaora import BadRequestError, Header, Response, appdaora, json, route

# Domain layer, here are the domain related definitions
# it is apidaora/framework/http independent

@jsondaora
class You:
name: str
last_name: str
age: int

DB: Dict[str, You] = {}

def add_you(you: You) -> None:
if you.name in DB:
raise YouAlreadyBeenAddedError(you.name)
DB[you.name] = you

def get_you(name: str) -> You:
try:
return DB[name]
except KeyError:
raise YouWereNotFoundError(name)

class DBError(Exception):
@property
def info(self) -> Dict[str, str]:
return {'name': self.args[0]}

class YouAlreadyBeenAddedError(DBError):
name = 'you-already-been-added'

class YouWereNotFoundError(DBError):
name = 'you-were-not-found'

# Application layer, here are the http related definitions

# See: https://dutrdda.github.io/apidaora/tutorial/headers/
class ReqID(Header, type=str, http_name='http_req_id'):
...

@route.post('/you/')
async def add_you_controller(req_id: ReqID, body: You) -> Response:
try:
add_you(body)
except YouAlreadyBeenAddedError as error:
raise BadRequestError(name=error.name, info=error.info) from error

return json(body, HTTPStatus.CREATED, headers=(req_id,))

@route.get('/you/{name}')
async def get_you_controller(name: str, req_id: ReqID) -> Response:
try:
return json(get_you(name), headers=(req_id,))
except YouWereNotFoundError as error:
raise BadRequestError(name=error.name, info=error.info) from error

app = appdaora([add_you_controller, get_you_controller])

```

Running the server:

```bash
uvicorn myapp:app
```

```
INFO: Started server process [16220]
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

```

Quering the server:

```bash
curl -X POST -i localhost:8000/you/ -H 'http_req_id: 1a2b3c4d' -d '{"name":"Me","last_name":"Myself","age":32}'

```

```
HTTP/1.1 201 Created
date: Thu, 1st January 1970 00:00:00 GMT
server: uvicorn
content-type: application/json
content-length: 43
http_req_id: 1a2b3c4d

{"name":"Me","last_name":"Myself","age":32}

```

```bash
curl -i localhost:8000/you/Me -H 'http_req_id: 4d3c2b1a'

```

```
HTTP/1.1 200 OK
date: Thu, 1st January 1970 00:00:00 GMT
server: uvicorn
content-type: application/json
content-length: 43
http_req_id: 4d3c2b1a

{"name":"Me","last_name":"Myself","age":32}

```

## Benchmark

![techempower benchmark](https://dutradda.github.io/apidaora/benchmark.png "TechEmpower Frameworks Benchmark")

The full results can be found [here](https://www.techempower.com/benchmarks/#section=test&runid=76bbd357-a161-42eb-a203-051bdd949006&hw=ph&test=query&l=zijzen-v)