Giter VIP home page Giter VIP logo

jonra1993 / fastapi-alembic-sqlmodel-async Goto Github PK

View Code? Open in Web Editor NEW
876.0 23.0 142.0 11.03 MB

This is a project template which uses FastAPI, Pydantic 2.0, Alembic and async SQLModel as ORM. It shows a complete async CRUD using authentication and role base access control.

License: MIT License

Dockerfile 0.43% Python 96.23% Mako 0.36% Makefile 2.33% Polar 0.65%
fastapi sqlmodel sqlalchemy alembic postgresql asyncio fastapi-crud fastapi-async-db fastapi-sqlmodel fastapi-pagination

fastapi-alembic-sqlmodel-async's Introduction

Async configuration for FastAPI and SQLModel

This is a project template which uses FastAPI, Alembic and async SQLModel as ORM which already is compatible with Pydantic V2 and SQLAlchemy V2.0. It shows a complete async CRUD template using authentication. Our implementation utilizes the newest version of FastAPI and incorporates typing hints that are fully compatible with Python >=3.10. If you're looking to build modern and efficient web applications with Python, this template will provide you with the necessary tools to get started quickly. You can read a short article with the motivations of starting this project in Our Journey Using Async FastAPI.

If you are looking to create new a project from zero, I recommend you to use create-fastapi-project.

Do you need assistance, trainning or support for your newxt project using fastapi?. Please don't hesitate to get in touch with our team at [email protected] or schedule a meeting with us here.

Why Use This Template?

Developing web applications can be a challenging process, especially when dealing with databases, authentication, asynchronous tasks, and other complex components. Our template is designed to simplify this process and offer you a solid starting point. Some of the highlights of this template include:

  • FastAPI Integration: FastAPI is a modern and efficient web framework that allows you to quickly and easily create APIs. This template uses the latest features of FastAPI and offers type hints that are compatible with Python 3.10 and later versions.
  • Asynchronous Database Management: We use SQLModel, an asynchronous ORM library, to interact with the database efficiently and securely.
  • Asynchronous Tasks with Celery: This template includes examples of how to execute asynchronous and scheduled tasks using Celery, which is ideal for operations that require significant time or resources.
  • Authentication and Authorization: We implement JWT-based authentication and role-based access control to ensure that your APIs are secure and protected.
  • Documentation and Automated Testing: The template is configured to automatically generate interactive documentation for your APIs. It also includes automated tests using pytest to ensure code quality.
  • Development Best Practices: We apply code formatting, type checking, and static analysis tools to ensure that the code is readable, robust, and reliable.

Table of Contents

  1. Prerequisites
  2. Run the project using Docker containers and forcing build containers
  3. Run project using Docker containers
  4. Setup database with initial data
  5. ERD Database model
  6. Containers architecture
  7. Preview
  8. Static files
  9. Minio server
  10. Celery
  11. Run Alembic migrations (Only if you change the DB model)
  12. Production Deployment
  13. Database unique IDs
  14. Code Style
  15. SonarQube static analysis
  16. Testing
  17. Type checker
  18. Basic chatbot example with Langchain and OpenAI
  19. Inspiration and References
  20. TODO List
  21. License

Prerequisites

Set environment variables

Create an .env file on root folder and copy the content from .env.example. Feel free to change it according to your own configuration.

Docker engine

This project utilizes Docker and Docker Compose, so please ensure that you have installed the latest version compatible with your operating system. If you haven't already installed Docker, you can find detailed instructions on how to do so here. Docker desktop can be good for a dev computer.

You can check if it is installed with this command

docker --version

Make

"Make" is a build automation tool that is primarily used to manage the compilation and building of software projects. It reads a file called a "Makefile" which specifies a set of rules and dependencies for building a project, and then executes the necessary commands to build the project according to those rules. Depending of your OS you will requiere to install it in different ways.

Mac

xcode-select --install

Ubuntu

sudo apt-get install build-essential
sudo apt-get -y install make

You can check if it is installed with this command

make --version

Python ">3.9,<3.12"

If you haven't already installed Python. You can download and install python from here.

You can check yu python version:

python --version

Poetry

Python Poetry is a tool for dependency management and packaging in Python. It provides a modern and efficient approach to managing Python projects' dependencies, virtual environments, and packaging. You can find detailed instructions on how install it here. Poetry manages packages in pyproject.toml file; In this project you can find it in the folder backend/app.

You can check if it is installed with this command

poetry --version

Dev tip to activate virtual environment

When you are opening python files do this cna help you to vscode detect installed packages.

cd backend/app/
poetry shell

After that you can show the interpreted path. You can copy that path and set as the default for the project in vscode. Press on Enter interpreter path .. and past path.

Run the project using Docker containers and forcing build containers

Using docker compose command

docker compose -f docker-compose-dev.yml up --build

Using Makefile command

make run-dev-build

Run project using Docker containers

Using docker compose command

docker compose -f docker-compose-dev.yml up

Using Makefile command

make run-dev

Setup database with initial data

This creates sample users on database.

Using docker compose command

docker compose -f docker-compose-dev.yml exec fastapi_server python app/initial_data.py

Using Makefile command

make init-db

Any of the above commands creates three users with the following passwords:

You can connect to the Database using pgAdmin4 and use the credentials from .env file. Database port on local machine has been configured to 5454 on docker-compose-dev.yml file

(Optional) If you prefer you can run pgAdmin4 on a docker container using the following commands, they should executed on different terminals:

Starts pgadmin

make run-pgadmin

This starts pgamin in http://localhost:15432. When connecting to db server introduce the password by default it is postgres if you didn't change it in .env file.

ERD Database model

Containers architecture

As this project uses Caddy as a reverse proxy, which uses namespaces routing, you can access the documentation with the following path http://fastapi.localhost/docs

Preview

Static files

All files on static folder will be served by Caddy container as static files. You can check it with this link http://static.localhost

Minio server

