{"id":21120785,"url":"https://github.com/sausheong/tanuki","last_synced_at":"2025-07-08T20:31:53.815Z","repository":{"id":66720212,"uuid":"201629156","full_name":"sausheong/tanuki","owner":"sausheong","description":"Tanuki is a polyglot web framework that allows you to develop web applications and services in multiple programming languages.","archived":false,"fork":false,"pushed_at":"2019-09-22T07:12:11.000Z","size":10545,"stargazers_count":62,"open_issues_count":0,"forks_count":2,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-04T10:43:48.063Z","etag":null,"topics":["go","golang","web-framework"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sausheong.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-08-10T12:45:19.000Z","updated_at":"2023-08-25T16:05:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"fecd8bb9-3434-4dd2-8cce-255cf1e8f8a3","html_url":"https://github.com/sausheong/tanuki","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sausheong/tanuki","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sausheong%2Ftanuki","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sausheong%2Ftanuki/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sausheong%2Ftanuki/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sausheong%2Ftanuki/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sausheong","download_url":"https://codeload.github.com/sausheong/tanuki/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sausheong%2Ftanuki/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264343644,"owners_count":23593763,"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":["go","golang","web-framework"],"created_at":"2024-11-20T03:19:07.394Z","updated_at":"2025-07-08T20:31:51.433Z","avatar_url":"https://github.com/sausheong.png","language":"Go","funding_links":["https://blog.tidelift.com/dependency-hell-is-inevitable"],"categories":[],"sub_categories":[],"readme":"\u003cdiv id=\"logo\" align=\"center\"\u003e\n    \u003cimg src=\"tanuki.png\" alt=\"Tanuki\" width=\"150\"/\u003e    \n\u003c/div\u003e\n\n# Tanuki\n\nWriting web apps are relatively straightforward nowadays especially when there are so many web frameworks to choose from. Just whip out a framework in your favourite programming language and start writing that MVP!\n\nWhat a lot of people (engineers included) trip over though, is that software is not static and over its lifespan, will grow and evolve. The libraries used in writing them are software too and they will also change and evolve. The programming languages used to write them will change and evolve too. And most importantly, the people writing the software grow and change, and quite often different people end up taking over and continuing the same software.\n\nAs you would expect, it takes an experienced and forward thinking engineer (or ‘architect’) to think about these changes, but as you would expect, even the most experienced engineers cannot predict the future. Horror stories of multi-month or even years to [just migrate from one version of a framework to another](https://github.blog/2018-09-28-upgrading-github-from-rails-3-2-to-5-2/) are quite common place. Some even regard [dependency hell](https://blog.tidelift.com/dependency-hell-is-inevitable) as inevitable.\n\nThere is no silver bullet solution — software is complex and difficult. I’ve been developing software (mostly web apps) for a good 25 years and this problem have been hitting me over the head over and over again. I’ve been toying with different possible answers for a long time and this is my latest attempt.\n\n## Sample web app\n\nI've created a sample web app to show how a simple Tanuki web app can be created. Please check out https://github.com/sausheong/pompoko.\n\n\n## How does it work\n\nThe basic premise is simple -- remove as much dependency between blocks of execution as possible.\n\nIn web apps, blocks of execution are _actions_ that are taken by the system when a user calls a particular URL route. In most web apps, these actions are organised into functions blocks called _handlers_. A handler takes in a HTTP request, performs the action, and returns a HTTP response. The action might or might not use data from the request but is likely to do so.\n\nA web app is a single piece of software, run in a single process. What if we take each handler and allow it to be developed independently and run as a separate process? \n\nTanuki is a polyglot web framework that allows developers to write a distributed web app in multiple programming languages. It has an acceptor, which is a HTTP server, that accepts HTTP requests and routes it accordingly to different handlers that can be implemented in different programming languages. The handlers receives a JSON string that has the HTTP request data from the acceptor and returns another JSON string that has the HTTP response data.\n\nThere are two types of handlers.\n\n### Executable binaries or scripts\n\nExecutable binaries or scripts (or _bins_) are files that are, well, executable. You can normally run them on the command line. As a Tanuki bin handler, it must be able to take process a JSON string (as the first command line argument) and return another JSON string to STDOUT. The input JSON contains HTTP request data and the output JSON has HTTP response data.\n\n### TCP socket server\n\nA listener is Tanuki handler that runs as a TCP socket servers. There are two types of listeners:\n\n1. A _local_ listener, which is started by Tanuki and runs in the same host as Tanuki\n2. A _remote_ listener, which is _not_ started by Tanuki and Tanuki assumes it is reachable (i.e. it is your responsibility to make sure it’s reachable)\n\nRemote listeners allow Tanuki to be distributed to multiple hosts. This effectively makes the web app distributed!\n\n## Installing Tanuki\n\nTanuki is written in Go. To install it in your platform, you can install [Go](https://golang.org) and get the source:\n\n```\ngo get github.com/sausheong/tanuki\n```\n\nThere will be downloadable binaries for your platform at a later date.\n\n## How to use Tanuki\n\nOnce you can have downloaded the source, you can build the command line tool `tanuki`. This command line tool is used for everything.\n\n```\ngo build\n```\n\nWith the command line tool, can you create a skeleton structure for your new web application or service:\n\n```\n./tanuki create poko\n```\n\nThis will create a new directory named `poko` with the necessary directories and sample files. To start your new Tanuki web app, go to your new application directory and run this:\n\n```\n./tanuki start\n```\n\nThis will start the Tanuki web app at port `8080`. You can change the port number or the IP address or hostname as well, just follow the instructions in the `tanuki` command line tool.\n\nBefore you can run any Tanuki web app you need to set up the handlers.\n\n### Handler configuration\n\nHandlers are configured in a `handlers.yaml` file by default. This is how a handler configuration file looks like:\n\n```yaml\n--- # handlers\n- method : get\n  route  : /_/hello/ruby\n  type   : bin\n  path   : bin/hello-ruby\n\n- method : post\n  route  : /_/hello/go\n  type   : bin\n  path   : bin/hello-go\n\n- method : get\n  route  : /_/hello/ruby/listener\n  type   : listener\n  local  : true \n  path   : listeners/hello-ruby\n\n- method : get\n  route  : /_/hello/go/listener\n  type   : listener\n  local  : false\n  path   : localhost:55771\n```\n\nThe configuration is pretty straightforward. The `method` parameter determines the type of HTTP method to be routed, the `route` determines the URL route, the `type` is the type of handler, either a /bin/ or a /listener/ and the `path` is either a path to the file, or a URL of the remote listener, which includes the hostname and the port. Finally, the `local` parameter determines if it’s a local or a remote listener.\n\n## Handler input and output\n\n### HTTP requests\n\nThe only input into the handlers (either bins or listeners) is the HTTP request JSON. The below is the Go structs for the JSON (I'm showing you the Go struct because Tanuki is written in Go).\n\n```go\n// RequestInfo corresponds to a HTTP 1.1 request\ntype RequestInfo struct {\n\tMethod           string                 `json:\"Method\"`\n\tURL              URLInfo                `json:\"URL\"`\n\tProto            string                 `json:\"Proto\"`\n\tHeader           map[string][]string    `json:\"Header\"`\n\tBody             string                 `json:\"Body\"`\n\tContentLength    int64                  `json:\"ContentLength\"`\n\tTransferEncoding []string               `json:\"TransferEncoding\"`\n\tHost             string                 `json:\"Host\"`\n\tParams           map[string][]string    `json:\"Params\"`\n\tMultipart        map[string][]Multipart `json:\"Multipart\"`\n\tRemoteAddr       string                 `json:\"RemoteAddr\"`\n\tRequestURI       string                 `json:\"RequestURI\"`\n}\n\n// Multipart corresponds to a multi-part file\ntype Multipart struct {\n\tFilename    string `json:\"Filename\"`\n\tContentType string `json:\"ContentType\"`\n\tContent     string `json:\"Content\"`\n}\n\n// URLInfo corresponds to a URL\ntype URLInfo struct {\n\tScheme   string `json:\"Scheme\"`\n\tOpaque   string `json:\"Opaque\"`\n\tHost     string `json:\"Host\"`\n\tPath     string `json:\"Path\"`\n\tRawQuery string `json:\"RawQuery\"`\n\tFragment string `json:\"Fragment\"`\n}\n```\n\nTanuki provides a `Params` field, which is a hash (or dictionary) with a string as the key and an array of strings as the value. The `Params` field is a convenience for Tanuki developers and contains all the parameters sent by the client, including those in the URL or in the case of a `POST`, also in the forms.\n\nThis is an example of the JSON for the HTTP request, a GET request to Tanuki at the URL `/_/hello/world`.\n\n```json\n{\n  \"Method\": \"GET\",\n  \"URL\": {\n    \"Scheme\": \"\",\n    \"Opaque\": \"\",\n    \"Host\": \"\",\n    \"Path\": \"/_/hello/world\",\n    \"RawQuery\": \"name=sausheong\",\n    \"Fragment\": \"\"\n  },\n  \"Proto\": \"HTTP/1.1\",\n  \"Header\": {\n    \"Accept\": [\n      \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"\n    ],\n    \"Accept-Encoding\": [\n      \"gzip, deflate\"\n    ],\n    \"Accept-Language\": [\n      \"en-sg\"\n    ],\n    \"Connection\": [\n      \"keep-alive\"\n    ],\n    \"Cookie\": [\n      \"hello=world; mykey=myvalue\"\n    ],\n    \"Upgrade-Insecure-Requests\": [\n      \"1\"\n    ],\n    \"User-Agent\": [\n      \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15\"\n    ]\n  },\n  \"Body\": \"\",\n  \"ContentLength\": 0,\n  \"TransferEncoding\": null,\n  \"Host\": \"localhost:8080\",\n  \"Params\": {\n    \"name\": [\n      \"sausheong\"\n    ]\n  },\n  \"Multipart\": {},\n  \"RemoteAddr\": \"[::1]:52340\",\n  \"RequestURI\": \"/_/hello/world?name=sausheong\"\n}\n```\n\nYou might notice here that if you want to get the `name` you can either parse the `RawQuery` or get `RequestURI` or you can simply get it from `Params`. \n\n### HTTP response\n\nThe only response that Tanuki accepts from the handlers is a HTTP response JSON. Here's the Go struct for the JSON.\n\n```go\n// ResponseInfo corresponds to a HTTP 1.1 response\ntype ResponseInfo struct {\n\tStatus int                 `json:\"status\"`\n\tHeader map[string][]string `json:\"header\"`\n\tBody   string              `json:\"body\"`\n}\n```\n\nHere's an example of the response JSON to be sent back to Tanuki.\n\n```json\n{\n  \"status\": 200,\n  \"header\": {\n    \"Content-Length\": [\"15\"],\n    \"Content-Type\": [\"text/plain; charset=utf-8\"]\n  },\n  \"body\": \"hello sausheong\"\n}\n```\n\n## Examples\n\nLet's look at some examples. They are pretty simple and almost trivial and does one thing. When the user browses the URL:\n\n```\nhttps://localhost:8081/_/hello/world?name=sausheong\n```\n\nThe handlers should return:\n\n```\nhello sausheong\n```\n\n### Bins\n\nBins are the simplest handler to write and can be written in any programming language than can parse JSON (actually since JSON is simply a string, it, just being able to manipulate strings is good enough) and take in a command line argument and print a JSON string to STDOUT. This is an example of a simple bin handler written in Ruby.\n\n```ruby\n#!/usr/bin/env ruby\nrequire 'json'\n\nrequest = JSON.parse ARGV[0]\nresponse = {\n    status: 200,\n    header: {},\n    body: \"hello #{request['Params']['name'][0]}\"\n}\nputs response.to_json\n```\n\nThe code above shows how the first command line argument to the script `ARGV[0]` is parsed into JSON, and the parameters are used in the body. The response is created first as a hash and converted into JSON before being printed out to STDOUT.\n\nHere's another, written in bash script. Just because you can doesn't mean you should write handlers in bash though.\n\n```bash\n#!/usr/bin/env bash\n\nname=$(echo $1|jq '.[\"Params\"][\"name\"][0]'|tr -d \\\")\ncat \u003c\u003c- _EOF_\n{\n    \"status\": 200, \n    \"header\": {}, \n    \"body\": \"hello $name\"\n}\n_EOF_\n```\n\nFor this I use the [`jq` tool](https://stedolan.github.io/jq/), a lightweight command line JSON processor. It parses the first command line argument `$1` and extracts the `name` from the `Params` field, which I then use to output back to STDOUT.\n\n### Listeners\n\nListeners are a bit more complicated to write but they work the same way as bins. As long as your programming language of choice is able to parse JSON, and can be used to create a TCP socket server, you're good to go.\n\nWhen a HTTP request is sent to Tanuki (from a browser or another application), Tanuki creates a TCP connection to the listener and sends in the JSON HTTP request. Important to remember that the JSON request will end with a newline `\\n` so when writing the listener you should use this to detect the end of the JSON string.\n\nIn the same way when you write your listener and construct a JSON HTTP response, you must end the JSON with a newline `\\n`.\n\nThe listener must return a JSON HTTP response as above through the same connection.\n\nThis is an example of a simple listener written in Python. The file name is `listeners/hello-python` and the listener will be triggered when a GET request is sent to the URL `/_/hello/python/listener?name=sausheong`\n\n```python\n#!/usr/bin/env python\n\nimport socket\nimport sys\nimport json\n\nHOST = '0.0.0.0'\nPORT = sys.argv[1]\n\nwith socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n    s.bind((HOST, int(PORT)))\n    s.listen()\n    while True:\n        conn, addr = s.accept()\n        with conn:\n            data = conn.recv(1024)\n            request = json.loads(data)\n            response = {\n                'status': 200,\n                'header': {},\n                'body': \"hello \" + request['Params']['name'][0]\n            }\n            conn.sendall(str.encode(json.dumps(response)+\"\\n\", 'utf-8' ))\n```\n\n## Developing and testing for handlers\n\nDeveloping and testing handlers is pretty much like developing and testing standalone command line programs or TCP socket servers. To test the handlers there are different techniques.\n\nFor bins, you can call the handler directly, passing in the JSON request as the only argument and it should produce a JSON response to STDOUT. For listeners, you can cal the handler directly again and passing in the port number. To test the listener, you can telnet to the listener at the port number and pass in the JSON request as input. It should return a JSON response.\n\nI've created a couple of tools to help in developing and testing handlers.\n\n### Capture tool\n\nThe capture tool helps to capture a normal HTTP request into a JSON request to be used as test input into the handlers. This tool is basically a HTTP server that you send a HTTP request to and it will save the request to a json file. \n\nTo start the capture server, do this at the command line:\n\n```\n./tanuki capture\n```\n\nThis will start a HTTP server at 8088. You can change the port with the different capture options.\n\nNow you can use a browser, enter the URL `http://localhost:8088//hello/ruby?name=sausheong` to send a HTTP GET request to the capture server. The capture server will then create a `request.json` file from this request that looks like this:\n\n```json\n{\n    \"Method\": \"GET\",\n    \"URL\": {\n        \"Scheme\": \"\",\n        \"Opaque\": \"\",\n        \"Host\": \"\",\n        \"Path\": \"/hello/ruby\",\n        \"RawQuery\": \"name=sausheong\",\n        \"Fragment\": \"\"\n    },\n    \"Proto\": \"HTTP/1.1\",\n    \"Header\": {\n        \"Accept\": [\n            \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"\n        ],\n        \"Accept-Encoding\": [\n            \"gzip, deflate\"\n        ],\n        \"Accept-Language\": [\n            \"en-sg\"\n        ],\n        \"Connection\": [\n            \"keep-alive\"\n        ],\n        \"Upgrade-Insecure-Requests\": [\n            \"1\"\n        ],\n        \"User-Agent\": [\n            \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15\"\n        ]\n    },\n    \"Body\": \"\",\n    \"ContentLength\": 0,\n    \"TransferEncoding\": null,\n    \"Host\": \"localhost:8088\",\n    \"Params\": {\n        \"name\": [\n            \"sausheong\"\n        ]\n    },\n    \"Multipart\": {},\n    \"RemoteAddr\": \"[::1]:56629\",\n    \"RequestURI\": \"/hello/ruby?name=sausheong\"\n}\n```\n\nTo use the contents of this `request.json` file as the argument to your bin handler, do this:\n\n```\nhandlers/hello-ruby \"$(cat request.json)\"\n```\n\nTo use it with a listener, you can use telnet, copy and paste the JSON to send to listener.\n\nAlternatively you can also use the Tanuki send tool.\n\n### Send tool\n\nThe Tanuki send tool can be used to send input from a request JSON file to either a bin or listener. \n\nTo use it with a bin:\n\n```\n./tanuki send --file handlers/hello-ruby\n```\n\n![send-bin](images/tanuki-send-success.png)\n\nThe tool also helps you to parse the JSON response and make sure it's valid. If it's not, it will show and error:\n\n![send-bin-error](images/tanuki-send-error.png)\n\nFor listeners, you need to start the listener first:\n\n```\nhandlers/hello-ruby-listener 55771\n```\n\nThis starts the listener normally as a TCP socket server that listens at port 55771.\n\nTo use the send tool with this listener:\n\n```\n./tanuki send --listen 55771\n```\n\n![send-listener](images/tanuki-send-listener.png)\n\nThis will send the JSON request to the listener at port 55771. You'd notice that the output is the same. If you have any errors you'll see the same error message as with the bin handler.\n\n## Why Tanuki?\n\nIt seems a bit of work to write a single web app in different programming languages, so why do it? It's really all about future-proofing.\n\n### End programming wars\n\nIn any team with multiple capabilities, it's inevitable there's always going to be some programming language or library or tool war on which  technology to use. With Tanuki, you can opt for _all of the above_ since you can (if you want) write every handler in a different programming language or use different libraries or even different versions.\n\nWhy you would want to do that is to really tap on the different strengths of the various languages and libraries as needed.\n\n### Get out of dependency hell\n\nMost professional developers have one time or another in their careers needed to take over _legacy_ projects and code. Sometimes it could even be your own legacy code! For example if you have written a large web application using version 2.1 of a web framework a few years ago and now you need to upgrade to version 5.2 this could be pretty painful. Every library you used then would have been new versions and some of them would be incompatible with each other. Very often it's a multi-month or even year project to upgrade the whole application.\n\nWith Tanuki you can easily upgrade different parts of the same application over time. And because each handler is a separate piece of software on its own, you can opt to keep using the different versions of the libraries in them!\n\n### Modular replacement\n\nIf the project was written in a programming language or uses a library or tool you don't know, you probably have to spent lots of time learning the new technologies, or try to re-write the whole thing using your own technology stack.\n\nHowever, if the web application was written in Tanuki, you can opt to switch out parts of the project to change, and add new features in your own stack and keep the ones that don't need to change intact!\n\n### Easy testing\n\nBecause each handler has standard input and output in a fixed format, testing the handlers can be pretty independent. In fact you can do test-driven development by writing the tests first and start writing the handlers until they all pass. You can also write the tests in one language and test the handlers written in other languages!\n\n### Independent development\n\nIf you are building a large application or service and a number of developers are working on it at the same time is that you can farm out handlers to different people to write in different technology stacks, as long as they pass the tests! You can even deploy them on different machines as long as they are reachable by Tanuki.\n\n### Distributed handling\n\nTanuki supports distributed handling and you can deploy your remote listener handlers to different servers, as long as the remote listener is reachable by Tanuki. This will help in scaling out, especially for resource intensive handlers.\n\n## What's the trade-off?\n\nYou can't win 'em all. There's always a trade-off somewhere. Whether the trade-off is worth it depends very much on the web application or service itself. Here are some draw-backs of using Tanuki (besides the point that this is still an :warning: _experimental_ :warning: software!).\n\n### Worse performance\n\nPerformance is definitely affected. In the case of bins, every time the action is called, the script is called to execute and return. Listeners are better because they are already running TCP servers, but the effort to create a socket connection and send the request JSON across has overhead. Things can get progressively worst if the listener is remote.\n\nNonetheless, if the processing done is significant, the percentage of the overhead could be small in comparison.\n\n### No sharing of code or in-memory data\n\nSince every handler (bin or listener) is a separate process, neither code nor data can be shared between them. To share data, you will need an intermediate store, for e.g. a database or a key-value store etc. To share code, one way of getting around it to is to use libraries. \n\n### Added complexities\n\nBecause the different handlers need to totally independent, there are additional overheads and complexities. For example, you need to parse the JSON request yourself and craft a JSON response almost in the raw. Besides adding to processing needs (and therefore lower performance) it also adds complexities in the code. And more complex code is overall harder to maintain.\n\nOn the other hand, some of the complexities can be grouped into reusable libraries, which helps to reduce the overheads.\n\n### Not the same with other web frameworks\n\nIf you're familiar with other web frameworks, many of them work the same way. Tanuki is quite different from other frameworks because of it the way it works. You might even argue that calling it a web framework is a bit of a stretch but I would beg to differ, from [this definition from Wikipedia](https://en.wikipedia.org/wiki/Web_framework).\n\n\u003e A web framework (WF) or web application framework (WAF) is a software framework that is designed to support the development of web applications including web services, web resources, and web APIs. Web frameworks provide a standard way to build and deploy web applications on the World Wide Web. Web frameworks aim to automate the overhead associated with common activities performed in web development. \n\nNonetheless because it works differently, first time developers on Tanuki would find a steeper learning curve. For example, if you're used to having a authentication and authorization drop-in library for other web frameworks, in Tanuki you might need to build that in yourself. To do this you might need to set and get the cookies to keep the state when you go from action to action.\n\n### Operations and monitoring complexities\n\nIt's obvious that monitoring and operating a single process will be easier than monitoring and operating multiple processes, which potentially can be distributed across different parts of the network. Complexity increases for managing code and for deployment as well.\n\nOn the other hand, in a sizable web app, you're already likely to be deploying micro-services and already have a structure for managing these services. Extending them to Tanuki remote handlers should add relatively little overhead. In any case this is primarily required for remote listeners only and you have an option to start off the web app using only bins, then local listeners and only start using remote listeners to scale up.\n\n## FAQs\n\n### I'm still skeptical, why spend the effort?\n\nOf course. Tanuki is not for every web application or service, and there is additional effort to do something that could be relatively simple to write. The real benefits of Tanuki comes with over the lifespan of the software. If you're planning to write something that needs to be maintained over a period of time by different developers or groups of developers, Tanuki can be a good choice (because of the reasons given above).\n\n### Isn't this the same as ...?\n\nYes, Tanuki draws inspiration from a number of previous technologies.\n\nTanuki is partially inspired by [CGI](https://en.wikipedia.org/wiki/Common_Gateway_Interface) but there are some significant differences. In CGI, data to the CGI scripts are passed using environment variables, while in Tanuki it is sent into the bin through the command line argument as a JSON request. The Tanuki JSON request is also much more closer to the actual HTTP request.\n\nTanuki uses TCP socket servers as listeners to reduce the overhead of creating one process per requeset. This method is very similar to that of [FastCGI](https://en.wikipedia.org/wiki/FastCGI) but has a much simpler implementation. Also for FastCGI, a web application or service is built using a single programming language only. While FastCGI APIs exist for multiple languages, the intent was never to write one web application or service using more than one.\n\nTanuki is also inspired by [WSGI](https://www.python.org/dev/peps/pep-0333/), a calling convention used to forward HTTP requests to web applications or frameworks written in Python. However as with CGI and FastCGI, WSGI is meant for a single programming language only (in this case Python, though other programming languages have similar calling conventions). \n\nSo for the implementations the mechanisms are close but not the same, and none are meant for multiple programming languages like Tanuki.\n\n### This sounds suspiciously like micro-services\n\nAbsolutely. Many of the benefits and issues with micro-services are the same with Tanuki because fundamentally the mechanisms are very similiar. However, Tanuki is meant to be used to build a single web application or service whereas micro-services are standalone and independent and performs a specialised service. Tanuki can use micro-services though -- the actions themselves can call micro-services to do the work. \n\n### You were working on Polyglot earlier, what happened to it?\n\n[Polyglot](https://github.com/sausheong/polyglot) was a previous incarnation of the same idea that I've been mulling over for many years and Tanuki is the latest (there were others in between but not released). There are several differences between Polyglot and Tanuki. Polyglot uses a fast message queue (ZeroMQ) as an intermediary to the handlers. This provides flexibility and scalability but it also adds quite a bit complexity. Because of the message queue, testing and debugging handlers can be difficult. Also, to create handlers the programming language needs to have the libraries to talk to the message queue. Also, securing the message queue can be quite a task because of the distributed nature of a Polyglot web application.\n\nIn designing Tanuki, I've deliberately moved away from any message queues to reduce the implementation complexity.  In Tanuki, the handlers are simple programs that are either command line binaries or scripts, or TCP socket servers. This reduces the complexity in creating handlers and also reduces requirements to the most bare-bone -- as long as the language can parse a string, you can write a handler!\n\nNonetheless, there were good lessons learnt from my experience in Polyglot. However, I've stopped working on Polyglot already.\n\n### Who is using this in production?\n\nNo-one yet. :warning: This is still _experimental_ software and not production ready. :warning: If you're trying it out, would appreciate feedback and even better yet, pull requests to contribute!\n\n## Credits\n\nThanks to Jasmine Lim for the seriously cute mascot!\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsausheong%2Ftanuki","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsausheong%2Ftanuki","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsausheong%2Ftanuki/lists"}