Fastapi Python

broken image


Awesome FastAPI. A curated list of awesome things related to FastAPI. FastAPI is a modern, high-performance, batteries-included Python web framework that's perfect for building RESTful APIs. High-Performance, Robust, Auto-docs APIs using FastAPI in Python October 6, 2020 October 6, 2020 by Om Singh Building APIs and using them in your application has become an inevitable task to make your application more modular and the development process even faster and use resources efficiently.

In this tutorial, we'll build a production-ready FastAPI server together.The full functional example is available here.

Fastapi

Our application stack will look like this:

FastAPI is a fairly new python (micro) web framework with built-in support for async endpoints. Uvicorn is our chosen ASGI server. ASGI Is the asynchronous sister of python WSGI. Now we are ready for some code. First async endpoint. To configure a FastAPI service, create a python module named app.py, with the following code. FastAPI is a Python web framework designed for building fast and efficient backend APIs. It handles both synchronous and asynchronous operations and has built-in support for data validation, authentication, and interactive API documentation powered by OpenAPI.

Start a New Project¶

Instead of pip, let's use the shiny Poetry to manage our project. Follow the link toinstall Poetry, and create our newproject in an empty directory:

Then follow the Poetry guide to finish the initialization - you may say 'no' to theinteractive dependency creation, because we will add them manually. It is okay to usethe default values in the other steps of the guide, and make sure the package nameremains gino-fastapi-demo.

Fastapi

Add Dependencies¶

FastAPI is built on top of the Starlette framework, so we shall use the GINOextension for Starlette. Simply run:

Then let's add FastAPI, together with the lightning-fast ASGI server Uvicorn, andGunicorn as a production application server:

For database migration, we'll use Alembic. Because it uses normal DB-API, we needpsycopg here too:

At last, let's add pytest in the development environment for testing. We also want toadd the requests library to use the StarletteTestClient:

Hint

With the steps above, Poetry will automatically create a virtualenv for youbehind the scene, and all the dependencies are installed there. We will assumeusing this for the rest of the tutorial. But you're free to create your ownvirtualenv, and Poetry will honor it when it's activated.

That's all, this is my pyproject.toml created by Poetry, yours should look similar:

And there's also an auto-generated poetry.lock file with the frozen versions. Thedirectory layout should look like the diagram on the right. Now let's add the two filesto the Git repository (we will skip showing these git operations in future steps):

Write a Simple Server¶

Python

Now let's write some Python code.

We'll create an extra src directory to include all the Python files, as demonstratedin the diagram below. This is known as the 'src layout' providing a cleaner hierarchy.

The root Python package of our project is named as gino_fastapi_demo, under which wewill create two Python modules:

  • asgi as the ASGI entry point - we'll feed it to the ASGI server

  • main to initialize our server

Here's main.py:

And we'll simply instantiate our application in asgi.py:

Then run poetryinstall to link our Python package into the PYTHONPATH indevelopment mode. We'll be able to start a Uvicorn development server after that:

The --reload option enables Uvicorn to automatically reload the server for us whenthe Python source code is updated. Now access http://127.0.0.1:8000/docs to see theSwagger UI of our new FastAPI server.

Hint

As mentioned previously, if you're in your own virtualenv, the command poetryrunuvicorn can be simplified as just uvicorn.

poetryrun is a convenient shortcut to run the following command in thevirtualenv managed by Poetry.

Add GINO Extension¶

Now let's add GINO to our server.

First of all, we need a way to configure the database. In this tutorial, we'll use theconfiguration system from Starlette.Add src/gino_fastapi_demo/config.py as follows:

This config file will load from environment variable first, if not found then from afile named .env from current path (usually the project root directory), and at lastuse the default value defined above. For example, you can either overwrite in CLIdirectly like this:

Or set them in the file .env (this file must not be committed into Git, remember toadd it to .gitignore):

Now it's time to create a PostgreSQL database and set the connection variablescorrectly here. This is usually something like createdbyourdbname, but it may varyacross different platforms, so we won't cover this part in this tutorial.

Tip

Alternatively, you could also set DB_DSN to for examplepostgresql://user:password@localhost:5432/dbname to override the other individualconfig values like DB_HOST defined before DB_DSN.

Free drupal themes. If defined, DB_DSN always have the higher priority over the individual ones,regardless of where they are defined - even if DB_HOST is defined in environmentvariable and DB_DSN is defined in .env file, DB_HOST is still ignored.Default value doesn't count.

Then, create a new Python sub-package gino_fastapi_demo.models to encapsulatedatabase-related code, and add the code below tosrc/gino_fastapi_demo/models/__init__.py:

At last, modify src/gino_fastapi_demo/main.py to install the GINO extension:

Saving the file, you should see the Uvicorn server reloads our changes and connects tothe database:

Create Models and API¶

It's time to implement the API now. Let's say we are building a user management service,through which we could add users, list users and delete users.

First of all, we need a database table users to store the data, mapped to a GINOmodel named User. We shall add the model in gino_fastapi_demo.models.users:

The model definition is simple enough to explain itself.