This template allows users can upload their photos. The images are stored using the open source Object Storage Service (OSS) minio, which provides storage of images using buckets in a secure way through presigned URLs.

  • Minio credentials -> username: minioadmin and password: minioadmin

Celery

Celery is a distributed task queue that allows developers to run asynchronous tasks in their applications. It is particularly useful for tasks that are time-consuming, require heavy computation or access external services, and can be run independently of the main application. It also offers features such as task scheduling, task prioritization, and retries in case of failure.

Celery Beat is an additional component of Celery that allows developers to schedule periodic tasks and intervals for their Celery workers. It provides an easy-to-use interface for defining task schedules and supports several scheduling options such as crontab, interval, and relative.

You can see the architecture used in this project which uses Redis as celery broker and the current postgres database as celery backend. It also uses celery-sqlalchemy-scheduler to store celery beats task into database so they can mutated.

Within the natural_language endpoints, you can access a sample application that demonstrates not only synchronous prediction of machine learning models but also batch prediction. Additionally, there are examples of how to schedule periodic tasks using Celery Beat in the periodic_tasks endpoints.

Run Alembic migrations (Only if you change the DB model)

Using docker compose command

docker compose -f docker-compose-dev.yml exec fastapi_server alembic revision --autogenerate
docker compose -f docker-compose-dev.yml exec fastapi_server alembic upgrade head

Using Makefile command

make add-dev-migration

Production Deployment

Remember to use a persistant PostgreSQL database, update the new credentials on .env file and use this command to run the project in a production environment. For testing this configuration on localhost you can uncomment the database container and depends_on of fastapi container otherwise it will not work on a local environment.

Using docker compose command

docker compose up --build

Database unique IDs

Generating and using unique IDs is a really important desicion when starting a new project and its most common use is as primary keys for database tables. This project uses a custom UUID7 Draft04 implementation to make it simple to use and take advantage of UUID type of PostgreSQL. UUID7 combines timestamp with random data in order to help to convert data into time-stamped sequencially. If you are looking for another alternatives for tables IDs like Snowflakes, ULID, KSUID, pushID, xid among others you can check these references.

Code Style

Code style: black

To ensure a standardized code style this project uses black and ruff. If you want to change the config rules you can edit both ruff and black rules in the pyproject.toml file.

To reformat files execute the following command

make formatter

To run lint, you can run the following command:

make lint

To run lint in watch mode, you can run the following command:

make lint-watch

To run lint and try to fix the errors, you can run the following command:

make lint-fix

SonarQube static analysis

SonarQube is an automatic code review tool that detects bugs, vulnerabilities, and code smells in a project. You can read this post in order to have a better understanding about what SonarQube can do.

The following steps can help you to run a local static code analysis

  1. Start SonarQube container
make run-sonarqube

The above code starts SonarQube at localhost:9000. You can login using this credentials -> username: admin and password: admin, after that it should requiere you change your password.

  1. Add new project

  1. Copy projectKey and login and replace on backend/sonar-project.properties file.

backend/sonar-project.properties file

# Organization and project keys are displayed in the right sidebar of the project homepage
sonar.organization=my_organization
sonar.projectKey=fastapi-alembic-sqlmodel-async
sonar.host.url=http://host.docker.internal:9000
sonar.login=157cc42f5b2702f470af3466610eebf38551fdd7

# --- optional properties ---

# defaults to project key
sonar.projectName=fastapi-alembic-sqlmodel-async
# defaults to 'not provided'
sonar.projectVersion=1.0

# Path is relative to the sonar-project.properties file. Defaults to .
sonar.sources=app

# Encoding of the source code. Default is default system encoding
sonar.sourceEncoding=UTF-8
  1. Run the following command to execute a new code scan
make run-sonar-scanner

When the build is successful, you can see the SonarQube screen automatically refreshed with the analysis. If you want to export a report, you can check this this post.

Testing

Testing in FastAPI with pytest involves creating test functions that simulate HTTP requests to the API endpoints and verifying the responses. This approach allows us to conduct both unit tests for individual functions and integration tests for the entire application.

To perform tests in this project, we utilize two essential libraries: pytest and pytest-asyncio.

However, when testing FastAPI endpoints that utilize async connections with the database and a pool strategy, there is a trick to be aware of. The recommended approach is to create an isolated testing environment that connects to the database using the "poolclass": NullPool parameter on the engine. This helps to avoid potential issues related to tasks being attached to different loops. For more details on this, you can refer to the following references: Fastapi testing RuntimeError: Task attached to a different loop and Connection Pooling.

To execute the tests, follow these steps:

  1. Start the testing environment using the command:
make run-test
  1. Once the testing environment is up and running, open another terminal and run the tests with the following command:
make pytest

Type checker

Python's type hints, introduced in PEP 484 and fully embraced in later versions of Python, allow you to specify the expected types of variables, function parameters, and return values. It is really good how fastapi documentation promotes type hints so this code base tryies to use this tool the most posible because type hints make the code more self-documenting by providing clear information about what types of values a function or variable can hold and they catch type-related errors at compile time, before the code is executed.

This project uses mypy a popular static type checker for Python. If you want to change the config rules you can edit the rules in the pyproject.toml file.

To execute Type checking, run this command:

make mypy

Basic chatbot example with Langchain and OpenAI

In addition to its core features, this project template demonstrates how to integrate an basic chatbot powered by Langchain and OpenAI through websockets. You can use PieSocket Websocket Tester to test websockets.

To begin experimenting with the basic chatbot, follow these steps:

  1. Obtain an OpenAI API Key: You'll need to set the OPENAI_API_KEY environment variable, which you can obtain from OpenAI's platform.

  2. Test Websocket Connection: You can test the websocket connection by using the following URL: ws://fastapi.localhost/chat/<USER_ID>. Replace <USER_ID> with a user identifier of your choice. It should be the ID of your user.

  3. Sending and Receiving Messages: You should be able to send messages to the chatbot using the provided websocket connection. To do this, use the following message structure:

    {"message":"Hello world"}

    Once you send a message, the chatbot will respond with generated responses based on the content of your input.

