{"id":18076560,"url":"https://github.com/strongbugman/apiman","last_synced_at":"2025-05-07T13:06:24.948Z","repository":{"id":34053608,"uuid":"167659251","full_name":"strongbugman/apiman","owner":"strongbugman","description":"Api document support for any python web framework","archived":false,"fork":false,"pushed_at":"2023-10-25T07:05:58.000Z","size":171,"stargazers_count":111,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-07T13:06:16.281Z","etag":null,"topics":["api","bottle","django","falcon","flask","openapi","redoc","starlette","swagger","swagger-ui","tornado"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/strongbugman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-26T06:49:47.000Z","updated_at":"2024-10-06T14:59:43.000Z","dependencies_parsed_at":"2024-12-26T19:16:53.630Z","dependency_job_id":null,"html_url":"https://github.com/strongbugman/apiman","commit_stats":{"total_commits":77,"total_committers":1,"mean_commits":77.0,"dds":0.0,"last_synced_commit":"afe107503222f3ee6f95230e5ced19198f9a17d3"},"previous_names":["strongbugman/starchart"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strongbugman%2Fapiman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strongbugman%2Fapiman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strongbugman%2Fapiman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strongbugman%2Fapiman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/strongbugman","download_url":"https://codeload.github.com/strongbugman/apiman/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252883204,"owners_count":21819160,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","bottle","django","falcon","flask","openapi","redoc","starlette","swagger","swagger-ui","tornado"],"created_at":"2024-10-31T11:10:21.514Z","updated_at":"2025-05-07T13:06:24.924Z","avatar_url":"https://github.com/strongbugman.png","language":"Python","funding_links":[],"categories":["Extensions"],"sub_categories":["API (REST, GraphQL...)"],"readme":"# APIMAN\n\n[![Build](https://github.com/strongbugman/apiman/workflows/test/badge.svg)](https://github.com/strongbugman/apiman/actions)\n[![codecov](https://codecov.io/gh/strongbugman/apiman/branch/master/graph/badge.svg)](https://codecov.io/gh/strongbugman/apiman)\n\nAPIMAN provide a easy way to integrate OPENAPI manual/document for python web project\n\n## Features\n\n* Out of the box for Starlette, Flask, Django, Bottle, Tornado and Falcon\n* Whole OpenAPI 2 and 3 specification support\n* Configurable [SwaggerUI](http://swagger.io/swagger-ui/) and [RedocUI](https://rebilly.github.io/ReDoc/)\n* Request data(json/xml/form body, query, header, cookie, path args) validation by API specification\n\n## Install\n\n```shell\npip install -U apiman\n```\n\n## Usage\n\n### create openapi basic file\n\n```yml\nopenapi: \"3.0.0\"\ninfo:\n  title: 'APIMAN'\n  version: '0.1'\n  description: 'API manual for example'\n```\n\n### create apiman instance\n\n```python\nfrom starlette.applications import Starlette\nfrom apiman.starlette import Apiman \n# from flask import Flask\n# from apiman.flask import Apiman\n\napiman = Apiman(template=\"./docs/openapi.yml\")\n```\n\n### register apiman\n\n```python\n# app = Flask(__name__)\napp = Starlette()\napiman.init_app(app)\n```\n\n### add document for api endpoint\n\nby docstring:\n\n```python\n@app.route(\"/hello/\", methods=[\"GET\"])\nasync def hello(req: Request):\n    \"\"\"\n    There is normal docstring, Below is OpenAPI specification\n    ---\n    summary: hello api\n    tags:\n    - test\n    parameters:\n    - name: name\n      in: query\n      required: True\n      schema:\n        type: string\n    responses:\n      \"200\":\n        description: OK\n    \"\"\"\n    return Response(f\"hello\")\n```\n\nby yaml content:\n\n```python\n@app.route(\"/hello/\", methods=[\"GET\"])\n@apiman.from_yaml(\n    \"\"\"\n    summary: hello api\n    tags:\n    - test\n    parameters:\n    - name: name\n      in: query\n      required: True\n      schema:\n        type: string\n    responses:\n      \"200\":\n        description: OK\n    \"\"\"\n)\nasync def hello(req: Request):\n    return Response(f\"hello\")\n```\n\n(why yaml content? for my usage, just for f-string support)\n\nby file:\n\n```python\n@app.route(\"/hello/\", methods=[\"GET\"])\n@apiman.from_file(\"./docs/hello.yml\")\nasync def hello(req: Request):\n    return Response(f\"hello\")\n\n@app.route(\"/hello/\", methods=[\"GET\"])\n@apiman.from_file(\"./docs/hello.json\")\nasync def hello(req: Request):\n    return Response(f\"hello\")\n```\n\n### validate defined OPENAPI specification\n\n```python\napiman.validate_specification()\n```\n\n### run app and browse swagger ui at `server:port/apiman/swagger` or `server:port/apiman/redoc`\n\n## More Usage\n\n### Config apiman\n\nConfig apiman's ui url, title and ui html template\n\n```python\napiman = Apiman(\n  title=\"OpenAPI document\",\n  specification_url=\"/apiman/specification/\",\n  swagger_url=\"/apiman/swagger/\",\n  redoc_url=\"/apiman/redoc/\",\n  swagger_template=\"swagger.html\",\n  redoc_template=\"redoc.html\",\n  template=\"template.yaml\",\n)\n```\n### reuseable schema\n\nWe can define some OpenAPI schema or parameters for config usage, in openapi.yml:\n\n```yml\nopenapi: \"3.0.0\"\ninfo:\n  title: 'APIMAN'\n  version: '0.1'\n  description: 'API manual for example'\ndefinitions:\n  Cat:\n    type: object\n    required:\n    - name\n    properties:\n      name:\n        type: string\n      age:\n        type: integer\n        format: int32\n        minimum: 0\n```\n\nor by code:\n\n```python\napiman.add_schema(\n    \"Cat\",\n    {\n        \"properties\": {\n            \"name\": {\"type\": \"string\"},\n            \"age\": {\"type\": \"integer\", \"minimum\": 0},\n        },\n        \"type\": \"object\",\n    },\n)\n```\n\n(All specification data store in `apiman.specification`), then use it by OpenAPI way:\n\n```python\n@apiman.from_yaml(\n    \"\"\"\n    responses:\n      \"200\":\n        description: OK\n        schema:\n          $ref: '#definitions/Cat'\n    \"\"\"\n)\n```\n\n### request validation\n\nvalide request by `validate_request`\n\n```python\n@app.route(\"/hello/\", methods=[\"POST\"])\n@apiman.from_yaml(\n    \"\"\"\n    summary: hello api\n    tags:\n    - test\n    parameters:\n    - name: name\n      in: query\n      required: True\n      schema:\n        type: string\n    - name: x-custom-param\n      schema:\n        type: string\n      in: header\n      required: True\n    - name: cookie_param\n      schema:\n        type: string\n      in: cookie\n    requesteBody:\n      required: true\n      content:\n        application/json:\n          schema:\n            type: object\n            properties:\n              key:\n                type: integer\n            required: [\"key\"]\n    responses:\n      \"200\":\n        description: OK\n    \"\"\"\n)\nasync def get(self, req: Request):\n    # await req.json()\n    # apiman.validate_request(req)\n    await apiman.async_validate_request(req)\n    # get validated params by original ways\n    request.query_params\n    request.cookies\n    request.headers\n    request.json()\n    ...\n```\n\n(for sync code, call `apiman.validate_request(req)`)\n\nThis method will find this request's OpenAPI specification and request params(query, path, cookie, header, body) then validate it, we can assess validated req params by origin way or raise validation exception.(by [jsonschema_rs](https://github.com/Stranger6667/jsonschema-rs/tree/master/bindings/python))\n\n\n### limit\n\n#### type limit\n\nAll request params type is **origin type**, so query/header/cookie params is always **string**, we should define this params type to string or we will get validation Exception(eg, flask's path params can be int type original).\n\nBut if we want some integer param for string type, set regex `pattern` in specification, eg:\n\n```yml\nid:\n  type: string\n  pattern: '^\\d+$'\n```\n\nor just use body data for rich format\n\n## Examples\n\nLet's see a Starlette example app:\n\n```python\n\"\"\"OpenAPI2(Swagger) with Starlette\n\"\"\"\nfrom starlette.applications import Starlette\nfrom starlette.requests import Request\nfrom starlette.responses import JSONResponse\nfrom starlette.endpoints import HTTPEndpoint\nfrom uvicorn import run\nfrom starlette.testclient import TestClient\n\nfrom apiman.starlette import Apiman\n\n\napp = Starlette()\napiman = Apiman(template=\"./examples/docs/cat_template.yml\")\napiman.init_app(app)\n\n\n# define data\nCATS = {\n    1: {\"id\": 1, \"name\": \"DangDang\", \"age\": 2},\n    2: {\"id\": 2, \"name\": \"DingDing\", \"age\": 1},\n}\n# add schema definition\napiman.add_schema(\n    \"Cat\",\n    {\n        \"properties\": {\n            \"id\": {\"description\": \"global unique\", \"type\": \"integer\"},\n            \"name\": {\"type\": \"string\"},\n            \"age\": {\"type\": \"integer\"},\n        },\n        \"type\": \"object\",\n    },\n)\n\n\n# define routes and schema(in doc string)\n@app.route(\"/cat/\")\nclass Cat(HTTPEndpoint):\n    \"\"\"\n    Declare multi method\n    ---\n    get:\n      summary: Get single cat\n      tags:\n      - cat\n      parameters:\n      - name: id\n        type: string\n        in: path\n        required: True\n      - name: x-client-version\n        type: string\n        in: header\n        required: True\n      responses:\n        \"200\":\n          description: OK\n          schema:\n            $ref: '#/definitions/Cat'\n        \"404\":\n          description: Not found\n    \"\"\"\n\n    def get(self, req: Request):\n        # validate params in path query header and cookie by schema (only support string type)\n        apiman.validate_request(req)\n        return JSONResponse(CATS[int(req.path_params[\"id\"])])\n\n    def delete(self, req: Request):\n        \"\"\"\n        Declare single method\n        ---\n        summary: Delete single cat\n        tags:\n        - cat\n        parameters:\n        - name: id\n          type: integer\n          in: path\n          required: True\n        responses:\n          \"204\":\n            description: OK\n            schema:\n              $ref: '#/definitions/Cat'\n          \"404\":\n            description: Not found\n        \"\"\"\n        cat = CATS.pop(int(req.path_params[\"id\"]))\n        return JSONResponse(cat)\n\n\n# define doc by yaml or json file\n@app.route(\"/cats/\", methods=[\"GET\"])\n@apiman.from_file(\"./examples/docs/cats_get.yml\")\ndef list_cats(req: Request):\n    return JSONResponse(list(CATS.values()))\n\n\n@app.route(\"/cats/\", methods=[\"POST\"])\n@apiman.from_file(\"./examples/docs/cats_post.json\")\nasync def list_cats(req: Request):\n    await req.json()\n    # validate json body\n    apiman.validate_request(req)\n    cat = await req.json()\n    CATS[cat[\"id\"]] = cat\n    return JSONResponse(cat)\n\n\nif __name__ == \"__main__\":\n    apiman.validate_specification()\n    run(app)\n```\n\nThen we get swagger web page at [http://localhost:8000/apiman/swagger/](http://localhost:8000/apiman/swagger/):\n![WebPage](docs/SwaggerUI.jpg)\n\nSee **examples/** for more examples\n\n## How it works\n\n* Provide a base class to handle api specification's collection\n* Provide extension for every web framework to extract api specification and register http endpoints to show UI web page and specification \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrongbugman%2Fapiman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstrongbugman%2Fapiman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrongbugman%2Fapiman/lists"}