Then we only have to use it properly in the API implementation, for which we'll create anew Python sub-package gino_fastapi_demo.views, and a new modulegino_fastapi_demo.views.users as follows:

The APIRouter holds our new APIs locally, and init_app is used to integrate itinto our FastAPI application. Here we want some inversion of control: let's make theAPIs plugable, so that we don't have to import all possible future views manually. Weshall use the Entry Points feature to load the dependencies. Add this code below togino_fastapi_demo.main:

Hint

If you're running Python < 3.8, you'll need this importlib-metadata backport.

And call it in our application factory:

Finally, define the entry points in pyproject.toml following the Poetry documentfor plugins:

Run poetryinstall again to activate the entry points - you may need to restart theUvicorn development server manually, as the reloader cannot capture the changes we madeto pyproject.toml.

Now you should be able to see the 3 new APIs on the Swagger UI. But none of them works,because we still haven't created the database tables.

Integrate with Alembic¶

To get started with Alembic, run this command in the project root directory:

This will generate a new directory migrations where Alembic will store databasemigration revisions. At the same time, an alembic.ini file is created in the projectroot directory. Let's simply add all of them to Git control.

For Alembic to use our data models defined with GINO (and of course the databaseconfig), we need to modify migrations/env.py to connect with the GINO instance:

Then create our first migration revision with:

The generated revision file should roughly look like this:

Hint

Whenever there is a change to the database schema in the future, just modify theGINO models and run alembicrevision--autogenerate again to generate newrevisions to track the change. Remember to review the revision file - you may wantto adjust it.

Eventually, let's apply this migration, by upgrading to the latest revision:

Now all the APIs should be fully operational, try with the Swagger UI.

Write the Tests¶

In order not to break our development database with running tests, let's create aseparate database to run tests. Apply this change to gino_fastapi_demo.config:

Tutorial

Hint

You need to run createdb to actually create the database. If you have setDB_DATABASE in .env - e.g. DB_DATABASE=mydb, the name of the testingdatabase should be mydb_test. Or else, gino_fastapi_demo_test.

Then, let's create our pytest fixture in tests/conftest.py:

This fixture creates all the database tables before running the test, yield a StarletteTestClient, and drop all the tables with all the data after the test to maintain aclean environment for the next test.

Fastapi

Our application stack will look like this:

FastAPI is a fairly new python (micro) web framework with built-in support for async endpoints. Uvicorn is our chosen ASGI server. ASGI Is the asynchronous sister of python WSGI. Now we are ready for some code. First async endpoint. To configure a FastAPI service, create a python module named app.py, with the following code. FastAPI is a Python web framework designed for building fast and efficient backend APIs. It handles both synchronous and asynchronous operations and has built-in support for data validation, authentication, and interactive API documentation powered by OpenAPI.

Start a New Project¶

Instead of pip, let's use the shiny Poetry to manage our project. Follow the link toinstall Poetry, and create our newproject in an empty directory:

Then follow the Poetry guide to finish the initialization - you may say 'no' to theinteractive dependency creation, because we will add them manually. It is okay to usethe default values in the other steps of the guide, and make sure the package nameremains gino-fastapi-demo.

Add Dependencies¶

FastAPI is built on top of the Starlette framework, so we shall use the GINOextension for Starlette. Simply run:

Then let's add FastAPI, together with the lightning-fast ASGI server Uvicorn, andGunicorn as a production application server:

For database migration, we'll use Alembic. Because it uses normal DB-API, we needpsycopg here too:

At last, let's add pytest in the development environment for testing. We also want toadd the requests library to use the StarletteTestClient:

Hint

With the steps above, Poetry will automatically create a virtualenv for youbehind the scene, and all the dependencies are installed there. We will assumeusing this for the rest of the tutorial. But you're free to create your ownvirtualenv, and Poetry will honor it when it's activated.

That's all, this is my pyproject.toml created by Poetry, yours should look similar:

And there's also an auto-generated poetry.lock file with the frozen versions. Thedirectory layout should look like the diagram on the right. Now let's add the two filesto the Git repository (we will skip showing these git operations in future steps):

Write a Simple Server¶

Now let's write some Python code.

We'll create an extra src directory to include all the Python files, as demonstratedin the diagram below. This is known as the 'src layout' providing a cleaner hierarchy.

The root Python package of our project is named as gino_fastapi_demo, under which wewill create two Python modules:

  • asgi as the ASGI entry point - we'll feed it to the ASGI server

  • main to initialize our server

Here's main.py:

And we'll simply instantiate our application in asgi.py:

Then run poetryinstall to link our Python package into the PYTHONPATH indevelopment mode. We'll be able to start a Uvicorn development server after that:

The --reload option enables Uvicorn to automatically reload the server for us whenthe Python source code is updated. Now access http://127.0.0.1:8000/docs to see theSwagger UI of our new FastAPI server.

Hint

As mentioned previously, if you're in your own virtualenv, the command poetryrunuvicorn can be simplified as just uvicorn.

poetryrun is a convenient shortcut to run the following command in thevirtualenv managed by Poetry.

Add GINO Extension¶

Now let's add GINO to our server.

