{"id":18761067,"url":"https://github.com/michaelawyu/nanopie","last_synced_at":"2026-05-02T10:42:22.742Z","repository":{"id":57444843,"uuid":"230346116","full_name":"michaelawyu/nanopie","owner":"michaelawyu","description":"A lightweight framework for building Python microservices and API services with best practices.","archived":false,"fork":false,"pushed_at":"2020-09-02T11:54:01.000Z","size":526,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-29T13:16:08.675Z","etag":null,"topics":["api-service","microservices","python"],"latest_commit_sha":null,"homepage":"https://nanopie.readthedocs.io/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/michaelawyu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-27T00:33:11.000Z","updated_at":"2020-09-02T22:34:10.000Z","dependencies_parsed_at":"2022-09-26T17:30:17.440Z","dependency_job_id":null,"html_url":"https://github.com/michaelawyu/nanopie","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelawyu%2Fnanopie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelawyu%2Fnanopie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelawyu%2Fnanopie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelawyu%2Fnanopie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michaelawyu","download_url":"https://codeload.github.com/michaelawyu/nanopie/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239653910,"owners_count":19675183,"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-service","microservices","python"],"created_at":"2024-11-07T18:14:53.136Z","updated_at":"2025-12-04T00:30:17.301Z","avatar_url":"https://github.com/michaelawyu.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nanopie\n\n## Overview\n\nnanopie is a lightweight Python framework for writing microservices and API\nbackends. The framework provides pluggable solutions for a number of tasks,\nsuch as input validation, serialization/deserialization, authentication,\nlogging, and tracing, commonly encountered in microservice and API backend\ndevelopment.\n\n**Note**: nanopie in still in Development. At this moment, nanopie supports\nonly HTTP (RESTful) microservices/API backends.\n\n## Features\n\n* **Bring your own transport**\n\n    Python has a large number of web framework + server solutions, such\n    [flask](https://flask.palletsprojects.com/en/1.1.x/) +\n    [gunicorn](http://www.gunicorn.org/),\n    [quart](https://pgjones.gitlab.io/quart/) + \n    [uvicorn](https://www.uvicorn.org/),\n    and [Tornado](http://www.tornadoweb.org/); many of these options are\n    mature, time-tested, and widely-adopted, and it is not nanopie's design\n    goal to create another system for processing requests. Instead, nanopie\n    leaves the request processing part up to a transport of your choice.\n    This allows developers to use nanopie with any web framework and\n    server they are most familiar with; it is also possible to\n    switch transports at will as you see fit without having to drastically\n    change the code.\n\n* **Data modeling and validation**\n\n    nanopie provides a data modeling mechansim, based on metaclasses\n    in Python ([PEP 3115](https://www.python.org/dev/peps/pep-3115/)),\n    that helps developers model the inputs and outputs to the endpoints of\n    their microservices and API backends, making it easy to access data\n    idiomatically. nanopie models also features automatic data validation,\n    saving the trouble of writing additional checking logic in the code.\n\n    ``` python\n    from nanopie import Model, IntField, StringField\n\n    class User(Model):\n        name = StringField(max_length=20, min_length=1, pattern=\"[a-zA-Z]*\")\n        age = IntField(maximum=100, minimum=0)\n\n    user = User(name=\"Albert Wesker\", age=49)\n    print(user.name)\n    print(user.age)\n\n    # This will raise an exception\n    user = User(name=\"Link in BotW\", age=117)\n    ```\n\n    In addition, nanopie models work seamlessly with the built-in\n    serialization/deserialization solution; once configured, nanopie\n    can automatically parse incoming requests into data model objects,\n    and return data model objects as responses in interchangable formats.\n\n* **Pluggable authentication, serialization, logging, and tracing solutions**\n\n    nanopie provides a number of solutions for common tasks in microservices and\n    API backend development, known as handlers. You can add handlers to the\n    endpoints of your microservices and API backends so that they can perform\n    the tasks when requests arrive at the endpoint:\n    `HTTPBasicAuthenticationHandler`, for example, authenticates requests\n    based on the `Basic` HTTP Authentication Scheme\n    ([RFC 7617](https://tools.ietf.org/html/rfc7617)),\n    and `OpenTelemetryTracingHandler`, for another example, traces the execution\n    of your endpoint and writes the trace to `STDOUT/STDERR` every time a\n    request is being processed.\n\n    Handlers can be chained together to execute a sequence of actions.\n    A typically pattern is to chain an authentication handler,\n    a logging handler, a tracing handler, a serialization handler together\n    for an endpoint, so that when a request arrives at the endpoint,\n    it is authenticated, logged, and traced by respective handlers, with its\n    payload deserialized into an object of nanopie data model automatically.\n\n    ![Handlers](https://github.com/michaelawyu/nanopie/blob/master/docs/images/handlers_alt.png?raw=true)\n\n## Installation\n\nTo install nanopie, run the following command:\n\n``` sh\npip install nanopie\n```\n\n## Getting started\n\nThe following example showcases how to use nanopie to write a simple\nmicroservice with RESTful HTTP API for user management with one\nendpoint, `get_user`, which returns a user with a specific user ID.\n\n### Setup\n\nInstall `nanopie` and `flask`, a popular Python web micro-framework in your\nproject. A `Flask` app will serve as the transport of your `nanopie`\nmicroservice in this example:\n\n``` sh\npip install nanopie flask\n```\n\n### Defining the data model\n\nThe microservice you will build in this example, as introduced at the beginning\nof this section, has only one endpoint, which returns the information of\na specific user to clients. To model the user information, create a `user.py`\nscript in your project with the following `nanopie` data model:\n\n``` python\nfrom nanopie import (\n    Model,\n    IntField,\n    StringField\n)\n\nclass User(Model):\n    name = StringField(max_length=20, min_length=1, pattern=\"[a-zA-Z]*\")\n    age = IntField(maximum=100, minimum=0)\n```\n\nThe `User` model includes two fields, `name`, and `age`, which takes a\n`String` value and a `Integer` value respectively. \n\nNote that both fields have hints and constraints specified:\n    \n* The `name` field must be an alphabetic string with a maximum of 20\ncharacters and a minimum of 1 character.\n* The `age` field must be an integer no smaller than `1` and no greater\nthan `100`.\n\n`nanopie` can use these hints and constraints to validate data.\n\n### Writing the microservice\n\nCreate a Python script, `main.py`, as follows in your project. The script\nfirst creates a Flask app, then creates a `FlaskService` object that nanopie\nprovides for using a Flask app as the transport of your microservice. The\n`FlaskService` object includes a number of methods (decorators) for specifying\nHTTP microservice or API backend endpoints; in the case of this project, you\nwill create a `GET` endpoint which returns a `User` object as defined by\nthe `User` model detailed in the previous section.\n\n``` python\nfrom flask import Flask\nfrom nanopie import (\n    FlaskService\n)\n\n# Import the User model created in the last step\nfrom user import User\n\n# Create a Flask app\napp = Flask(__name__)\n# Create a nanopie microservice with the Flask app as transport\nsvc = FlaskService(app=app)\n\n# Create a HTTP RESTful API endpoint with the `GET` HTTP method\n@svc.get(name=\"get_user\", rule=\"/users/\u003cint:uid\u003e\")\ndef get_user(uid):\n    # Always return the same user regardless of the ID provided\n    return User(name=\"Albert Wesker\", age=49)\n\nif __name__ == \"__main__\":\n    app.run(debug=True, port=8080)\n```\n\nRun the script:\n\n``` sh\npython main.py\n```\n\nOpen the browser of your system and visit `127.0.0.1:8080/users/1`. It should\nreturn the following JSON string:\n\n``` json\n{\"name\": \"Albert Wesker\", \"age\": 49}\n```\n\n### Adding authentication capabilities\n\nAt this moment your microservice accepts any request from the network. To\nprotect data from unauthorized sources, you should authenticate\nrequests in the microservice. nanopie provides a number of pluggable\nauthentication solutions, known as authentication handlers, that perform\nthe authentication of requests automatically; for this project, you will\nenable HTTP OAuth2 Bearer Token with JWT ([RFC 6750](https://tools.ietf.org/html/rfc6750),\n[RFC 7519](https://tools.ietf.org/html/rfc7519)) based authentication in the\nservice using the\nauthentication handler `HTTPOAuth2BearerJWTAuthenticationHandler`.\n\nTo enable authentication, edit `main.py` and create\na `HTTPOAuth2BearerJWTAuthenticationHandler`:\n\n``` python\nfrom flask import Flask\nfrom nanopie import (\n    FlaskService,\n    HTTPOAuth2BearerJWTModes,\n    HTTPOAuth2BearerJWTAuthenticationHandler\n)\n\n# Import the User model created in the last step\nfrom user import User\n\n# Create a Flask app\napp = Flask(__name__)\n\n# Create an authentication handler\nauthentication_handler = HTTPOAuth2BearerJWTAuthenticationHandler(\n    # The secret used for generating JWT tokens\n    key_or_secret='my-secret',\n    # The algorithm used for generating JWT tokens\n    algorithm='HS256',\n    # Specify that the token comes in the URI query string of the HTTP request\n    mode=HTTPOAuth2BearerJWTModes.URI_QUERY\n)\n\n# Create a nanopie microservice with the authentication handler just created\nsvc = FlaskService(app=app,\n                   authn_handler=authentication_handler)\n\n...\n```\n\nRun the script again:\n\n``` sh\npython main.py\n```\n\nOpen the browser of your system and visit `127.0.0.1:8080/users/1`. Without\na valid token, your service should return a `401 Unauthorized` error.\n\nYou can generate a valid token using the algorithm and secret specified in\nthe code snippet. There are many Python packages available for this purpose;\nyou can also use [`jwt.io`](https://jwt.io/) to get a token for testing\npurposes interactively. Alternatively, use the token below:\n\n```\neyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.EpM5XBzTJZ4J8AfoJEcJrjth8pfH28LWdjLo90sYb9g\n```\n\nAnd point your browser to `127.0.0.1/users/1?access_token=YOUR-ACCESS-TOKEN`\n(replace `YOUR-ACCESS-TOKEN` with a token of your own). Your request is now\nauthenticated and you should see the user data returned to you.\n\n### Adding logging and tracing capabilities\n\nIt is common for developers to use logging and tracing to observe their\nmicroservices and API backends in action. Similar to authentication\nhandlers, nanopie provides a number of pluggable logging and tracing solutions\nas well, integrating with the\n[standard Python logging module](https://docs.python.org/3/library/logging.html)\nand [OpenTelemetry](https://opentelemetry.io/) respectively.\n\nTo enable logging and tracing, edit `main.py` for another time and create\na `LoggingHandler` and `OpenTelemetryTracingHandler`:\n\n``` python\nfrom flask import Flask\nfrom nanopie import (\n    FlaskService,\n    HTTPOAuth2BearerJWTModes,\n    HTTPOAuth2BearerJWTAuthenticationHandler,\n    LoggingHandler,\n    OpenTelemetryTracingHandler\n)\n\n# Import the User model created in the last step\nfrom user import User\n\n# Create a Flask app\napp = Flask(__name__)\n\n# Create an authentication handler\n...\n\n# Create a logging handler and a tracing handler.\nlogging_handler = LoggingHandler()\ntracing_handler = OpenTelemetryTracingHandler()\n\n# Create a nanopie microservice with an authentication handler,\n# a logging handler, and a tracing handler.\nsvc = FlaskService(app=app,\n                   authn_handler=authentication_handler,\n                   logging_handler=logging_handler,\n                   tracing_handler=tracing_handler)\n```\n\n\nRun the script for another time:\n\n``` sh\npython main.py\n```\n\nOpen the browser of your system and visit\n`127.0.0.1/users/1?access_token=YOUR-ACCESS-TOKEN`. Check the output\nin the terminal running the Python script, you should see outputs similar\nto the snippets below:\n\n```\n# The log entry nanopie writes when the execution of an endpoint begins.\n{\"host\": \"YOUR-HOST\", \"logger\": \"nanopie.logging.base\", \"level\": \"INFO\", \"module\": \"base\", \"func\": \"__call__\", \"message\": \"Entering span unspecified_span.\"}\n\n# The trace of the execution of the endpoint.\n{\"name\": \"unspecified\", \"context\": {\"trace_id\": \"7732924096377129845968717310647764965\", \"span_id\": \"7510401874971537686\", \"trace_state\": \"{}\", \"is_remote\": \"False\"}, \"kind\": \"SpanKind.SERVER\", \"parent\": null, \"start_time\": \"2020-08-11T20:23:01.350515Z\", \"end_time\": \"2020-08-11T20:23:01.351445Z\", \"attributes\": \"{}\"}\n\n# The long entry nanopie writes when the execution of an endpoint completes.\n{\"host\": \"YOUR-HOST\", \"logger\": \"nanopie.logging.base\", \"level\": \"INFO\", \"module\": \"base\", \"func\": \"__call__\", \"message\": \"Exiting span unspecified_span.\"}\n```\n\n## What's next\n\nLearn more about nanopie in its [documentation](https://nanopie.readthedocs.io).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelawyu%2Fnanopie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichaelawyu%2Fnanopie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelawyu%2Fnanopie/lists"}