Package: sanic-openapi3e
Sanic OpenAPI v3eOpenAPI v3 support for Sanic. Document and describe all parameters,
including sanic path params. python 3.6+; sanic 18.12.0+ but sanic 20.12.0 and later are recommended.Installationpipinstallsanic-openapi3eUsageImport blueprint and use simple decorators to document routes:importsanicimportsanic.responsefromsanic_openapi3eimportopenapi_blueprint,swagger_blueprint,docapp=sanic.Sanic(strict_slashes=True)app.blueprint(openapi_blueprint)app.blueprint(swagger_blueprint)@app.get("/user/<user_id>")@doc.summary("Fetches a user by ID")@doc.response(200,"The user")asyncdefget_user(request,user_id):returnsanic.response.json({"user_id":user_id})app.go_fast()You'll now have a specification at the URL/openapi/spec.jsonand
a YAML version at/openapi/spec.yml.p.s.: Clicking on the/openapi/spec.ymllink in a browser will generally download a file and your computer will open
an application to read it. To see the YAML spec in the browser as a plain text file, add a?as_textquery string like/openapi/spec.yml?as_textinstead.Your routes will be automatically categorized by their blueprints'
names.Below are some simple examples, which you can copy/paste, run, and point your browser tohttp://127.0.0.1:8000/swagger/to see them in action.Describe route path parametersIf you have path parameters, you will want to describe them:importsanicimportsanic.responsefromsanic_openapi3eimportopenapi_blueprint,swagger_blueprint,docapp=sanic.Sanic(strict_slashes=True)app.blueprint(openapi_blueprint)app.blueprint(swagger_blueprint)@app.get("/examples/test_id/<an_id:int>")@doc.parameter(name="an_id",description="An ID",required=True,_in="path")deftest_id(request,an_id):d=locals()deld["request"]# not JSON serializablereturnsanic.response.json(d)app.go_fast()sanic-openapiv3ewill recognise that the path parameteran_idis
described with@doc.parameterand will merge the details together.You may wish to specify that a parameter be limited to a set of choices,
such as day-of-week or that it has a minimum value. These can be done
for parameters inpath,query,headerandcookie:importsanicimportsanic.requestimportsanic.responsefromsanic_openapi3eimportopenapi_blueprint,swagger_blueprint,docapp=sanic.Sanic(strict_slashes=True)app.blueprint(openapi_blueprint)app.blueprint(swagger_blueprint)int_min_4=doc.Schema(_type="integer",_format="int32",minimum=4,description="Minimum: 4")@app.get("/test/some_ids")@doc.parameter(name="ids",description="Some IDs",required=True,choices=[1,3,5,7,11,13],_in="query",schema=doc.Schema.Integers,)deftest_some_ids(request:sanic.request.Request):query=request.query_stringreturnsanic.response.json(query)@app.get("/examples/test_id_min/<an_id:int>")@doc.parameter(name="an_id",description="An ID",required=True,_in="path",schema=int_min_4)deftest_id_min(request,an_id:int):d=locals()deld["request"]# not JSON serializablereturnsanic.response.json(d)app.go_fast()Describe your tagsOpenAPI uses "tags" (there can be more than one per route) to group the
endpoints. It's nice to be able to group your endpoints into tags given
by the blueprint's name, but sometimes you will want to give them better
names:@doc.tag("tag name"). Better still is to give a description
to these tags (which shows up nicely in Swagger UI), so@doc.tag("tag name", description="tag description").You don't have to add the description more than once,sanic-openapiv3ewill make it available, so while you'll want to
decorate each endpoint with@doc.tag(...), only one of these will
need the description. If you try to set different descriptions for the
same tag,sanic-openapiv3ewill raise an exception showing the tag
name and the conflicting descriptions.Share and reuse common parameters in your appYou probably have some common parameters that appear in many places in
your API. Days of the week? Pagination where the minimum value must be
greater than zero? OpenAPI v3 has the concept of "components" which can
be shared. Setting them up is easy:importsanic.requestimportsanic.responsefromsanicimportSanicfromsanic_openapi3eimportopenapi_blueprint,swagger_blueprint,docdays_of_week=doc.Schema(_type="string",description="Days of the week, short, English",enum=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],)app=Sanic(strict_slashes=True)app.blueprint(openapi_blueprint)app.blueprint(swagger_blueprint)schemas={"int.min4":doc.Schema(title="int.min4",_type="integer",_format="int32",minimum=4,description="Minimum: 4",),"days":days_of_week,}components=doc.Components(schemas=schemas)app.config.OPENAPI_COMPONENTS=components# ^^ the line above adds these to OAS v3's "components"# the next two, which would ordinarily live in your blueprints's module,# reuse these shared components.int_min_4_ref=doc.Reference("#/components/schemas/int.min4")dow_ref=doc.Reference("#/components/schemas/days")@app.get("/simple/01/from/<start>/to/<end>/in/<hops:int>")@doc.parameter(name="start",description="Start day",required=True,_in="path",schema=dow_ref)@doc.parameter(name="end",description="End day",required=True,_in="path",schema=dow_ref)@doc.parameter(name="hops",description="hops to use",required=True,_in="path",schema=int_min_4_ref,)defget_start_end_hops(request,start:str,end:str,hops:int):d=locals()deld["request"]# not JSON serializablereturnsanic.response.json(d)app.go_fast()Deprecate route paths and/or parametersA parameter can be marked asdeprecated=True:importsanicimportsanic.requestimportsanic.responsefromsanic_openapi3eimportopenapi_blueprint,swagger_blueprint,docapp=sanic.Sanic(strict_slashes=True)app.blueprint(openapi_blueprint)app.blueprint(swagger_blueprint)@app.get("/examples/test_parameter__deprecated/<an_id:int>")@doc.parameter(name="an_id",description="An ID",required=True,_in="path",deprecated=True)@doc.summary("A path deprecated parameter")@doc.description("The parameter should be marked as deprecated")defparam__deprecated(request,an_id:int):d=locals()deld["request"]# not JSON serializablereturnsanic.response.json(d)app.go_fast()as can a whole route with@doc.deprecated():importsanicimportsanic.requestimportsanic.responsefromsanic_openapi3eimportopenapi_blueprint,swagger_blueprint,docapp=sanic.Sanic(strict_slashes=True)app.blueprint(openapi_blueprint)app.blueprint(swagger_blueprint)@app.get("/examples/test_path__deprecated/<an_id:int>")@doc.parameter(name="an_id",description="An ID",required=True,_in="path",)@doc.summary("A path with parameter examples")@doc.description("This is marked as being deprecated")@doc.deprecated()defpath__deprecated(request,an_id:int):d=locals()deld["request"]# not JSON serializablereturnsanic.response.json(d)app.go_fast()Please note, that while many python decorators can be called without the(), this one really
does require the()at the end of@doc.deprecated().Exclude routes from appearing in the OpenAPI spec (and swagger)Need to soft-launch an endpoint, or keep your swagger simple? Add a@doc.excludeand it won't be in the OpenAPI spec at all (unless you
have set yourapp.config.SHOW_OPENAPI_EXCLUDED = Truewhen asecondspec at/openapi/spec.all.jsonwill be created which will
have all routes, including excluded.importsanicimportsanic.requestimportsanic.responsefromsanic_openapi3eimportopenapi_blueprint,swagger_blueprint,docapp=sanic.Sanic(strict_slashes=True)app.blueprint(openapi_blueprint)app.blueprint(swagger_blueprint)@app.get("/test/alpha_release")@doc.exclude@doc.parameter(name="ids",description="Some IDs",required=True,choices=[1,3,5,7,11,13],_in="query",schema=doc.Schema.Integers,)deftest_some_ids(request:sanic.request.Request):query=request.query_stringreturnsanic.response.json(query)app.go_fast()Predefined components for responsesThere are predefimed components for common responses. You can overwrite and append these per route.Control spec generationapp.config.API_VERSION='1.0.0'app.config.API_TITLE='An API'app.config.API_DESCRIPTION='An API description'To have acontact, set at least one of (but preferably all)app.config.API_CONTACT_NAME,app.config.API_CONTACT_URLorapp.config.API_CONTACT_EMAIL.To have alicense,set app.config.API_LICENSE_NAMEand
optionallyapp.config.API_LICENSE_URL(all str, but the Swagger UI expects this to be a URL).To have atermsOfService, setapp.config.API_TERMS_OF_SERVICE_URL(a str, but the Swagger UI
expects to use this as a URL).Settingcomponents,securityandexternalDocsrequires you tofirst create the relevant objects somewhere in your code (near to
where you create theapp),set the appropriateapp.config.OPENAPI_COMPONENTS,app.config.OPENAPI_SECURITY,app.config.OPENAPI_EXTERNAL_DOCS.By default, the YAML spec is served on/openapi/spec.ymlwith the content-type ofapplication/x-yamlbut you can
overwrite that by settingapp.config.OPENAPI_YAML_CONTENTTYPEto something liketext/plain. This lets you view
your spec in a browser, and it still works with/swagger:-)Configure how the operationId is madeThe default code for creating the operationId creates unique (as they must be) strings from
the following code:defdefault_operation_id_fn(method:str,uri:str,route:sanic.router.Route)->str:uri_for_operation_id:str=uriforparameterinroute.parameters:uri_for_operation_id=re.sub("<"+parameter.name+".*?>",parameter.name,uri_for_operation_id)return"{}~~{}".format(method.upper(),uri_for_operation_id).replace("/","~")You can implement your own code (as long as the method signature matches this), and set that to
be used in the app config:app.config.OPENAPI_OPERATION_ID_FN = my_operation_id_fnFor example, to makecamelCaseoperationIds from the Routes' handlers (but be advised you that
you must ensure your handlers' function names are globally unique, not "just" unique within the
Blueprint), you could use the suppliedcamel_case_operation_id_fn:defcamel_case_operation_id_fn(method:str,uri:str,route:sanic.router.Route)->str:ifhasattr(route.handler,"__class__")andhasattr(route.handler,"handlers"):# These are `sanic.view.CompositeView`s_method_handler=route.handler.handlers.get(method.upper())if_method_handler:handler_name=method+"_"+_method_handler.__name__else:raiseValueError(f"No{method.upper()}handler found for{uri}handlers:{route.handler.handlers}")elifhasattr(route.handler,"__name__"):iflen(route.methods)>1:# This fn will be called many times, once per method, but we should prefix the handler_name with this# prefix to make the operationIds globally unique. If the route is only used by one method, use that# handler's name.handler_name=method+"_"+route.handler.__name__else:handler_name=route.handler.__name__else:raiseNotImplementedError()returnsimple_snake2camel(handler_name)and set it likeapp.config.OPENAPI_OPERATION_ID_FN = sanic_openapi3e.openapi.camel_case_operation_id_fnNote that this does not change theparameter's  names.hide_openapi_self = app.config.get("HIDE_OPENAPI_SELF", True)
show_excluded = app.config.get("SHOW_OPENAPI_EXCLUDED", False)
show_unused_tags = app.config.get("SHOW_OPENAPI_UNUSED_TAGS", False)In practice, you don't usually want to document the/swaggernor/openapiroutes, but by settingapp.config.HIDE_OPENAPI_SELF = Falseyou can have them appear in the generated spec (and therefore swagger
too).Your@doc.excludeannotations are always respected, but if your
config hasapp.config.SHOW_OPENAPI_EXCLUDED = Truethen asecondspec at/openapi/spec.all.jsonis created. You generally won't want
these to be on your production deployment, but you may want it for dev
and test purposes.app.configPurposeapp.config.get("HIDE_OPENAPI_SELF", True)If True, hide the /openapi and /swagger endpoints from the swagger UI. Usually set to True.app.config.get("HIDE_SANIC_STATIC", True)If True, hide any sanic paths designed to serve up static files like images. Usually set to True.app.config.get("SHOW_OPENAPI_EXCLUDED", False)See above.app.config.get("SHOW_OPENAPI_UNUSED_TAGS", False)If True, your spec will include tags which have no visible routes. Usually set to False.app.config.get("OPENAPI_OPERATION_ID_FN", default_operation_id_fn)See above.app.config.get("OPENAPI_COMPONENTS")Allows you to build your ownComponentsfor the spec.app.config.get("API_CONTACT_NAME")If set, creates aContactwith a.name. Combines with the otherContactattributes.app.config.get("API_CONTACT_URL")If set, creates aContactwith a.url. Combines with the otherContactattributes.app.config.get("API_CONTACT_EMAIL")If set, creates aContactwith a.email. Combines with the otherContactattributes.app.config.get("API_LICENSE_NAME")If set, creates aLicensewith a.name. Combines with the otherLicenseattributes.app.config.get("API_LICENSE_URL")If set, creates aLicensewith a.url. Combines with the otherLicenseattributes.app.config.get("API_TITLE", "API")Set a better.namethan "API" for your API!app.config.get("API_DESCRIPTION", "Description")Set a better.descriptionthan "Description" for your API!app.config.get("API_TERMS_OF_SERVICE_URL")If set, adds atermsOfServiceto your.infoapp.config.get("API_VERSION", "v1.0.0")Set a better.versionfor your API!app.config.get("OPENAPI_SERVERS")Allows you to build your ownServersfor the spec.app.config.get("OPENAPI_SECURITY")Allows you to build your ownSecurityfor the spec.app.config.get("OPENAPI_EXTERNAL_DOCS")If set, adds anExternalDocumentationto your specapp.config.get("OPENAPI_YAML_CONTENTTYPE", default_yaml_content_type)See your/openapi/spec.ymlin a browser by setting this totext/plainOAS Object maturitysanic-openapi3eis being used in production, and all of the spec is implemented. Most of the spec is known to be in
production use, but some of the spec's objects are marked here as "beta" due to no known production use.Classsanic-openapi3e maturitynotesComponentsproduction/stableContactproduction/stableExampleproduction/stableExternalDocumentationproduction/stableInfoproduction/stableLicenseproduction/stableLinkbetano known usageMediaTypeproduction/stableOAuthFlowproduction/stableOAuthFlowsproduction/stableOpenAPIv3production/stableOperationproduction/stableParameterproduction/stablePathItemproduction/stablePathsproduction/stableReferenceproduction/stableRequestBodyproduction/stableResponsesproduction/stableSchemaproduction/stableSecuritySchemeproduction/stableServerproduction/stableTagproduction/stableResponseproduction/stableno known use of the "default" valueSecurityRequirementbetaonly the[]empty-list override knownDiscriminatorbetano known usageEncodingstableno known usageHeaderproduction/stableServerVariablebetano known usageXMLbetano known usageCallbacknonenot implementedsanic-openapi3eis built to createOpenAPI 3.0.2specs.Changelogv0.9.10Adds CORSAccess-Control-Allow-Origin: *headers to the OpenAPI/openapi/spec*endpoints.v0.9.9Fixes type validation bug foroneOf.v0.9.8Further extends complexSchemaobjects by extending howallOf,oneOf,anyOf, andnotare handled.v0.9.7Extends the ability to define complexSchemaobjects.v0.9.6Fixes a bug where None values in examples would cause a runtime error.v0.9.5Improves how the.components.responsesare rendered.Fixes issues with the "static"/"frozen" predefinedSchemaobjects.v0.9.4Adds a@doc.responses()to give an easier way of documenting a route.v0.9.3Adds a@doc.security()to override security requirements on a route.Removes entries withfalsevalues from the spec iffalseis the default value. This makes the specs smaller and
are more idiomatic.v0.9.2Fixes an issue of rendering SecurityRequirement when there were no entries in the list.