REST support for bareASGI
Project description
bareASGI-rest
This package provides enhanced support for writing REST APIs with bareASGI, (read the docs).
It includes:
- A router to simplify the creation of REST APIs,
- A swagger API endpoint
This is a Python 3.7+ package, and is currently pre-release.
Installation
The package can be install from pypi.
It is currently pre-release so you will need the --pre flag.
$ pip install --pre bareASGI-rest
An ASGI server will be required to run the code. The examples below use uvicorn.
$ pip install uvicorn
Usage
The router provided by this package maps the arguments and types of request handlers.
We will create a mock book repository.
Creating typed dictionaries
Here is the type of a book. We use TypedDict
to allow automatic type discovery
from datetime import datetime try: # Available in 3.8 from typing import TypedDict # type:ignore except: # Available in 3.7 from typing_extensions import TypedDict class Book(TypedDict): """A Book Args: book_id (int): The book id title (str): The title author (str): The author published (datetime): The publication date """ book_id: int title: str author: str published: datetime
Note: the docstring will be used to provide documentation for swagger.
Creating the API
Now we can build the API.
from typing import Dict, List from urllib.error import HTTPError BOOKS: Dict[int, Book] = {} NEXT_ID: int = 0 async def get_books() -> List[Book]: """Get all the books. This method gets all the books in the shop. Returns: List[Book]: All the books """ return list(BOOKS.values()) async def get_book(book_id: int) -> Book: """Get a book for a given id Args: book_id (int): The id of the book Raises: HTTPError: 404, when a book is not found Returns: Book: The book """ if book_id not in BOOKS: raise HTTPError(None, 404, None, None, None) return BOOKS[book_id] async def create_book( author: str, title: str, published: datetime ) -> int: """Add a book Args: author (str): The author title (str): The title published (datetime): The publication date Returns: int: The id of the new book """ NEXT_ID += 1 BOOKS[NEXT_ID] = Book( book_id=NEXT_ID, title=title, author=author, published=published ) return NEXT_ID async def update_book( book_id: int, author: str, title: str, published: datetime ) -> None: """Update a book Args: book_id (int): The id of the book to update author (str): The new author title (str): The title published (datetime): The publication date Raises: HTTPError: 404, when a book is not found """ if book_id not in BOOKS: raise HTTPError(None, 404, None, None, None) BOOKS[book_id]['title'] = title BOOKS[book_id]['author'] = author BOOKS[book_id]['published'] = published
We can see that errors are handler by raising HTTPError
from the urllib.errors
standard library package. A convention has been applied such that the status code MUST
appear before the message, separated by a comma.
Adding support for the REST router
Now we must create our application and add support for the router.
from bareasgi import Application from bareasgi_rest import RestHttpRouter, add_swagger_ui router = RestHttpRouter( None, title="Books", version="1", description="A book api", base_path='/api/1', tags=[ { 'name': 'Books', 'description': 'The book store API' } ] ) app = Application(http_router=router) add_swagger_ui(app)
Note the base_path
argument can be used to prefix all
paths.
The RestHttpRouter
is a subclass of the basic router, so
all those methods are also available.
Creating the routes
Now we can create the routes:
tags = ['Books'] router.add_rest({'GET'}, '/books', get_books,tags=tags) router.add_rest({'GET'}, '/books/{bookId:int}', get_book, tags=tags) router.add_rest({'POST'}, '/books', create_book, tags=tags, status_code=201) router.add_rest({'PUT'}, '/books/{bookId:int}', update_book, tags=tags, status_code=204)
First we should note that the paths will be prefixed with the
base_path
provided to the router.
Referring back to the implementation of get_book
we can
see that the camel-case path variable bookId
has been
mapped to the snake-case book_id
parameter. The JSON object provided in the body of the create_book
will
similarly map camel-cased properties to the snake-cased
function parameters.
We can also see how the status codes have been overridden
for the POST
and PUT
endpoints, and all the routes
have the "Books" tag for grouping in the UI.
Serving the API
Finally we can serve the API:
import uvicorn uvicorn.run(app, port=9009)
Browsing to http://localhost/api/1/swagger we should see:
When we expand GET /books/{bookId}
we can see all the
information provided in the docstring and typing has been
passed through to the swagger UI.
Thanks
Thanks to rr- and contributors for the excellent docstring-parser package.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Filename, size | File type | Python version | Upload date | Hashes |
---|---|---|---|---|
Filename, size bareASGI_rest-3.0.0-py3-none-any.whl (22.1 kB) | File type Wheel | Python version py3 | Upload date | Hashes View |
Filename, size bareASGI-rest-3.0.0.tar.gz (18.2 kB) | File type Source | Python version None | Upload date | Hashes View |
Hashes for bareASGI_rest-3.0.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | dd78b01f4cd9ce26567268a92b37b1dfdd1afaa6c1af57901f0b7478955b6525 |
|
MD5 | af3f8b36e65682b4e6f41cb372a94072 |
|
BLAKE2-256 | a808a631d54a217ba7982f54de6ab522964f6f48efd4c119f1857a968d863908 |