API Documentation for Anvil App

I went down a rabbit hole the past two days building documentation for my very simple API for my app. Originally I wrote a text file but the developers consuming the API didn’t seem satisfied with this.

I moved over to Swagger/OpenAPI and wrote a yaml file to declare the API spec. After a few hours tweaking, I got it working. (Pro tip, in Python a requests PUT data and json show up in different areas on an Anvil HTTP endpoint.)

I will be hosting a simple HTML file in the app exported from SwaggerHub. But it would be super duper awesome if there was some tool built in to export your HTTP endpoints to a simple doc.

3 Likes

Hello @robert and anvil central
I vote for this too.
API Doc could be automatically generated from structured comments put in code, like javadoc.
Then an HTML asset could be auto-generated, or an ad-hoc component could serve this HTML.
This is more or less what I did with this toolchain:
1.APIDOC to generate a 1-single-html-file api documentation from comments in code
2.uploaded as html asset in my app
3.served that html file in a form as an iframe

It looks quite pretty! (even though automatically generated example api test forms don’t work… need to investigate why) Check it out here (API DOC page).

I think 1) 2) could be automated by the IDE, and 3) could be the ad-hoc component.

That was generated by these comments in code:

"""
@api {get|post} /get_regioni/:api_key Ricerca la tabella delle Regioni
@apiName Get Regioni
@apiGroup Regioni
@apiExample {curl} Esempio d'uso
  curl -i https://comuni-italiani.anvil.app/_/api/get_regioni/api_key_1234567890?formatted=true

@apiParam {Number} api_key La API KEY assegnata.

@apiSuccess {Boolean=true} success true
@apiSuccess {Object} result dict contenente i records
@apiError {Boolean=false} success false
@apiError {String} error dict contente l'eccezione generata
"""
@anvil.server.http_endpoint("/get_regioni/:api_key", enable_cors=True)
def get_regioni(api_key, **params):
...my code...

  
"""
@api {get|post} /get_ripartizioni_sovracomunali/:api_key Ricerca la tabella delle Ripartizioni Sovracomunali
@apiName Get Ripartizioni Sovracomunali
@apiGroup Province
@apiExample {curl} Esempio d'uso
  curl -i https://comuni-italiani.anvil.app/_/api/get_ripartizioni_sovracomunali/api_key_1234567890?reg_denom=Piemonte

@apiParam {Number} api_key La API KEY assegnata.
@apiParam {Boolean} formatted=false Restituisce un JSON indentato
@apiParam {String} campo=valore Ogni campo restituito può essere utilizzato come campo query di ricerca

@apiSuccess {Boolean=true} success true
@apiSuccess {Object} attuali dict contenente i records
@apiError {Boolean=false} success false
@apiError {Object} error dict contente l'eccezione generata
"""
@anvil.server.http_endpoint("/get_ripartizioni_sovracomunali/:api_key", enable_cors=True)
def get_ripartizioni_sovracomunali(api_key, **params):
...my code...
  
"""
@api {get|post} /get_province/:api_key Ricerca la tabella delle Province
@apiName Get Province
@apiGroup Province
@apiExample {curl} Esempio d'uso
  curl -i https://comuni-italiani.anvil.app/_/api/get_province/api_key_1234567890?reg_denom=Piemonte&formatted=true

@apiParam {Number} api_key La API KEY assegnata.
@apiParam {Boolean} formatted=false Restituisce un JSON indentato
@apiParam {Boolean} soppresse=false Restituisce anche le province soppresse
@apiParam {String} campo=valore Ogni campo restituito può essere utilizzato come campo query di ricerca

@apiSuccess {Boolean=true} success true
@apiSuccess {Object} attuali dict contenente i records delle province attuali
@apiSuccess {Object} soppr dict contenente i records delle province soppresse (se soppresse=true)
@apiError {Boolean=false} success false
@apiError {Object} error dict contente l'eccezione generata
"""  
@anvil.server.http_endpoint("/get_province/:api_key", enable_cors=True)
def get_province(api_key, **params):
...my code...

"""
@api {get|post} /get_comuni/:api_key?querystring_params Ricerca la tabella dei Comuni
@apiName Get Comuni
@apiGroup Comuni
@apiExample {curl} Esempio d'uso
  curl -i https://comuni-italiani.anvil.app/_/api/get_comuni/api_key_1234567890?sigla_auto=TO&formatted=true&soppressi=true

@apiParam {Number} api_key La API KEY assegnata.
@apiParam (querystring_params) {Boolean} formatted=false Restituisce un JSON indentato
@apiParam (querystring_params) {Boolean} soppressi=false Restituisce anche i comuni soppressi
@apiParam (querystring_params) {String} campo=valore Ogni campo restituito può essere utilizzato come campo query di ricerca

@apiSuccess {Booolean=true} success true
@apiSuccess {Object} attuali dict contenente i records dei comuni attuali
@apiSuccess {Object} soppr dict contenente i records dei comuni soppressi (se soppressi=true)
@apiError {Boolean=false} success false
@apiError {Object} error dict contente l'eccezione generata
"""   
@anvil.server.http_endpoint("/get_comuni/:api_key", enable_cors=True)
def get_comuni(api_key, **params):
...my code...

3 posts were split to a new topic: Can you help me with the APIs?

I need to write an externally facing API (preferably using anvil HTTP endpoints)
Most of the stuff I do is internal to the company where I work.

I also want to easily write documentation (preferably even using OpenAPI / SwaggerHub html) , so think this this FR would be useful.

I recently found out that FastAPI (which uses pydantic, which server modules already have installed) has a feature that automatically creates a SwaggerUI based docs page, so I’m sure its possible.
I am unsure of how easy to implement it would be, but it would make Anvil incredibly powerful for making an API with a front facing web presence, and secure internal backends to get data to serve via API (using uplink).