0

I have a FastAPI application that I'm using AWS secrets manager to store things like connection strings. In main.py I have fastapi configured to load the secrets into environment variables on startup of the app. I have a file db.py where it uses these secrets to create a database engine. However it seems that the engine creation is happening in db.py is happening either at the same time or before load_secrets in @app.on_event("startup") because I get an error essentially saying that the environment variables needed to make the db connection string are None. How can I get it so it doesn't try to run the code in db.py to create the engine until after everything in @app.on_event("startup") finishes? I know I could wrap the code in db.py in a function and just create a new but it seems a little silly to create a new engine each time vs just creating the engine once and then spawning sessions off that 1 engine as needed. Thanks.

main.py below

# Package Imports
from fastapi import FastAPI, HTTPException
from mangum import Mangum
import uvicorn
import os
from dotenv import load_dotenv
import boto3
import json

# File Imports
from api.api_v1.api import router as api_router_v1

# Loads secrets from AWS Secrets Manager Into Environment Variables
def load_secrets():

    try:
        # Load environment variables from .env file
        load_dotenv()

        session = boto3.session.Session()
        client = session.client(service_name='secretsmanager', region_name= os.getenv('AWS_REGION_NAME'), aws_access_key_id= os.getenv('AWS_ACCESS_KEY_ID'), aws_secret_access_key= os.getenv('AWS_SECRET_ACCESS_KEY'))
        get_secret_value_response = client.get_secret_value(SecretId=os.getenv('AWS_SECRET_NAME'))

        if 'SecretString' in get_secret_value_response:
            secret = get_secret_value_response['SecretString']
            json_secret = json.loads(secret)

            for key, value in json_secret.items():
                os.environ[key] = value
        else:
            raise HTTPException(status_code=503, detail="One or more secrets are missing. Unable to start the application.")
        
    except Exception as ex:

        raise HTTPException(status_code=503, detail=str(ex))

app = FastAPI(
    docs_url="/",
    title="API",
    description="",
    version="0.0.1"
)

# Define startup code.
@app.on_event("startup")
def startup_event():

    load_secrets()
    
    
# Define the routes
app.include_router(api_router_v1, prefix="/api/v1")

# This is the handler for AWS
handler = Mangum(app)

# This runs the app for testing
if __name__ == '__main__':

    uvicorn.run('main:app', host="localhost", port=8000, reload=True, timeout_keep_alive=3000)

db.py below

import os
from sqlmodel import create_engine, Session
from sqlalchemy.engine import URL

connection_string = 'DRIVER={};SERVER={};DATABASE={};UID={};PWD={}'.format(os.getenv('DRIVER'), os.getenv('SERVER'), os.getenv('DB'), os.getenv('USERNAME'), os.getenv('PASSWORD'))
connection_url = URL.create("mssql+pyodbc", query={"odbc_connect": connection_string})
engine = create_engine(connection_url)
configure.ng
  • 159
  • 1
  • 11
  • 1
    Your code in the db file runs as soon as you import it - wrap it in a function and run the function as part of your startup after you've loaded the settings. – MatsLindh Jun 14 '23 at 18:27
  • 1
    As @MatsLindh has mentioned, `db.py` will run as soon as you import it. Another way to fix would be to call `load_dotenv` right after the import (I typically do it like this) at the top of `main.py`. This will load the environment variables before db gets imported or anything that depends on it. – Mark Jun 15 '23 at 08:27
  • 1
    You might find [this answer](https://stackoverflow.com/a/76322910/17865804) helpful as well – Chris Jun 15 '23 at 10:03

0 Answers0