Inspiration and References

TODO List:

  • Add Custom Response model
  • Create sample one to many relationship
  • Create sample many to many relationship
  • Add JWT authentication
  • Add Pagination
  • Add User birthday field with timezone
  • Add static server
  • Add basic RBAC (Role base access control)
  • Add sample heroes, teams and groups on init db
  • Add cache configuration using fastapi-cache2 and redis
  • Create a global database pool of sessions to avoid to pass the session as dependency injection on each handle
  • Refactor tablename to Pascal case
  • Add one to one relationship sample
  • Add sample to upload images and store them using minio
  • Invalidate access and refresh tokens when the password is changed using Redis
  • Add shortcuts using a Makefile
  • Add sample async, sync and concurrent functions using asyncer
  • Add Black formatter and flake8 lint (Rasa as reference)
  • Add static code analysis using SonarQube
  • Function return type annotations to declare the response_model (fastapi > 0.89.0)
  • Add export report api in csv/xlsx files using StreamingResponse
  • Add Github actions automation for deploy on Elastic Beanstalk - AWS
  • Database query optimization. Many-Many use "selectin" and One-One and One-Many use "joined" issue
  • Add Enum sample column
  • Add docstrings
  • Install pg_trgm by code and add a query for smart search of users by name
  • Upgrade typing (Compatible just with python > 3.10)
  • Add sample transformers NLP models and use them globally
  • Add Celery samples for tasks, and schedule tasks
  • Migrate from traefik reverse proxy to Caddy reverse proxy for automatic ssl
  • Add fastapi limiter to natural language endpoints
  • Add websocket conneting with chatgpt
  • Setup testing configuracion
  • Add sample composition using pydantic
  • Add a nextjs sample frontend
  • Add testing
  • Add jsonb field on table sample
  • Make that celery-sqlalchemy-scheduler works async
  • Add AuthZ using oso
  • Add SSL to reverse proxy on prod
  • Add instructions on doc for production deployment using github actions and dockerhub (CI/CD)
  • Add production deployment orchestation using terraform + Elastic Beanstalk - AWS
  • Convert repo into template using cookiecutter

Support and Maintenance

fastapi-alembic-sqlmodel-async is supported by the Allient development team. Our team is composed by a experienced professionals specializing in FastAPI projects and NLP. Please don't hesitate to get in touch with our team at [email protected] or schedule a meeting with us here.

PR are welcome โค๏ธ

License

License

  • This project is licensed under the terms of the MIT license

fastapi-alembic-sqlmodel-async's People

Contributors

8thgencore avatar ae-mc avatar bazylhorsey avatar dongfengweixiao avatar hambergerpls avatar jamazi avatar jonra1993 avatar jymchng avatar kabirivan avatar leoor22 avatar stefan2409 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fastapi-alembic-sqlmodel-async's Issues

How to manage sub-domains?

I want to modify sub-domains names and paths but I couldn't know where this template configure them?
Usually I put sub-domains confg files in /etc/nginx/conf.d/ but I didn't see which script control them for this template!

Questions concerning Relationship definition

Hi @jonra1993 ,
I have questions on the Relationship usage, class Hero:

Why not add a created_by_id to the HeroBase instead of Hero? Why is it Optional, or can it be required, since we always have a user that created a hero?

Where can I find information, which sa_relationship_kwargs I should use, and should there be another one to define what happens if records are deleted?

And also: why no backpopulates with the created_by relationship?
Do we really need the primaryjoin argument?

Calling postgres from celery

Right now we can use syncify with the CRUD functions inside celery, but aysncpg is still in charge of the session. This seems suboptimal but switching into psychopg seems like a lot of duplicated work? Is using asyncpg fine for this use-case?

Add user to group does not work

How to reproduce the error:

  1. Start dev environment
  2. Make migrations
  3. Login as admin user via Swagger UI
  4. Create new user
  5. Create new group
  6. Add created user to created group

I'm getting the following error when I try to add a user to a group (The user and group ids are correct):

