Skip to content

App structure

tldr

๐Ÿ“ฆfastapi-app
 โ”ฃ ๐Ÿ“‚app
 โ”ƒ โ”ฃ ๐Ÿ“‚api - endpoints
 โ”ƒ โ”ฃ ๐Ÿ“‚core - configurations
 โ”ƒ โ”ฃ ๐Ÿ“‚crud - crud
 โ”ƒ โ”ฃ ๐Ÿ“‚database - database session and base class
 โ”ƒ โ”ฃ ๐Ÿ“‚models - SQLAlchemy models
 โ”ƒ โ”ฃ ๐Ÿ“‚schemas - Pydantic schemas
 โ”ƒ โ”ฃ ๐Ÿ“‚tests - pytest tests
  ...

When you run the cfa create command with auth none or backend options, a following file tree will be created:

๐Ÿ“ฆfastapi-app
 โ”ฃ ๐Ÿ“‚app
 โ”ƒ โ”ฃ ๐Ÿ“‚api
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚v1
 โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚endpoints
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œitem.py
 โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œdeps.py
 โ”ƒ โ”ฃ ๐Ÿ“‚core
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œconfig.py
 โ”ƒ โ”ฃ ๐Ÿ“‚crud
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œbase.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œcrud_example.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œcrud_item.py
 โ”ƒ โ”ฃ ๐Ÿ“‚database
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œbase.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œinitialize.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œsession.py
 โ”ƒ โ”ฃ ๐Ÿ“‚models
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œexample.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œitem.py
 โ”ƒ โ”ฃ ๐Ÿ“‚schemas
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œexample.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œitem.py
 โ”ƒ โ”ฃ ๐Ÿ“‚tests
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚api
 โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚v1
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œtest_item.py
 โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œconftest.py
 โ”ƒ โ”— ๐Ÿ“œ__init__.py
 โ”ฃ ๐Ÿ“‚git_hooks
 โ”ƒ โ”ฃ ๐Ÿ“œpre-commit.sh
 โ”ƒ โ”— ๐Ÿ“œpre-push.sh
 โ”ฃ ๐Ÿ“œ.gitignore
 โ”ฃ ๐Ÿ“œDockerfile
 โ”ฃ ๐Ÿ“œPipfile
 โ”ฃ ๐Ÿ“œPipfile.lock
 โ”ฃ ๐Ÿ“œREADME.md
 โ”ฃ ๐Ÿ“œcreate_git_hooks.sh
 โ”ฃ ๐Ÿ“œdb.db
 โ”ฃ ๐Ÿ“œdocker-compose.yml
 โ”ฃ ๐Ÿ“œenv_example
 โ”ฃ ๐Ÿ“œinit_db.sh
 โ”— ๐Ÿ“œmain.py

auth = self adds and updates several files:

๐Ÿ“ฆfastapi-app
 โ”ฃ ๐Ÿ“‚app
 โ”ƒ โ”ฃ ๐Ÿ“‚api
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚v1
 โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚endpoints
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œauth.py
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œitem.py
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œuser.py
 โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œdeps.py
 โ”ƒ โ”ฃ ๐Ÿ“‚core
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œconfig.py
 โ”ƒ โ”ฃ ๐Ÿ“‚crud
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œbase.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œcrud_item.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œcrud_user.py
 โ”ƒ โ”ฃ ๐Ÿ“‚database
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œbase.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œinitialize.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œsession.py
 โ”ƒ โ”ฃ ๐Ÿ“‚models
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œitem.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œuser.py
 โ”ƒ โ”ฃ ๐Ÿ“‚schemas
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œauth.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œitem.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œuser.py
 โ”ƒ โ”ฃ ๐Ÿ“‚tests
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚api
 โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“‚v1
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œtest_item.py
 โ”ƒ โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œtest_user.py
 โ”ƒ โ”ƒ โ”ƒ โ”— ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”ƒ โ”— ๐Ÿ“œconftest.py
 โ”ƒ โ”ฃ ๐Ÿ“œ__init__.py
 โ”ƒ โ”— ๐Ÿ“œutils.py
 โ”ฃ ๐Ÿ“œ.gitignore
 โ”ฃ ๐Ÿ“œDockerfile
 โ”ฃ ๐Ÿ“œPipfile
 โ”ฃ ๐Ÿ“œPipfile.lock
 โ”ฃ ๐Ÿ“œREADME.md
 โ”ฃ ๐Ÿ“œcreate_git_hooks.sh
 โ”ฃ ๐Ÿ“œdocker-compose.yml
 โ”ฃ ๐Ÿ“œenv_example
 โ”ฃ ๐Ÿ“œinit_db.sh
 โ”— ๐Ÿ“œmain.py

./

Place for some general files, environment files and other configurations

./app/api/

Folder where endpoints should be specified. E.g. to create a new endpoint for table. All endpoints related to a single table should be grouped in the same file, and the router should be then included in the ./app/api/v1/__init__.py file.

./app/core/

A place for app configurations

./app/crud/

Crud classes utilizing models and schemas for a table in the database. Each crud class should inherit from the CRUDBase class which contains some basic methods for:

  • creating (create(db, obj_in))
  • reading (read(db, item_id), read_multi(db, offset, limit))
  • updating (update(db, item_id, obj_in))
  • deleting (delete(db, item_id))

objects in the database

./app/database/

Database base class base.py, which is inherited by the models, and session (session.py). The initialize.py contains db initialization scripts useful for development. However, in production the tables and migrations should be handled ideally by alembic

./app/models/

Database tables models. Simple classes that inherit from the base class specifying the columns, datatypes, indexes, foreign keys etc. For more info see SQLAlchemy docs

./app/schemas/

pydantic schemas declaring data validation. Schemas are used for database manipulations (create, read, update) as well as for validating requests and specifying response schemas. E.g. imagine we would like to add a post endpoint for creating new items in the database (in the ./app/api/v1/endpoints/item.py file). It would look something like this:

@router.post('/', response_model=schemas.Item)
def get_items(
        obj_in: schemas.ItemCreate,
        db: Session = Depends(deps.get_db)
) -> Any:
    return crud_item.create(db=db, obj_in=obj_in)

In the first line, we specify the schema of the json object that will be returned to the user. In the third line, we specify what we expect. If the incoming object does not correspond with the schema, the endpoint will throw an Error.

./app/tests/

Tests. See pytest for more info. In short, you simply write functions (with the word test in their names - this is very important) in a file with the word test in its name and then include some assertions inside of the functions. Running the command pytest will detect all these functions and run them.

For example to test if GET item endpoint works, simply create a file ./app/tests/api/v1/test_item.py with following content:

from fastapi.testclient import TestClient

from app.core.config import settings


def test_item_get_multi(client: TestClient) -> None:
    """Test the get items endpoint"""
    r = client.get(f"{settings.API_VERSION}/item/")
    assert r.status_code == 200

    all_runs = r.json()
    assert len(all_runs) > 0

There are some configurations already in place. See the app/tests/conftest.py for more info. You should be fine with the default settings. For more info see the official documentation