Package: fastapi-mctools
FastAPI-MCtoolsMC(나)의 FastAPI로 개발할 때 자주 사용하는 코드들을 모아놓은 패키지입니다.This is a package that contains frequently used codes when developing with FastAPI by MC(me).This is kind of an utility package블로그소개글 참고Installationpipinstallfastapi-mctoolsUsageCLI1. runrun uvicorn server for developmentthis command will findmain.pyand run uvicorn serveruse this command when running in local environmentmctrundev2. startprojectcreate a new FastAPI project in ordinary waymctstartproject3. gunicornmake gunicorn basic configmctgunicorn4. typescheck all functions if existing missing type hintthis command will find all functions in the file or directory and check if there is any missing type hintuse this if you are very strict about type hintingmcttypes<path:directoryorfile>DB SessionDB, AsyncDBwhen you want to make db session in the fast way, use thisfromfastapi_mctools.dbimportDB,AsyncDBget_db=DB(db_url)get_db=AsyncDB(db_url)Request Logging MiddlewareRequestLoggingMiddlewarewhen you simply want to log all requests, use thisyou can use log configuration by dictConfig or fileConfig# main.pyimportloggingfromfastapiimportFastAPIfromfastapi_mctools.middlewaresimportRequestLoggingMiddlewareapp=FastAPI()logger=logging.getLogger('request')app.add_middleware(RequestLoggingMiddleware,logger=logger)SQLAlchemy ORMsync_base, async_basethese are kind of repositories for frequently used ORM operations, such as CRUDif not putting any column name, it will use all columns in the modelfromfastapi_mctools.ormsimportsync_base,async_baseclassUserCreate(async_base.ACreateBase):...classUserRead(async_base.AReadBase):...classUserUpdate(async_base.AUpdateBase):...classUserDelete(async_base.ADeleteBase):...classUserRepository(UserCreate,UserRead,UserUpdate,UserDelete):def__init__(self,model:SqlalchemyModel)->None:super().__init__(model=model)user_repository=UserRepository(model=User)# AsyncSession from sqlalchemy.ext.asyncioasyncdefcreate_user(db:AsyncSession,data:dict)->User:returnawaituser_repository.create(db,**data)asyncdefread_user(db:AsyncSession,user_id:int)->User:user=awaituser_repository.get(db,user_id)users=awaituser_repository.get_all_by_filters(db,age=20)users_ages=awaituser_repository.get_all_by_filters(db,age=20,columns=['age'])users_in=awaituser_repository.get_all_in(db,column='age',values=[20,21])# if you want to use not in, make `_not=True`users_like=awaituser_repository.get_all_like(db,column='name',value='mc')...# update and delete are similar to create and readTestToolsdb_managerswhen you want to test with db and set up and tear down db in very simple way# conftest.pyfromfastapi_mctools.test_tools.db_managersimportTestConfDBManagerdb_test_manager=TestConfDBManager(TEST_DB_URL)@pytest.fixtureasyncdefdb():# Base is your sqlalchemy baseasyncforsessionindb_test_manager.get_async_db_session(base=Base,is_meta=True):yieldsession# test_something.pyasyncdeftest_something(db):# db is your test db session...dependenciesDependencythis is a class that can be used as a dependency manager that can handle multiple dependencies in an single instanceso you can only import one instance even if there are many dependenciesasyncdeftemp_dep_1():return1asyncdeftemp_dep_2():return2asyncdeftemp_dep_3():return3temp_dep=Dependency(TempDep1=temp_dep_1,TempDep2=temp_dep_2,TempDep3=temp_dep_3)# in your route@app.get('/temp_1')asyncdeftemp_1(result:temp_dep.TempDep1):returnresult@app.get('/temp_2')asyncdeftemp_2(result:temp_dep.TempDep2):returnresult@app.get('/temp_3')asyncdeftemp_3(result:temp_dep.TempDep3):returnresultcreate_simple_form_dependencythis is developed to avoid the tedious task of repeatedly writing out the Form.especially when you make api with file upload, you can use this to make it simplefromfastapi_mctools.dependenciesimportcreate_simple_form_dependencysimple_form_params={"name":str,"age":int,"address":str,"email":str,"status":str,"memo":str,}form_data=create_simple_form_dependency(simple_form_params)@app.post("/user")defcreate_user(form_data:FastAPIForm=Depends(),file:UploadFile=File()):returnform_dataRequestsAPIClientthis is a class that can be used to make requests to external APIs only asynchronouslyyou can keep the session alive and reuse itI normally use this in the lifespan of the appfromfastapi_mctools.utils.requestsimportAPIClient@asynccontextmanagerasyncdeflifespan(app:FastAPI):try:api_client=APIClient()awaitapi_client.start()states["api_client"]=api_clientlogger.info(f"Start App: states{states}, Debug:{app_settings.DEBUG}")yieldawaitapi_client.close()exceptExceptionase:raiseeResponsesResponseInterFacethis is an interface that can unify the response format ex) {"results" : data, "message": message, "status": status}fromfastapi_mctools.utils.responsesimportResponseInterFaceasyncdefget_user(user_id:int)->ResponseInterFace:user=awaituser_repository.get(user_id)ifuser:returnResponseInterFace(results=user,message="Success",temp_response_1="temp1",temp_response_2="temp2",status=200).to_dict()returnResponseInterFace(results=None,message="Not Found",temp_response_1="temp1",temp_response_2="temp2",status=404).to_dict()timetime_checkerthis is a decorator that can be used to check orm query timefromfastapi_mctools.utils.timeimporttime_checker@time_checker(debug=True,logger=logger)classSomeRepository:defget(self,db:AsyncSession,id:int)->SomeModel:...temp=SomeRepository()temp.get(db,1)# SomeRepository.get took 0.01 msexceptionsHTTPException, handle_http_exception, handle_500_exceptionHTTPException is a class that can be used instead of FastAPI's HTTPException, you can add more information to the exception responsehandle_http_exception is exception handler that can be used to handle HTTPException, if you want to add more information with HTTPException, add this handler to apphandle_500_exception is exception handler that can be used to handle 500 error, it converts Exception to HTTPException and returns itfromfastapi_mctools.exceptionsimportHTTPException,handle_http_exception,handle_500_exceptionasyncdeftemp_api():raiseHTTPException(status_code=400,detail="Bad Request",more_info="more info",code="TEMP_ERROR")app.add_exception_handler(HTTPException,handle_http_exception)