fastapi_server  | INFO:     172.20.0.6:52558 - "POST /api/v1/group/add_user/79c7fc32-99fc-4a0a-ac3e-6db8516fb8b2/f99a6f9c-4c16-42fc-b79e-e8e1e982346c HTTP/1.1" 500 Internal Server Error
fastapi_server  | ERROR:    Exception in ASGI application
fastapi_server  | Traceback (most recent call last):
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/httptools_impl.py", line 372, in run_asgi
fastapi_server  |     result = await app(self.scope, self.receive, self.send)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
fastapi_server  |     return await self.app(scope, receive, send)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/fastapi/applications.py", line 261, in __call__
fastapi_server  |     await super().__call__(scope, receive, send)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
fastapi_server  |     await self.middleware_stack(scope, receive, send)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
fastapi_server  |     raise exc
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
fastapi_server  |     await self.app(scope, receive, _send)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/middleware/cors.py", line 92, in __call__
fastapi_server  |     await self.simple_response(scope, receive, send, request_headers=headers)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/middleware/cors.py", line 147, in simple_response
fastapi_server  |     await self.app(scope, receive, send)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
fastapi_server  |     raise exc
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
fastapi_server  |     await self.app(scope, receive, sender)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
fastapi_server  |     raise e
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
fastapi_server  |     await self.app(scope, receive, send)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 656, in __call__
fastapi_server  |     await route.handle(scope, receive, send)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 259, in handle
fastapi_server  |     await self.app(scope, receive, send)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 61, in app
fastapi_server  |     response = await func(request)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 227, in app
fastapi_server  |     raw_response = await run_endpoint_function(
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 160, in run_endpoint_function
fastapi_server  |     return await dependant.call(**values)
fastapi_server  |   File "/code/./app/api/api_v1/endpoints/group.py", line 70, in add_user_to_group
fastapi_server  |     group = await crud.group.add_user_to_group(db_session, user=user, group_id=group_id)
fastapi_server  |   File "/code/./app/crud/crud_group.py", line 16, in add_user_to_group
fastapi_server  |     group = await super().get(db_session, group_id)
fastapi_server  | TypeError: get() takes 2 positional arguments but 3 were given

I don't understand the TypeError because we only give 2 arguments not 3 like in the error message....

Auth Suggestion

I see on your todo list to use KeyCloak. Might I suggest a cloud solution instead such as:

  • Auth0 - highly recommend
  • Cognito - good, but can get complex

Reasoning:
Keycloak is very difficult to extend off of, plus it introduces Java to the project.
Our team has played with it and we were not pleased with the results.

I would be happy to share code on how easy it is to integrate Auth0 with FastAPI, on your project I believe it can be done in less than 20 lines of code.

I want to use mysql(mariadb)

I want to use MySQLDsn and 'mysql+asyncmy',

but I got the error message

 from pydantic import BaseSettings, PostgresDsn, MySQLDsn, validator, EmailStr, AnyHttpUrl
ImportError: cannot import name 'MySQLDsn' from 'pydantic' (/usr/local/lib/python3.10/site-packages/pydantic/__init__.cpython-310-x86_64-linux-gnu.so)

Is it possible to import MySQLDsn or MariadDBDsn.

base route always returns "Hello World 2"

Looking at the code, it seems what was anticipated would it would be a sequence for every request looking like: 2, 4, 8, 16, 32, etc.
This seems to always return the same response which would indicate the global is not actually iterating as expected.
Great code by the way, I was completely unaware of async lifetimes and managing contexts.

How to pass `models_as_dict=False` into those Response Models?

Reference: https://stackoverflow.com/questions/76075777/fastapi-returning-a-listreadschema-as-another-schema/76076717#76076717

I have two tables, Category and Project:

from pydantic import BaseModel
from typing import List, Optional
from sqlmodel import SQLModel, Field, Relationship
import inspect
from enum import Enum

class Category(SQLModel, table=True):
    __table_name__ = 'category'
    id: Optional[int] = Field(
        primary_key=True,
        index=True,
        nullable=False,
    )
    category: str
    project_id: Optional[int] = Field(
        default=None, foreign_key='project.id', nullable=False)

class Project(SQLModel, table=True):
    __table_name__ = 'project'
    id: Optional[int] = Field(
        primary_key=True,
        index=True,
        nullable=False,
    )
    categories: List[Category] = Relationship(
        back_populates='project',
        sa_relationship_kwargs={
            "lazy": "selectin",
            'cascade': 'all,delete,delete-orphan',
            "primaryjoin": "category.project_id==project.id",
        })

Definition of CategoryEnum:

class CategoryEnum(str, Enum):
    A = "A"
    B = "B"

I have two read schemas:

class ICategoryRead(BaseModel):
    category: CategoryEnum

class IProjectRead(BaseModel):
    categories: List[ICategoryRead]

Let's say now I have this record:

one_project_read = IProjectRead(
    categories=[
        ICategoryRead(category=CategoryEnum.A)
    ]
)

When I call one_project_read.json() that is what I get:

'{"categories": [{"category": "A"}]}'

I want it to be:

'{"categories": ["A"]}'

I managed to achieve this by doing two things:

  1. Add class Config: ... to IProjectRead:
class IProjectRead(BaseModel):
    categories: List[ICategoryRead]
        
    class Config:
        json_encoders = {                                  <========= ADD THIS
            ICategoryRead: lambda v: v.category,
        }
  1. In my own small testing, pass the keyword argument models_as_dict=False into .json() method:
>>> one_project_read.json(models_as_dict=False)
... '{"categories": ["A"]}'

Finally, my question is, how do I let your awesome create_response function knows that I want models_as_dict=False?

Thank you.

Add instructions for production deployment using github actions and dockerhub (CI/CD)

Hello @jonra1993 , Thank you for the amazing work.
I see that you put "Add instructions for production deployment using github actions and dockerhub (CI/CD)" on your to do list, It is excatly what I need as a bigginer, Is there any way to put it on your top todolist for the next task, will be really appreciated.
PS: Can you make it as detailled as possible, and as readdy to use as possible, if you can of course
Thank you verry much

Ability to omit table joining if relations are not needed by API consumer

After turning on SQL echo, it seems that there can be quite a bit more querying than needs to necessarily happen.
Queries can be much faster if related tables are not unwrapped.

Example body param:

    expand_fields: Optional[bool] = Query(
        default=False, description="Expand relational fields"
    ),

It would be EVEN BETTER if you could only select from one of the related fields. This could easily be implemented with an additional Enum representing the base table.
Example body param:

    expand_fields: Optional[List[Enum[{SOMETHING}]] = Query(
        default={SOMETHING}, description="Expand relational fields"
    ),

In cases where the expand_fields list is empty, it should not show the unwrapped location, and more importantly not perform the selectin.

A short version for the image object in the schemas

Now we get the user like this

{
  "message": "Data got correctly",
  "meta": {},
  "data": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "first_name": "string",
    "last_name": "string",
    .......
    "image": {
      "file_format": "string",
      "width": 0,
      "height": 0,
      "media": {
        "title": "string",
        "description": "string",
        "path": "string",
        "id": "string",
        "link": "string"
      }
    }
  }
}

But the image object takes up a lot of information.
This is especially clearly seen when we get a list of users and unnecessary fields are inserted.
it happens that need when only the url is needed from the image.

Example:

{
  "message": "Data got correctly",
  "meta": {},
  "data": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "first_name": "string",
    "last_name": "string",
    .......
    "image_url": "string"
  }
}

It would be nice if there was an alternative scheme.

How to return a field that consolidate the attributes of other fields?

Let's say I have a Project table and it has these two fields:

cover_img: ImageMedia = Relationship(
        sa_relationship_kwargs={
            "lazy": "selectin",
            "primaryjoin": "Project.cover_img_id==ImageMedia.id",
        }
    )

logo_img: ImageMedia = Relationship(
        sa_relationship_kwargs={
            "lazy": "selectin",
            "primaryjoin": "Project.logo_img_id==ImageMedia.id",
        }
    )

How do I have an extra field call images such that the read of a record of Project will return the following?

images: {
    'cover_img': 'UUID-1',
    'logo_img': 'UUID-2',
}

ERROR [alembic.util.messaging] Can't locate revision identified

I use recent commit 81983dc

fastapi_server  | 2022-07-30 12:32:43,203 INFO sqlalchemy.engine.Engine select pg_catalog.version()
fastapi_server  | INFO  [sqlalchemy.engine.Engine] select pg_catalog.version()
fastapi_server  | INFO  [sqlalchemy.engine.Engine] [raw sql] ()
fastapi_server  | 2022-07-30 12:32:43,205 INFO sqlalchemy.engine.Engine [raw sql] ()
fastapi_server  | 2022-07-30 12:32:43,343 INFO sqlalchemy.engine.Engine select current_schema()
fastapi_server  | INFO  [sqlalchemy.engine.Engine] select current_schema()
fastapi_server  | 2022-07-30 12:32:43,343 INFO sqlalchemy.engine.Engine [raw sql] ()
fastapi_server  | INFO  [sqlalchemy.engine.Engine] [raw sql] ()
fastapi_server  | 2022-07-30 12:32:43,385 INFO sqlalchemy.engine.Engine show standard_conforming_strings
fastapi_server  | 2022-07-30 12:32:43,388 INFO sqlalchemy.engine.Engine [raw sql] ()
fastapi_server  | INFO  [sqlalchemy.engine.Engine] show standard_conforming_strings
fastapi_server  | INFO  [sqlalchemy.engine.Engine] [raw sql] ()
fastapi_server  | INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
fastapi_server  | INFO  [alembic.runtime.migration] Will assume transactional DDL.
fastapi_server  | INFO  [sqlalchemy.engine.Engine] BEGIN (implicit)
fastapi_server  | INFO  [sqlalchemy.engine.Engine] select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where pg_catalog.pg_table_is_visible(c.oid) and relname=%s
fastapi_server  | INFO  [sqlalchemy.engine.Engine] [generated in 0.00079s] ('alembic_version',)
fastapi_server  | 2022-07-30 12:32:43,428 INFO sqlalchemy.engine.Engine BEGIN (implicit)
fastapi_server  | 2022-07-30 12:32:43,431 INFO sqlalchemy.engine.Engine select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where pg_catalog.pg_table_is_visible(c.oid) and relname=%s
fastapi_server  | 2022-07-30 12:32:43,439 INFO sqlalchemy.engine.Engine [generated in 0.00079s] ('alembic_version',)
fastapi_server  | 2022-07-30 12:32:43,467 INFO sqlalchemy.engine.Engine SELECT alembic_version.version_num 
fastapi_server  | FROM alembic_version
fastapi_server  | 2022-07-30 12:32:43,544 INFO sqlalchemy.engine.Engine [generated in 0.07736s] ()
fastapi_server  | INFO  [sqlalchemy.engine.Engine] SELECT alembic_version.version_num 
fastapi_server  | FROM alembic_version
fastapi_server  | INFO  [sqlalchemy.engine.Engine] [generated in 0.07736s] ()
fastapi_server  | INFO  [sqlalchemy.engine.Engine] ROLLBACK
fastapi_server  | 2022-07-30 12:32:44,291 INFO sqlalchemy.engine.Engine ROLLBACK
fastapi_server  | ERROR [alembic.util.messaging] Can't locate revision identified by 'f157607648d6'
fastapi_server  | FAILED: Can't locate revision identified by 'f157607648d6'
fastapi_server exited with code 255

Refresh and Access tokens are not included in the Redis database

During use, I noticed that Refresh and Access tokens do not get into the Redis database

try to connect and see what data is in the redis database

During authorization, after using the get_valid_tokens function, we get an empty set().
And that's why writing to redis doesn't happen

  valid_access_tokens = await get_valid_tokens(redis_client, user.id, TokenType.ACCESS)
  if valid_access_tokens:
      await add_token_to_redis(
          redis_client,
          user,
          access_token,
          TokenType.ACCESS,
          settings.ACCESS_TOKEN_EXPIRE_MINUTES,
      )

I don't understand why the function is called get_refresh_token if we get a new access token at the end.
It would be logical to update the refresh token together too.

@router.post(
    "/refresh-token",
    response_model=IPostResponseBase[TokenRead],
    status_code=201,
)
async def get_refresh_token(
    body: RefreshToken = Body(...),
    redis_client: Redis = Depends(get_redis_client),
) -> Any:
    """
    Gets a refresh token
    """

Unable to do validation on models.

Let's supposed I have the following model and create schema.

class ProjectBase(SQLModel):
    name: str = Field(nullable=False)
    repr_name: Optional[str] = Field(default=None, nullable=False)
    is_active: bool = Field(default=True, nullable=False)
    publish_date: date = Field(
        default_factory=datetime.now().date, nullable=False)
    approved: bool = Field(default=False, nullable=False)
    featured: bool = Field(default=False, nullable=False)
    
    # post_init
    @validator("repr_name", always=True)
    def slugify_name(cls, v: Optional[str], values: dict[str, Any]) -> str:
        if v is None:
            logger.info(f"{v=}, {values=}")
            repr_name = slugify(values.get('name'), separator="_")
            return repr_name
        raise ValueError(
            f"`repr_name` cannot be passed in as a parameter to the `Request`.")

As you can see, I am trying to post-process the name field by 'slugify' it.

The ICreateProject schema is as follows:

class IProjectCreate(ProjectBase):
    pass

As I try to create a project using the API, with the following request:

curl -X 'POST' \
  'http://localhost/api/v1/project' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NzY2NTc1MzgsInN1YiI6IjAxODYzZjUwLTkyNWMtNzU3Ny1hYTdmLTc4NmQ1Mjc4M2U2YSIsInR5cGUiOiJhY2Nlc3MifQ.bOtkIl5wJl1ahzGG_A5y7Dei-jxvrmtZalglclXaiR8' \
  -H 'Content-Type: application/json' \
  -d '{
  "is_active": true,
  "name": "Hello A",
  "publish_date": "2023-02-17",
  "approved": false,
  "featured": false
}'

The error log I got is this:

2023-02-18 02:08:37 INFO:     Application startup complete.
2023-02-18 02:08:43 2023-02-17 18:08:43,325 INFO     [project_model.py:29] v=None, values={'name': 'Hello A'}
2023-02-18 02:08:43 2023-02-17 18:08:43,325 INFO     [project_model.py:29] v=None, values={'name': 'Hello A'}
2023-02-18 02:08:43 2023-02-17 18:08:43,326 INFO     [base_crud.py:159] In CRUD.create
2023-02-18 02:08:43 2023-02-17 18:08:43,326 INFO     [base_crud.py:160] obj_in=IProjectCreate(name='Hello A', repr_name='hello_a', is_active=True, publish_date=datetime.date(2023, 2, 17), approved=False, featured=False)
2023-02-18 02:08:43 2023-02-17 18:08:43,326 INFO     [project_model.py:29] v=None, values={}
2023-02-18 02:08:43 INFO:     172.18.0.3:54102 - "POST /api/v1/project HTTP/1.1" 500 Internal Server Error
2023-02-18 02:08:43 ERROR:    Exception in ASGI application
2023-02-18 02:08:43 Traceback (most recent call last):
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/anyio/streams/memory.py", line 94, in receive
2023-02-18 02:08:43     return self.receive_nowait()
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/anyio/streams/memory.py", line 89, in receive_nowait
2023-02-18 02:08:43     raise WouldBlock
2023-02-18 02:08:43 anyio.WouldBlock
2023-02-18 02:08:43 
2023-02-18 02:08:43 During handling of the above exception, another exception occurred:
2023-02-18 02:08:43 
2023-02-18 02:08:43 Traceback (most recent call last):
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 77, in call_next
2023-02-18 02:08:43     message = await recv_stream.receive()
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/anyio/streams/memory.py", line 114, in receive
2023-02-18 02:08:43     raise EndOfStream
2023-02-18 02:08:43 anyio.EndOfStream
2023-02-18 02:08:43 
2023-02-18 02:08:43 During handling of the above exception, another exception occurred:
2023-02-18 02:08:43 
2023-02-18 02:08:43 Traceback (most recent call last):
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
2023-02-18 02:08:43     result = await app(  # type: ignore[func-returns-value]
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
2023-02-18 02:08:43     return await self.app(scope, receive, send)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/fastapi/applications.py", line 270, in __call__
2023-02-18 02:08:43     await super().__call__(scope, receive, send)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/applications.py", line 124, in __call__
2023-02-18 02:08:43     await self.middleware_stack(scope, receive, send)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__
2023-02-18 02:08:43     raise exc
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__
2023-02-18 02:08:43     await self.app(scope, receive, _send)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/cors.py", line 92, in __call__
2023-02-18 02:08:43     await self.simple_response(scope, receive, send, request_headers=headers)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/cors.py", line 147, in simple_response
2023-02-18 02:08:43     await self.app(scope, receive, send)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 106, in __call__
2023-02-18 02:08:43     response = await self.dispatch_func(request, call_next)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/fastapi_async_sqlalchemy/middleware.py", line 45, in dispatch
2023-02-18 02:08:43     return await call_next(request)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 80, in call_next
2023-02-18 02:08:43     raise app_exc
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 69, in coro
2023-02-18 02:08:43     await self.app(scope, receive_or_disconnect, send_no_error)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
2023-02-18 02:08:43     raise exc
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
2023-02-18 02:08:43     await self.app(scope, receive, sender)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
2023-02-18 02:08:43     raise e
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
2023-02-18 02:08:43     await self.app(scope, receive, send)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 706, in __call__
2023-02-18 02:08:43     await route.handle(scope, receive, send)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle
2023-02-18 02:08:43     await self.app(scope, receive, send)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 66, in app
2023-02-18 02:08:43     response = await func(request)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/fastapi/routing.py", line 237, in app
2023-02-18 02:08:43     raw_response = await run_endpoint_function(
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/fastapi/routing.py", line 163, in run_endpoint_function
2023-02-18 02:08:43     return await dependant.call(**values)
2023-02-18 02:08:43   File "/code/./app/api/v1/endpoints/project.py", line 83, in create_project
2023-02-18 02:08:43     created = await crud.project.create(obj_in=project, created_by_id=None)
2023-02-18 02:08:43   File "/code/./app/crud/base_crud.py", line 161, in create
2023-02-18 02:08:43     db_obj = self.model.from_orm(obj_in.dict())  # type: ignore
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/sqlmodel/main.py", line 551, in from_orm
2023-02-18 02:08:43     m = cls()
2023-02-18 02:08:43   File "<string>", line 4, in __init__
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/sqlalchemy/orm/state.py", line 481, in _initialize_instance
2023-02-18 02:08:43     with util.safe_reraise():
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/sqlalchemy/util/langhelpers.py", line 70, in __exit__
2023-02-18 02:08:43     compat.raise_(
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/sqlalchemy/util/compat.py", line 208, in raise_
2023-02-18 02:08:43     raise exception
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/sqlalchemy/orm/state.py", line 479, in _initialize_instance
2023-02-18 02:08:43     return manager.original_init(*mixed[1:], **kwargs)
2023-02-18 02:08:43   File "/usr/local/lib/python3.10/site-packages/sqlmodel/main.py", line 498, in __init__
2023-02-18 02:08:43     values, fields_set, validation_error = validate_model(
2023-02-18 02:08:43   File "pydantic/main.py", line 1077, in pydantic.main.validate_model
2023-02-18 02:08:43   File "pydantic/fields.py", line 877, in pydantic.fields.ModelField.validate
2023-02-18 02:08:43   File "pydantic/fields.py", line 1148, in pydantic.fields.ModelField._apply_validators
2023-02-18 02:08:43   File "pydantic/class_validators.py", line 287, in pydantic.class_validators._generic_validator_cls.lambda5
2023-02-18 02:08:43   File "/code/./app/models/project_model.py", line 30, in slugify_name
2023-02-18 02:08:43     repr_name = slugify(values['name'], separator="_")
2023-02-18 02:08:43 KeyError: 'name'

Somehow, the from_orm method does not have the field name.

Switch to Pytorch

Reasons: nearly all models are supported on pytorch in the Transformer Pipeline feature, much more limited on tensorflow. All that should need changed is the poetry package, all the pipelines should continue to work.

Question about Oso

Hello,

first of all, I'd like to thank you for this useful project example. I find the codebase very neat. One thing that I noticed is that you're using Oso library for authorization but I don't see it being initialised anywhere in the Python codebase. Can you please point me to where you initialise Oso, load the main.polar file, and how do you use it in the project?

Thanks! ๐Ÿ˜„

Convert the project

I suggest to convert this repository into Github template repository or you can support cookiecutter for better approach to this template.

How to enable delete on cascade for a many-to-one relationship?

Hi @jonra1993

I have two tables, Project and Category. One row in Project can be linked to many rows in Category, i.e. one project can belong to many categories.

Project.categories: List[Category] and Category.project: Project.

Right now, this is my Category table:

class Category(BaseUUIDModel, CategoryBase, table=True):
    # pass
    project: Optional["Project"] = Relationship(back_populates="categories", sa_relationship_kwargs={
            "lazy": "selectin",
            "primaryjoin": "Category.project_id==Project.id",
            'cascade': 'all, delete'})

And this is my Project table:

class Project(BaseIntModel, ProjectBase, table=True):  # PROJECT ID IS INT!!!
    categories: List[Category] = Relationship(
        back_populates='project',
        sa_relationship_kwargs={
            "lazy": "selectin",
            'cascade': 'all,delete',
            "primaryjoin": "Category.project_id==Project.id",
        })

What sa_relationship_kwargs I need to change in order to have this effect that whenever I delete a record in Project, all rows in Category such that Category.project_id == Project.id are deleted?

RDS configuration terraform

It is mentioned in your docs to use a persistent database. I think it would be cool to show how to use terraform to create this.
The way it's currently set up, horizontal scaling will cause data to not be centralized. So if another instance boots, there is a 50/50 chance the data gets in the spot the user is connected to. Then, its also possible they could have data in both spots. Therefore postgres (and likely redis) should be taken out of docker-compose.yml and moved to terraform.

Unable to `GET` `hero` by its `name`.

Hi, thanks for creating such a powerful and useful template!

I am attempting to retrieve a hero by its name, e.g. 'spiderman'.

I have created a record under the Hero table with name='spiderman'.

The code for the API to retrieve hero via name is:

@router.get("/{hero_name}")
async def get_hero_by_name(
    hero_name: str
) -> IGetResponseBase[IHeroReadWithTeam]:
    """
    Gets a hero by its `name`: str.
    """
    hero = await crud.hero.get_heroe_by_name(name=hero_name)
    if not hero:
        raise NameNotFoundException(Hero, name=hero_name)
    return create_response(data=hero)

Using the Swagger UI and upon execution, I get the following:
image

The codes for the function get_heroe_by_name is as follows:

class CRUDHero(CRUDBase[Hero, IHeroCreate, IHeroUpdate]):
    async def get_heroe_by_name(
        self, *, name: str, db_session: Optional[AsyncSession] = None
    ) -> Hero:
        db_session = db_session or db.session
        heroe = await db_session.execute(select(Hero).where(Hero.name == name))
        return heroe.scalar_one_or_none()

Basically, I have took your template and attempted to write an API to get a hero by its name but failed to do so.

Thank you.

Caddy not working with reverse proxy on remote host

I tested this on an EC2, even when providing the elastic IP to the EXT_ENDPOINT1, none of the subdomains could be reached. Although I was able to get the docs on http://{ELASTIC_IP]/docs

I also noticed that while fastapi.localhost works locally to get the hello world json response, fastapi.localhost/docs did not load the docs page. The only way to get the docs locally from my testing was localhost/docs on port 80.
Is there any way to get ssl behavior working using an elastic ip? Or do you have to own a dns to get an ssl cert working?

404 page not found

Hello, I tried to install the project with docker with excactly same configuration on the repository. Only edit was creating .env file with same data of exemple.env and changer mycustomwebsite.com to my domain that point to my server IP.
The issue I fascing (propably because I am an idiot): 404 page not found, when accesing the / (hello word) endpoint.
Any help/guide on how to propebly use it. Thank you verry much

Query with sorting

Why don't you use the function get_multi_paginated_ordered when executing a query with sorting

@router.get(
    "/order_by_created_at",
    response_model=IGetResponsePaginated[IUserReadWithoutGroups],
)
async def get_hero_list_order_by_created_at(
    params: Params = Depends(),
    current_user: User = Depends(
        deps.get_current_user(required_roles=[IRoleEnum.admin, IRoleEnum.manager])
    ),
):
    """
    Gets a paginated list of users ordered by created datetime
    """
    query = select(User).order_by(User.created_at)
    users = await crud.user.get_multi_paginated(query=query, params=params)
    return create_response(data=users)

Arbitrarily ordering on paginated GET requests

User story: Alli the API consumer wants to be able to sort heroes by certain fields and choose if they are ascending or descending order.

    async def get_multi_paginated_ordered(
        self,
        *,
        params: Optional[Params] = Params(),
        ordered_by: Optional[str] = None,
        order: Optional[IOrderEnum] = IOrderEnum.ascendent,
        query: Optional[Union[T, Select[T]]] = None,
        db_session: Optional[AsyncSession] = None
    ) -> Page[ModelType]:
        db_session = db_session or db.session
        
        if ordered_by not in self.model.__table__.columns:
            ordered_by = self.model.id
        
        if query == None:
            if order == order.ascendent:
                query = select(self.model).order_by(self.model[ordered_by].asc()) # <---- SQLModelMetaclass is not subscriptable
            else:
                query = select(self.model).order_by(self.model.desc()) <---- type object {T_type_goes_here} has no attribute 'desc'

        return await paginate(db_session, query, params)

Here is what I have so far.
Trying to figure out how I can pass in a specific field to be ordered into the model to order by that is validated as an existing field in the pydantic model.
Any ideas?

field required (type=value_error.missing)

After executing docker compose up --build this error keeps running forever

How can fix it please?

nginx           | 2022/07/25 09:21:32 [notice] 1#1: start worker process 32
traefik-proxy   | time="2022-07-25T09:21:33Z" level=info msg="Configuration loaded from file: /traefik.yml"
fastapi_server  | Traceback (most recent call last):
fastapi_server  |   File "/usr/local/bin/alembic", line 8, in <module>
fastapi_server  |     sys.exit(main())
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/alembic/config.py", line 588, in main
fastapi_server  |     CommandLine(prog=prog).main(argv=argv)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/alembic/config.py", line 582, in main
fastapi_server  |     self.run_cmd(cfg, options)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/alembic/config.py", line 559, in run_cmd
fastapi_server  |     fn(
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/alembic/command.py", line 320, in upgrade
fastapi_server  |     script.run_env()
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/alembic/script/base.py", line 563, in run_env
fastapi_server  |     util.load_python_file(self.dir, "env.py")
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/alembic/util/pyfiles.py", line 92, in load_python_file
fastapi_server  |     module = load_module_py(module_id, path)
fastapi_server  |   File "/usr/local/lib/python3.9/site-packages/alembic/util/pyfiles.py", line 108, in load_module_py
fastapi_server  |     spec.loader.exec_module(module)  # type: ignore
fastapi_server  |   File "<frozen importlib._bootstrap_external>", line 850, in exec_module
fastapi_server  |   File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
fastapi_server  |   File "alembic/env.py", line 7, in <module>
fastapi_server  |     from app.core.config import Settings
fastapi_server  |   File "/code/app/core/config.py", line 51, in <module>
fastapi_server  |     settings = Settings()
fastapi_server  |   File "pydantic/env_settings.py", line 38, in pydantic.env_settings.BaseSettings.__init__
fastapi_server  |   File "pydantic/main.py", line 331, in pydantic.main.BaseModel.__init__
fastapi_server  | pydantic.error_wrappers.ValidationError: 1 validation error for Settings
fastapi_server  | REDIS_PORT
fastapi_server  |   field required (type=value_error.missing)
fastapi_server exited with code 1

Error command: docker-compose -f docker-compose-dev.yml up --build

This installer will now exit. If you understand the above warning and wish to proceed anyway, set GET_POETRY_IGNORE_DEPRECATION=1 in the environment and run this installer again.
/bin/sh: 1: poetry: not found
1 error occurred:
* Status: The command '/bin/sh -c curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python && cd /usr/local/bin && ln -s /opt/poetry/bin/poetry && poetry config virtualenvs.create false' returned a non-zero code: 127, Code: 127

Include locals and all defaults for vars in terraform

This snippet fails due to no locals, its hard to infer what these are. A local in variables.tf seems appropriate @jonra1993 .

  env_vars = {
    for obj in concat(local.fastapi, local.database, local.traefik, local.redis, local.minio, local.external_apis) : "${obj.name}" => " ${obj.value}"
  }

This one is not as big a deal but variables need to set by the user by putting defaults or setting it up on terraform cloud. It'd be really cool if this was all taken from .env

Svelte instead of nextjs

I think Svelte would be a more modern and fun choice here.
It uses the actual DOM instead of the virutal DOM.
It includes Redux-like functionality out-of-the-box.
Shorter syntax. And if you need things outside of SPA (like SSR), there is SvelteKit (which to be honest I don't think is needed.)

Add a table self association example.

RT,
In the near future, I will add the following data tables:

class HeroComment:
  id:
  hero_id:
  user_id:
  content:
  parent_id: -> point to other HeroComment

But before that, I want to discuss the problem of API path. Which of the following two forms is more appropriate?

  • /comment/hero
  • /hero/{hero_id}/comment

After modifying the password, need to disable the token of other clients.

def password_changed():
  ......
  access_token_key = "user:{}:access_token".format(user_id)
  redis.sadd(access_token_key, new_access_token)
  redis.expore(access_token_key, ACCESS_TOKEN_EXPIRE_MINUTES)

  refresh_token_key = "user:{}:refresh_token".format(user_id)
  redis.sadd(refresh_token_key, new_refresh_token)
  redis.expore(refresh_token_key, REFRESH_TOKEN_EXPIRE_MINUTES)
  ......
def get_current_user(required_roles: List[str] = None) -> User:
    async def current_user(token: str = Depends(reusable_oauth2)) -> User:        
        try:
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[security.ALGORITHM])
        except (jwt.JWTError, ValidationError):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Could not validate credentials",
            )
        id = payload["sub"]
        access_token_key = "user:{}:access_token".format(id)
        valid_access_tokens = redis.smembers(access_token_key, token)
        if valid_access_tokens and token not in valid_access_tokens:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Could not validate credentials",
            )

        user: User = await crud.user.get(id=payload["sub"])
        if not user:
            raise HTTPException(status_code=404, detail="User not found")
......

SSL config for production

I see the comment in nginx for turning on SSL connections.
However, there could be code written for integrating OpenSSL.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.