First of all, we need a way to configure the database. In this tutorial, we'll use theconfiguration system from Starlette.Add src/gino_fastapi_demo/config.py as follows:

This config file will load from environment variable first, if not found then from afile named .env from current path (usually the project root directory), and at lastuse the default value defined above. For example, you can either overwrite in CLIdirectly like this:

Or set them in the file .env (this file must not be committed into Git, remember toadd it to .gitignore):

Now it's time to create a PostgreSQL database and set the connection variablescorrectly here. This is usually something like createdbyourdbname, but it may varyacross different platforms, so we won't cover this part in this tutorial.

Tip

Alternatively, you could also set DB_DSN to for examplepostgresql://user:password@localhost:5432/dbname to override the other individualconfig values like DB_HOST defined before DB_DSN.

Free drupal themes. If defined, DB_DSN always have the higher priority over the individual ones,regardless of where they are defined - even if DB_HOST is defined in environmentvariable and DB_DSN is defined in .env file, DB_HOST is still ignored.Default value doesn't count.

Then, create a new Python sub-package gino_fastapi_demo.models to encapsulatedatabase-related code, and add the code below tosrc/gino_fastapi_demo/models/__init__.py:

At last, modify src/gino_fastapi_demo/main.py to install the GINO extension:

Saving the file, you should see the Uvicorn server reloads our changes and connects tothe database:

Create Models and API¶

It's time to implement the API now. Let's say we are building a user management service,through which we could add users, list users and delete users.

First of all, we need a database table users to store the data, mapped to a GINOmodel named User. We shall add the model in gino_fastapi_demo.models.users:

The model definition is simple enough to explain itself.

Then we only have to use it properly in the API implementation, for which we'll create anew Python sub-package gino_fastapi_demo.views, and a new modulegino_fastapi_demo.views.users as follows:

The APIRouter holds our new APIs locally, and init_app is used to integrate itinto our FastAPI application. Here we want some inversion of control: let's make theAPIs plugable, so that we don't have to import all possible future views manually. Weshall use the Entry Points feature to load the dependencies. Add this code below togino_fastapi_demo.main:

Hint

If you're running Python < 3.8, you'll need this importlib-metadata backport.

And call it in our application factory:

Finally, define the entry points in pyproject.toml following the Poetry documentfor plugins:

Run poetryinstall again to activate the entry points - you may need to restart theUvicorn development server manually, as the reloader cannot capture the changes we madeto pyproject.toml.

Now you should be able to see the 3 new APIs on the Swagger UI. But none of them works,because we still haven't created the database tables.

Integrate with Alembic¶

To get started with Alembic, run this command in the project root directory:

This will generate a new directory migrations where Alembic will store databasemigration revisions. At the same time, an alembic.ini file is created in the projectroot directory. Let's simply add all of them to Git control.

For Alembic to use our data models defined with GINO (and of course the databaseconfig), we need to modify migrations/env.py to connect with the GINO instance:

Then create our first migration revision with:

The generated revision file should roughly look like this:

Hint

Whenever there is a change to the database schema in the future, just modify theGINO models and run alembicrevision--autogenerate again to generate newrevisions to track the change. Remember to review the revision file - you may wantto adjust it.

Eventually, let's apply this migration, by upgrading to the latest revision:

Now all the APIs should be fully operational, try with the Swagger UI.

Write the Tests¶

In order not to break our development database with running tests, let's create aseparate database to run tests. Apply this change to gino_fastapi_demo.config:

Hint

You need to run createdb to actually create the database. If you have setDB_DATABASE in .env - e.g. DB_DATABASE=mydb, the name of the testingdatabase should be mydb_test. Or else, gino_fastapi_demo_test.

Then, let's create our pytest fixture in tests/conftest.py:

This fixture creates all the database tables before running the test, yield a StarletteTestClient, and drop all the tables with all the data after the test to maintain aclean environment for the next test.

Here's a sample test in tests/test_users.py:

Then run the test:

Notes for Production¶

Given the popularity of Docker/Kubernetes, we'll build a Dockerfile for our demo:

In this Dockerfile, we used 2 phases to separate the building from the productionimage to reduce target artifact size. Also, we are using Gunicorn withUvicornWorker from Uvicorn as the worker class for best production reliability.

Let's review what we have in the project.

This is the end of the tutorial to build a demo. Below is an incomplete checklist togo live:

  • Set DB_RETRY_LIMIT to a larger number to allow staring the application serverbefore the database is fully ready.

  • Implement the same retry logic in migrations/env.py so that Alembic gets the samefunctionality.

  • Enable DB_SSL if needed.

  • Write a docker-compose.yml for other developers to get a quick taste or even useit for development.

  • Enable CI, install pytest-cov and use --cov-fail-under to guarantee coverage.

  • Integrate static code analysis tools and security/CVE checking tools.

  • Automate Alembic upgrade properly - e.g. after new version is deployed.

  • Be aware of the common security attacks like CSRF, XSS, etc.

  • Write load tests.

Python Fastapi Tutorial

Again, the source code of the demo is available here,and the source of this tutorial is here.Please feel free to submit PRs to fix issues or add your thoughts. Happy hacking!





broken image