Package: fastapi-jsonrpc
DescriptionJSON-RPC server based on fastapi:https://fastapi.tiangolo.comOpenRPC supported too.MotivationAutogeneratedOpenAPIandSwagger(thanks to fastapi) for JSON-RPC!!!Installationpipinstallfastapi-jsonrpcDocumentationRead FastAPI documentation and see usage examples bellowSimple usage examplepipinstalluvicornexample1.pyimportfastapi_jsonrpcasjsonrpcfrompydanticimportBaseModelfromfastapiimportBodyapp=jsonrpc.API()api_v1=jsonrpc.Entrypoint('/api/v1/jsonrpc')classMyError(jsonrpc.BaseError):CODE=5000MESSAGE='My error'classDataModel(BaseModel):details:str@api_v1.method(errors=[MyError])defecho(data:str=Body(...,examples=['123']),)->str:ifdata=='error':raiseMyError(data={'details':'error'})else:returndataapp.bind_entrypoint(api_v1)if__name__=='__main__':importuvicornuvicorn.run('example1:app',port=5000,debug=True,access_log=False)OpenRPC:http://127.0.0.1:5000/openrpc.jsonSwagger:http://127.0.0.1:5000/docsFastAPI dependencies usage examplepipinstalluvicornexample2.pyimportloggingfromcontextlibimportasynccontextmanagerfrompydanticimportBaseModel,Fieldimportfastapi_jsonrpcasjsonrpcfromfastapiimportBody,Header,Dependslogger=logging.getLogger(__name__)# database modelsclassUser:def__init__(self,name):self.name=namedef__eq__(self,other):ifnotisinstance(other,User):returnFalsereturnself.name==other.nameclassAccount:def__init__(self,account_id,owner,amount,currency):self.account_id=account_idself.owner=ownerself.amount=amountself.currency=currencydefowned_by(self,user:User):returnself.owner==user# fake databaseusers={'1':User('user1'),'2':User('user2'),}accounts={'1.1':Account('1.1',users['1'],100,'USD'),'1.2':Account('1.2',users['1'],200,'EUR'),'2.1':Account('2.1',users['2'],300,'USD'),}defget_user_by_token(auth_token)->User:returnusers[auth_token]defget_account_by_id(account_id)->Account:returnaccounts[account_id]# schemasclassBalance(BaseModel):"""Account balance"""amount:int=Field(...,example=100)currency:str=Field(...,example='USD')# errorsclassAuthError(jsonrpc.BaseError):CODE=7000MESSAGE='Auth error'classAccountNotFound(jsonrpc.BaseError):CODE=6000MESSAGE='Account not found'classNotEnoughMoney(jsonrpc.BaseError):CODE=6001MESSAGE='Not enough money'classDataModel(BaseModel):balance:Balance# dependenciesdefget_auth_user(# this will become the header-parameter of json-rpc method that uses this dependencyauth_token:str=Header(None,alias='user-auth-token',),)->User:ifnotauth_token:raiseAuthErrortry:returnget_user_by_token(auth_token)exceptKeyError:raiseAuthErrordefget_account(# this will become the parameter of the json-rpc method that uses this dependencyaccount_id:str=Body(...,example='1.1'),user:User=Depends(get_auth_user),)->Account:try:account=get_account_by_id(account_id)exceptKeyError:raiseAccountNotFoundifnotaccount.owned_by(user):raiseAccountNotFoundreturnaccount# JSON-RPC middlewares@asynccontextmanagerasyncdeflogging_middleware(ctx:jsonrpc.JsonRpcContext):logger.info('Request:%r',ctx.raw_request)try:yieldfinally:logger.info('Response:%r',ctx.raw_response)# JSON-RPC entrypointcommon_errors=[AccountNotFound,AuthError]common_errors.extend(jsonrpc.Entrypoint.default_errors)api_v1=jsonrpc.Entrypoint(# Swagger shows for entrypoint common parameters gathered by dependencies and common_dependencies:#    - json-rpc-parameter 'account_id'#    - header parameter 'user-auth-token''/api/v1/jsonrpc',errors=common_errors,middlewares=[logging_middleware],# this dependencies called once for whole json-rpc batch requestdependencies=[Depends(get_auth_user)],# this dependencies called separately for every json-rpc request in batch requestcommon_dependencies=[Depends(get_account)],)# JSON-RPC methods of this entrypoint# this json-rpc method has one json-rpc-parameter 'account_id' and one header parameter 'user-auth-token'@api_v1.method()defget_balance(account:Account=Depends(get_account),)->Balance:returnBalance(amount=account.amount,currency=account.currency,)# this json-rpc method has two json-rpc-parameters 'account_id', 'amount' and one header parameter 'user-auth-token'@api_v1.method(errors=[NotEnoughMoney])defwithdraw(account:Account=Depends(get_account),amount:int=Body(...,gt=0,example=10),)->Balance:ifaccount.amount-amount<0:raiseNotEnoughMoney(data={'balance':get_balance(account)})account.amount-=amountreturnget_balance(account)# JSON-RPC APIapp=jsonrpc.API()app.bind_entrypoint(api_v1)if__name__=='__main__':importuvicornuvicorn.run('example2:app',port=5000,debug=True,access_log=False)OpenRPC:http://127.0.0.1:5000/openrpc.jsonSwagger:http://127.0.0.1:5000/docsDevelopmentInstall poetryhttps://github.com/sdispater/poetry#installationInstall dependenciespoetryupdateRegenerate README.rstrst_includeinclude-qREADME.src.rstREADME.rstChange dependenciesEditpyproject.tomlpoetryupdateBump versionpoetryversionpatchpoetryversionminorpoetryversionmajorPublish to pypipoetrypublish--build