{"id":27134790,"url":"https://github.com/joedeveloper55/httpglue","last_synced_at":"2025-08-01T18:11:24.795Z","repository":{"id":172486917,"uuid":"541214255","full_name":"joedeveloper55/httpglue","owner":"joedeveloper55","description":"extremely minimal python http application framework for rest api microservices, supports wsgi or asgi","archived":false,"fork":false,"pushed_at":"2025-07-12T19:10:11.000Z","size":111,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-19T13:49:00.546Z","etag":null,"topics":["framework","http","python3","wsgi"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joedeveloper55.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2022-09-25T15:20:56.000Z","updated_at":"2025-07-12T19:10:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"c0a1fdde-50d8-42d9-8d48-e770163f1db6","html_url":"https://github.com/joedeveloper55/httpglue","commit_stats":null,"previous_names":["joedeveloper55/httpglue"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/joedeveloper55/httpglue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joedeveloper55%2Fhttpglue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joedeveloper55%2Fhttpglue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joedeveloper55%2Fhttpglue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joedeveloper55%2Fhttpglue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joedeveloper55","download_url":"https://codeload.github.com/joedeveloper55/httpglue/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joedeveloper55%2Fhttpglue/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268273007,"owners_count":24223790,"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","status":"online","status_checked_at":"2025-08-01T02:00:08.611Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["framework","http","python3","wsgi"],"created_at":"2025-04-08T00:59:16.843Z","updated_at":"2025-08-01T18:11:24.767Z","avatar_url":"https://github.com/joedeveloper55.png","language":"Python","readme":"# httpglue\n\nhttpglue is an **extremely minimal** python http application framework.\n\nIt is optimized for building small to medium sized rest apis and http microservices that can be run on wsgi or asgi servers.\n\nIt pushes simple to its limits while still providing just enough structure and functionality to be useful. It is a kind of *nanoframework* if you will, taking simplicity and minimalism a bit further than the typical 'microframework'.\n\nExcluding exceptions, the entire api defines only five classes: WsgiApp, AsgiApp, Headers, Request and Response. The WsgiApp object has only 5 public methods; The AsgiApp object has only 9 public methods. The Headers, Request, and Response objects are just plain old python objects.\n\nThere are no dependencies on any third party libraries. The standard library is all that is required. It is 100% pure python. It will work wherever you have a recent enough (3.6 or greater) python installation without any hassle. The maintainers are commited to following [semvar](https://semver.org/) conventions to keep your builds reliable and predictable.\n\nhttpglue also pushes explicitness and transparency to their limits. \n\nIt has a philosophy of *no magic*. It puts *configuration over convention*. What you see is what you get. httpglue never sends back a response that has not been specified by the application programmer. The application programmer explicitly codes for all error conditions and specifies a static default error response for cases that were not anticipated or handled. There are no implicit behind the scenes configurational defaults happening.\n\nBecasue transparency is a central concern, the framework was designed to make apps written in httpglue as easily testable as possible from day 1 since sometimes the only way to really know what some code does is to 'test it'. It supports thorough testing of your apps regardless of the type (unit, integration, end to end, etc), testing style (mockisist vs classicist), and frameowrk (unittest, pytest, etc) you choose. It supports all of this not by providing new special test utilities of its own (since that could lock you into specific test frameworks and methedologies), but by ensuring the api itself has a design that guaruntees easy and straightforward testing.\n\nTo quickly get the flavor of httpglue, take a look at the below hello_world.py example:\n\n```python\nimport logging\nimport time\n\nfrom httpglue import Response\nfrom httpglue import WsgiApp\nfrom httpglue import NoMatchingPathError\nfrom httpglue import NoMatchingMethodError\n\n\ndef make_app():\n    logging.basicConfig(level=logging.INFO)\n    logger = logging.getLogger(__name__)\n\n    app = WsgiApp(\n        logger=logger,\n        default_fallback_err_res=Response(\n            status=500,\n            headers={},\n            body=b'500 Internal Server Error'\n        )\n    )\n\n    app.sleepytime = time.sleep\n\n    app.register_endpoint(\n        ['GET'], '/hello',\n        handle_get_hello\n    )\n\n    app.register_err_handler(\n        [NoMatchingPathError],\n        handle_no_matching_path\n    )\n    app.register_err_handler(\n        [NoMatchingMethodError],\n        handle_no_matching_method\n    )\n\n    return app\n\n\ndef handle_get_hello(app, req):\n    app.logger.info('saying hello in a second.')\n    app.sleepytime(1)\n    return Response(\n        status=200,\n        headers={},\n        body=b'Hello world!'\n    )\n\n\ndef handle_no_matching_path(app, e, req):\n    return Response(\n        status=404,\n        headers={},\n        body=b'404 Not Found'\n    )\n\n\ndef handle_no_matching_method(app, e, req):\n    return Response(\n        status=405,\n        headers={\n            'Allow': ','.join(e.allowed_methods)\n        },\n        body=b'405 Method Not Allowed'\n    )\n```\n\nIf you simply copy this to a file named \"my_app.py\" and run it with \n\n\u003e pip install httpglue; pip install waitress; waitress-serve --host 127.0.0.1 --call my_app:make_app\n\nyou can actually play around with this example with no other steps. An http application is now servicing http requests at localhost:8080.\n\nSince testability is important to this framework, here's an example of a single test case for the above app:\n\n```python\nimport unittest\nfrom unittest import mock\n\nfrom httpglue import Request\n\nfrom my_app import make_app\n\n\nclass TestGetHelloEndpoint(unittest.TestCase):\n    @classmethod\n    def setUpClass(self):\n        self.app = make_app()\n\n    def test_get_hello_endpoint(self):\n        req = Request(\n            method='GET',\n            path='/hello',\n            headers={},\n            body=b''\n        )\n\n        # mock away the wait operation for this test\n        with mock.patch.object(self.app, 'sleepytime') as m:\n            res = self.app.handle_request(req)\n            m.assert_called()\n\n        self.assertEqual(res.status, 200)\n        self.assertEqual(res.body, b'Hello world!')\n```\n\nIf you copy this to a file named test_my_app.py in the same directory as my_app.py you should be able to run this test with:\n\n\u003e PYTHONPATH=. python -m unittest test_my_app\n\nAs mentioned above, httpglue supports asgi as well as wsgi. Here's a simple eqivalent asgi app:\n\n```python\nimport asyncio\nimport logging\n\nfrom httpglue import Response\nfrom httpglue import AsgiApp\nfrom httpglue import NoMatchingPathError\nfrom httpglue import NoMatchingMethodError\n\n\ndef make_app():\n    logging.basicConfig(level=logging.INFO)\n    logger = logging.getLogger(__name__)\n\n    app = AsgiApp(\n        logger=logger,\n        default_fallback_err_res=Response(\n            status=500,\n            headers={},\n            body=b'500 Internal Server Error'\n        )\n    )\n\n    async def startup_routine(app):\n        app.logger.info('ready to serve requests in 2 seconds.')\n        app.sleepytime = asyncio.sleep\n        await app.sleepytime(2)\n        app.logger.info('ready now.')\n\n    app.register_startup_routine(startup_routine)\n\n    async def shutdown_routine(app):\n        app.logger.info('shutting down in 2 seconds.')\n        await app.sleepytime(2)\n        app.logger.info('shutdown successful.')\n\n    app.register_shutdown_routune(shutdown_routne)\n\n    app.register_endpoint(\n        ['GET'], '/hello',\n        handle_get_hello\n    )\n\n    app.register_err_handler(\n        [NoMatchingPathError],\n        handle_no_matching_path\n    )\n    app.register_err_handler(\n        [NoMatchingMethodError],\n        handle_no_matching_method\n    )\n\n    return app\n\n\nasync def handle_get_hello(app, req):\n    app.logger.info('saying hello in a second.')\n    await app.sleepytime(1)\n    return Response(\n        status=200,\n        headers={},\n        body=b'Hello world!'\n    )\n\n\nasync def handle_no_matching_path(app, e, req):\n    return Response(\n        status=404,\n        headers={},\n        body=b'404 Not Found'\n    )\n\n\nasync def handle_no_matching_method(app, e, req):\n    return Response(\n        status=405,\n        headers={\n            'Allow': ','.join(e.allowed_methods)\n        },\n        body=b'405 Method Not Allowed'\n    )\n```\n\nyou can copy this into a file named \"my_async_app.py\" and run it with:\n\n\u003e pip install httpglue; pip install uvicorn; uvicorn my_async_app:make_app()\n\nAnd here's a sample test case for your asgi app:\n\n```python\nimport asyncio\nimport unittest\nfrom unittest import mock\n\nfrom httpglue import Request\n\nfrom my_async_app import make_app\n\n\nclass TestGetHelloEndpoint(unittest.TestCase):\n    @classmethod\n    def setUpClass(self):\n        self.event_loop = asyncio.new_event_loop()\n\n        self.app = make_app()\n\n        self.event_loop.run_until_complete(\n            self.app.startup()\n        )\n\n    @classmethod\n    def tearDownClass(self):\n        self.event_loop.run_until_complete(\n            self.app.shutdown()\n        )\n\n        self.event_loop.close()\n\n    def test_get_hello_endpoint(self):\n        req = Request(\n            method='GET',\n            path='/hello',\n            headers={},\n            body=b''\n        )\n\n        with mock.patch.object(self.app, 'sleepytime') as m:\n            res = self.event_loop.run_until_complete(\n                self.app.handle_request(req)\n            )\n            m.assert_called()\n\n        self.assertEqual(res.status, 200)\n        self.assertEqual(res.body, b'Hello world!')\n```\n\nAssuming you placed this in a file named test_my_async_app.py in the same directory as my_async_App, you can run this with:\n\n\u003e PYTHONPATH=. python -m unittest test_my_async_app\n\nAs you can see above, this asgi example is almost an exact copy of the wsgi example, the only differences being that:\n\n  1. we registered async functions to endpoints and err_handlers rather than regular functions.\n\n  2. we registered a \"startup_routine\" async function and a \"shutdown_routine\" async function with the app (generally, this is needed for initializing and shutting down async helpers in asgi applications, such as an async pool of database connections)\n\n  3. the \"startup\" and \"shutdown\" async methods are exposed for running the startup and shutdown code in your unit tests, and the handle_request method is now async\n\nIf you want to know more, check out [our comprehensve documentation](https://github.com/joedeveloper55/httpglue/blob/master/API_DOCUMENTATION.md).\n\nAlso, For more robust examples that showcase how you can leverage httpglue to build more serious http applications, check out:\n\n* [The official httpglue wsgi exemplar project](https://github.com/joedeveloper55/httpglue/tree/master/example_proj) - An exemplar halfway between a trite \"hello world\" exemplar and a real world application. It is a simple CRUD-type json rest api for a single resource that persists data to postgres, authenticates users via basic auth, and leverages a thread pool for its database connections. It also comes with thorough unit tests. This is the best place to go to learn the idiomatic structure of an httpglue wsgi application and idiomatic patterns for testing such an application.   \n\n* [The official httpglue asgi exemplar project]() - An exemplar just like the above wsgi one, except it leverages python's async features and is backed by AWS's DynamoDB instead of Postgres. This is the best place to go to learn the idiomatic structure of an httpglue asgi apllication and idiomatic patterns for testing such an application.\n\n## Authors\n\n* Joseph P McAnulty\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoedeveloper55%2Fhttpglue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoedeveloper55%2Fhttpglue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoedeveloper55%2Fhttpglue/lists"}