Instarest
Declarative, instant REST APIs based on FastAPI, Pydantic, SQLAlchemy, and PostgreSQL
Documentation: instarest.erob.io
Source Code: github.com/erob123/instarest
Instarest provides an opinionated, extensible implementation of a FastAPI, Pydantic, SQLAlchemy, and PostgreSQL stack. It is designed to simplify and reuse common code patterns for these technologies, preventing excess boilerplate and duplicative code.
Our goal is to help you turn months of work into days and thousands of lines of code into less than a hundred.
By using instarest, you will notice
- Simplicity: your typical multi-folder, multi-file, multi-class, multi-method, multi-line, multi-annotation, multi-configuration FastAPI application will be reduced single file with a few lines of code.
- Consistency: your application will be built on a consistent, declarative, and opinionated foundation, making it easier to understand and maintain.
- Speed: your application will be built on a foundation that is designed to be fast, both in terms of development and runtime performance.
- Fewer Unit Tests: your application will be built on a foundation that is designed to be correct, reducing the need for extensive unit testing. Complete code coverage can be achieved with a handful of unit tests.
- Easy: Designed to be easy to use and learn. Less time reading docs.
- Extensible: Instarest is built to be modular, and as such is easy to extend so that you can have your own custom, realizable opinionations and abstractions. Frameworks such as Aimbase and Chainbase are built on top of Instarest in this way.
- Standards-based: Based on FastAPI, which itself is based on (and fully compatible with) the open standards for APIs: OpenAPI (previously known as Swagger) and JSON Schema
Requirements¶
- Python 3.11+
- A PostgreSQL database (use
docker-composeto get one up and running quickly)
Installation¶
$ pip install instarest
---> 100%
Example¶
Create it¶
Let's create a complete database-backed, type-checked, versioned REST API with Instarest in five minutes:
- Create a file
main.pywith:
from instarest import (
AppBase,
DeclarativeBase,
RESTRouter,
SchemaBase,
CRUDBase,
Initializer,
)
from sqlalchemy import Column, String, Boolean
class EmptyTestModel(DeclarativeBase):
bool_field = Column(Boolean(), default=False)
title = Column(String(), default="title")
initializer.execute()
# built pydantic data transfer schemas automagically
crud_schemas = SchemaBase(EmptyTestModel)
# build crud db service automagically
crud_test = CRUDBase(EmptyTestModel)
# build crud router automagically
test_router = RESTRouter(
schema_base=crud_schemas,
crud_base=crud_test,
prefix="/test",
allow_delete=True,
)
# setup base up from routers
app_base = AppBase(crud_routers=[test_router], app_name="Test App API")
# automagic and version app
auto_app = app_base.get_autowired_app()
# core underlying app
app = app_base.get_core_app()
Setup the database¶
If you already have a PostgreSQL database running, you can skip this step.
If not, we will launch via a local container:
-
First, make sure that you have docker installed and running. If you don't, you can install it by following the directions at this link.
-
Download the
instarestdocker-compose file:
$ curl -O https://raw.githubusercontent.com/erob123/instarest/main/docker-compose.yml
- Launch the database and pgadmin console via docker-compose from the same directory as the
docker-compose.ymlfile:
$ docker-compose up --build
Tell your app how to connect to the database¶
Instarest is setup to automatically connect to PostgreSQL with just a few Environment variables. To get started, create
a file named local.env in the same directory as main.py with the following contents:
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_SERVER=localhost
POSTGRES_PORT=5432
POSTGRES_DB=postgres
This will allow your app to connect to the database on launch. For reference, these values are defined
within the docker-compose.yml file for local development, but for production they will come from your
password defined through your database provider.
Run it¶
You should now have two files in the same directory: main.py and local.env. Let's run the app with:
$ uvicorn main:auto_app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
About the command uvicorn main:app --reload...
The command uvicorn main:auto_app refers to:
main: the filemain.py(the Python "module").auto_app: the object created inside ofmain.pywith the lineauto_app = app_base.get_autowired_app().--reload: make the server restart after code changes. Only do this for development.
Interactive API docs¶
Now go to http://127.0.0.1:8000/v1/docs.
You will see the automatic interactive API documentation (provided by Swagger UI):
Congratulations! You have just created your first fully-functional REST API with Instarest, implementing all CRUD operations (Create, Read, Update, Delete).
Check it¶
Step 1: Create a new item¶
In the interactive docs (or using curl if you prefer), go to the POST operation and try it. Send this JSON body:
{"title": "Hello", "bool_field": true}
You should see the curl that was sent
$ curl -X 'POST' \
'http://localhost:8000/v1/test/' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"title": "Hello", "bool_field": true}'
and the response received (with unique id):
{
"bool_field": true,
"title": "Hello",
"id": "be79d6de-752d-468d-a649-2e0bc62fcf64"
}
Step 2: Read the item you just created¶
Open your browser at http://127.0.0.1:8000/v1/test/{id}, replacing {id} with the one you got in the previous step (for example, be79d6de-752d-468d-a649-2e0bc62fcf64).
You will see the JSON response as (with the same unique id):
{"bool_field":true,"title":"Hello","id":"be79d6de-752d-468d-a649-2e0bc62fcf64"}
Step 3: Update the item you just created¶
In the interactive docs (or using curl if you prefer), go to the PUT operation and click Try it out. Put the same unique id into the
appropriate parameter box as pulled from the GET request in the previous step. Then, send this JSON body:
{"title": "Goodbye"}
You should see the curl that was sent
$ curl -X 'PUT' \
'http://localhost:8000/v1/test/be79d6de-752d-468d-a649-2e0bc62fcf64' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"title": "Goodbye"}'
and the response received (with the same unique id):
{
"bool_field": true,
"title": "Goodbye",
"id": "be79d6de-752d-468d-a649-2e0bc62fcf64"
}
Notice that the title field changed from "Hello" to "Goodbye", but all other fields remained the same, even though we didn't send them.
Step 4: Read the item you just updated¶
Open your browser at http://127.0.0.1:8000/v1/test/{id}, replacing {id} with the same one you have been using.
You will see the JSON response as (with the same unique id):
{"bool_field":true,"title":"Goodbye","id":"be79d6de-752d-468d-a649-2e0bc62fcf64"}
Notice that the title field changed from "Hello" to "Goodbye", but all other fields remained the same.
Step 5: Delete the item you just created¶
In the interactive docs (or using curl if you prefer), go to the DELETE operation and click Try it out. Put the same unique id into the
appropriate parameter box that we have been using and click execute.
You should see the curl that was sent
$ curl -X 'DELETE' \
'http://localhost:8000/v1/test/be79d6de-752d-468d-a649-2e0bc62fcf64' \
-H 'accept: application/json'
and the response received:
{}
Now, without clearing the parameter box, click execute again. You should see a 400 response
with the appropriate id in the error message:
{
"detail": "EmptyTest with id: be79d6de-752d-468d-a649-2e0bc62fcf64 not found"
}
A successful delete will return a 200 response with an empty JSON body. A failed delete will return a 400 response with a JSON body containing the error message.
Step 6: Read the item you just deleted¶
Open your browser at http://127.0.0.1:8000/v1/test/{id}, replacing {id} with the same one you have been using.
You will see a response with the appropriate id in the error message:
{"detail":"EmptyTest with id: be79d6de-752d-468d-a649-2e0bc62fcf64 not found"}
And that's it! You have a fully-functioning, single-store, persistent, versioned REST API with all CRUD operations implemented.
All done in under 50 lines of code.
Alternative API docs¶
If you prefer, go to http://127.0.0.1:8000/v1/redoc.
You will see the alternative automatic documentation (provided by ReDoc):
License¶
This project is licensed under the terms of the MIT license.