{"id":44338751,"url":"https://github.com/initialed85/frinkconv-api","last_synced_at":"2026-02-11T12:18:15.535Z","repository":{"id":56359032,"uuid":"523450213","full_name":"initialed85/frinkconv-api","owner":"initialed85","description":"A JSON HTTP API for converting a value from one unit of measurement to another","archived":false,"fork":false,"pushed_at":"2022-08-16T14:30:44.000Z","size":3464,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-14T02:51:50.472Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/initialed85.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}},"created_at":"2022-08-10T18:13:22.000Z","updated_at":"2022-08-11T11:14:16.000Z","dependencies_parsed_at":"2022-08-15T17:20:12.706Z","dependency_job_id":null,"html_url":"https://github.com/initialed85/frinkconv-api","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/initialed85/frinkconv-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/initialed85%2Ffrinkconv-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/initialed85%2Ffrinkconv-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/initialed85%2Ffrinkconv-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/initialed85%2Ffrinkconv-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/initialed85","download_url":"https://codeload.github.com/initialed85/frinkconv-api/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/initialed85%2Ffrinkconv-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29332792,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T06:13:03.264Z","status":"ssl_error","status_checked_at":"2026-02-11T06:12:55.843Z","response_time":97,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2026-02-11T12:18:14.676Z","updated_at":"2026-02-11T12:18:15.520Z","avatar_url":"https://github.com/initialed85.png","language":"Go","readme":"# frinkconv-api\n\n`frinkconv-api` provides a JSON HTTP API for converting a value from one unit of measurement to another.\n\nUnder the hood, it uses [frinkconv](https://github.com/seanbreckenridge/frinkconv) which is a nice CLI tool\naround [Frink](https://frinklang.org/) for unit of measurement conversions.\n\nIt works by spawning a configurable number of long-running `frinkconv` REPL processes at startup time, incoming HTTP requests will then pick\nan available REPL from a pool (or block if none are available) with which to execute the conversion.\n\nIn my testing, it seems to be computationally fairly lightweight but there is a memory cost to running more REPL processes.\n\nPer-request performance is not too bad; in all cases using my 2019 MacBook Pro (x86) to test:\n\n- Docker\n    - `1000 batch requests of 10 conversions each in 11.466923209s; 87.20735124615936 requests per second, 872.0735124615936 conversions per second`\n- Native\n    - `1000 batch requests of 10 conversions each in 4.12123353s; 242.6457983321319 requests per second, 2426.4579833213193 conversions per second`\n\nAs you'd expect, Docker for Mac is slower due to the virtualization; I daresay there's little to no cost for a Linux-based machine.\n\n## TODO\n\n- Functionality\n    - Add support for \"Multiple Conversions\" ([reference](https://frinklang.org/#Conversions)); basically the process of extracting the\n      destination value as a tuple\n- Bugs / tech debt\n    - Find a way to pin the version of `frinkconv` that's downloaded at Docker build time\n    - Validate the given `source_units` and `destination_units` before attempting the conversion\n        - _Possibly_ some not-yet-fully-understood risk of code injection without this\n    - Probably lurking bugs in some of the REPL output parsing I'm doing\n    - Look at a closer / direct integration with Frink (skip the REPL)\n        - Speculated that this may be faster / less resource heavy\n\n## Usage\n\n### Interact with the API as it is deployed on [Fly.io](https://fly.io/)\n\nAttempt a single conversion:\n\n```shell\ncurl -s -X POST -d '{\"source_value\": 120.0, \"source_units\": \"feet\", \"destination_units\": \"metres\"}' https://frinkconv-api.fly.dev/convert/ | jq\n{\n  \"destination_value\": 36.576\n}\n```\n\nAttempt a batch conversion:\n\n```shell\ncurl -s -X POST -d '[{\"source_value\": 120.0, \"source_units\": \"feet\", \"destination_units\": \"metres\"}, {\"source_value\": 120.0, \"source_units\": \"feet\", \"destination_units\": \"metres\"}]' https://frinkconv-api.fly.dev/batch_convert/ | jq\n[\n  {\n    \"destination_value\": 36.576\n  },\n  {\n    \"destination_value\": 36.576\n  }\n]\n```\n\n### Run using published Docker image\n\n**Prerequisites**\n\n- [Docker](https://www.docker.com/)\n\n**Run the server**\n\n```shell\ndocker run --rm -it -p 8080:8080 initialed85/frinkconv-api\n```\n\n### Build and run using Docker Compose\n\n**Prerequisites**\n\n- [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/)\n\n**Build and run the server**\n\n```shell\ndocker compose up --build\n```\n\n### Build and run natively\n\n**Prerequisites**\n\n- [Go 1.17](https://go.dev/)\n\n**Build the server**\n\n```shell\ngo build -o bin/frinkconv-api cmd/main.go\n```\n\n**Run the server**\n\n```shell\nbin/frinkconv-api -port 8080 -processes 4\n```\n\n## Interactions via the API\n\n**Prerequisites**\n\n- [Curl](https://github.com/curl/curl)\n- [jq](https://github.com/stedolan/jq)\n\n**Attempt some single conversions**\n\n```shell\n# this should succeed\ncurl -s -X POST -d '{\"source_value\": 120.0, \"source_units\": \"feet\", \"destination_units\": \"metres\"}' http://localhost:8080/convert/ | jq\n{\n  \"destination_value\": 36.576\n}\n\n# this should fail\ncurl -s -X POST -d '{\"source_value\": 120.0, \"source_units\": \"apples\", \"destination_units\": \"oranges\"}' http://localhost:8080/convert/ | jq\n{\n  \"error\": \"Warning: undefined symbol \\\"apples\\\".\\nUnknown symbol \\\"oranges\\\"\\nWarning: undefined symbol \\\"apples\\\".\\nWarning: undefined symbol \\\"oranges\\\".\\nUnconvertable expression:\\n  120 apples (undefined symbol) -\u003e oranges (undefined symbol)\"\n}\n```\n\n**Attempt a batch conversion**\n\n```shell\n# this should both succeed and fail, results are index mapped\ncurl -s -X POST -d '[{\"source_value\": 120.0, \"source_units\": \"feet\", \"destination_units\": \"metres\"}, {\"source_value\": 120.0, \"source_units\": \"apples\", \"destination_units\": \"oranges\"}, {\"source_value\": 120.0, \"source_units\": \"feet\", \"destination_units\": \"metres\"}, {\"source_value\": 120.0, \"source_units\": \"apples\", \"destination_units\": \"oranges\"}]' http://localhost:8080/batch_convert/ | jq\n[\n  {\n    \"destination_value\": 36.576\n  },\n  {\n    \"error\": \"Warning: undefined symbol \\\"apples\\\".\\nUnknown symbol \\\"oranges\\\"\\nWarning: undefined symbol \\\"apples\\\".\\nWarning: undefined symbol \\\"oranges\\\".\\nUnconvertable expression:\\n  120 apples (undefined symbol) -\u003e oranges (undefined symbol)\"\n  },\n  {\n    \"destination_value\": 36.576\n  },\n  {\n    \"error\": \"Warning: undefined symbol \\\"apples\\\".\\nUnknown symbol \\\"oranges\\\"\\nWarning: undefined symbol \\\"apples\\\".\\nWarning: undefined symbol \\\"oranges\\\".\\nUnconvertable expression:\\n  120 apples (undefined symbol) -\u003e oranges (undefined symbol)\"\n  }\n]\n```\n\n## Ghetto performance testing\n\n**Assuming you already have a `frinkconv_api` server running on port 8080:\n\n```shell\ngo run test/main.go\n2022/08/11 10:47:16 1000 batch requests of 10 conversions each in 3.970077757s; 251.88423532431082 requests per second, 2518.8423532431084 conversions per second\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finitialed85%2Ffrinkconv-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finitialed85%2Ffrinkconv-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finitialed85%2Ffrinkconv-api/lists"}