{"id":16402902,"url":"https://github.com/suhay/sandwich-shop","last_synced_at":"2026-02-26T02:02:23.817Z","repository":{"id":45913868,"uuid":"209562710","full_name":"suhay/sandwich-shop","owner":"suhay","description":"Containerless, Serverless experiment using Go and GraphQL","archived":false,"fork":false,"pushed_at":"2023-03-07T03:28:23.000Z","size":142,"stargazers_count":1,"open_issues_count":9,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-14T03:42:23.057Z","etag":null,"topics":["golang","hacktoberfest","serverless"],"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/suhay.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-09-19T13:34:32.000Z","updated_at":"2021-12-13T02:20:56.000Z","dependencies_parsed_at":"2024-06-20T00:04:44.605Z","dependency_job_id":"454a911e-7d17-4c54-908b-1577f3c364fe","html_url":"https://github.com/suhay/sandwich-shop","commit_stats":{"total_commits":23,"total_committers":2,"mean_commits":11.5,"dds":0.08695652173913049,"last_synced_commit":"d0e43ad673145df6e461e00c8695404d7e922fcc"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suhay%2Fsandwich-shop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suhay%2Fsandwich-shop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suhay%2Fsandwich-shop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suhay%2Fsandwich-shop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/suhay","download_url":"https://codeload.github.com/suhay/sandwich-shop/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247779760,"owners_count":20994572,"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":["golang","hacktoberfest","serverless"],"created_at":"2024-10-11T05:47:35.419Z","updated_at":"2026-02-26T02:02:23.607Z","avatar_url":"https://github.com/suhay.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sandwich Shop\n## A containerless, serverless experiment using Go and GraphQL\n\n*Note! This is purely experimental and not meant for production use.*\n\nThe purpose of this experiment is to explore other alternatives to the container-based, serverless pattern. We are hoping this solution will minimize the overhead needed for spinning up a new container on-demand and fully eliminate the idea of a \"cold start”.\n\n## Usage\n\n### Build\n\n```bash\ngh repo clone suhay/sandwich-shop\ncd sandwich-shop\nmake build\n```\n\nThis will build directly from the source code. The compiled binary will be within the project directory. One benefit of doing it this way is the `.env` file path will default to the project's root. This means you will not need to specify it as an argument.\n\n### Install\n\n```bash\ngo install github.com/suhay/sandwich-shop@latest\ngo install github.com/suhay/sandwich-shop/sandwiches/gowich@latest\n```\n\nInstall the latest release of Sandwich Shop. This will add the binary to your `GOPATH`. The benefit of installing vs building is being able to run the binary within any directory. You will, however, need to specify a path to the `.env` file you want to use as an argument. Also, be sure to add `$GOPATH/bin` to your `PATH`,\n\n### Run\n\n```bash\nsandwich-shop [--env=\u003cpath\u003e] [--port=\u003cport\u003e]\n```\n\nIf you built the application from the source, you will need to execute the binary within the project directory.\n\n| Flag | Description |\n| --- | --- |\n| --env | Path to a local, or remote .env this process should use for default configurations. |\n| --port | The port number to run on. Default: 3002 |\n\n## Setting up Shop\n\nOnce you have the Sandwich Shop installed, and you have at least one Sandwich ready to go (`gowich` or `nodewich`), the next task is to set up your `.env` files. These can live anywhere and are included as CLI arguments when launching the Shop or Sandwich.\n\n```bash\n# .env\n\nMONGODB_URL=cluster0.mongodb.net\nMONGODB_USER=mango\nMONGODB_PASSWD=1234password\nJWT_SECRET=1234567890jwtsecretcode\nTIMEOUT=60\nTENANTS=~/sandwich-shop/tenants\n```\n\n| Key | Description |\n| --- | --- |\n| MONGODB_URL | (optional) URL to the mongo database that stores tenant information including the Bearer token. |\n| MONGODB_USER | (optional) User for logging into the tenant info database. |\n| MONGODB_PASSWD | (optional) Password for the user above. |\n| JWT_SECRET | Used for signing a JWT payload while passing it through the shop as an order. This is used to verify the order came through a shop directly and that it was unchanged while in transit. This secret must be identical across all Sandwiches. |\n| TIMEOUT | Seconds to wait on the order to complete. |\n| TENANTS | The location where your shop tenants are stored. Think of these as project roots, function roots, or user roots of what will be using the Shop. |\n\n### Registering a Sandwich\n\nA Sandwich is what will handle the heavy lifting. Each Sandwich can be specialized in running one type of application (Node.js, Golang, Python, etc.), or they can do a mix of them, whichever you'd prefer. There are two ways you can register your Sandwiches. The first way is through a local `sandiwches.json` file. This is the more clunky way to maintain your Sandwich registry, but it cuts out a round trip to a MongoDB. The second way is through a MongoDB collection.\n\n```json\n[\n  {\n    \"_id\": \"rye\",\n    \"name\": \"Rye\",\n    \"host\": \"http://localhost\",\n    \"port\": 4006,\n    \"runtimes\": [\n      \"node12\"\n    ]\n  },\n  {\n    \"_id\": \"ciabatta\",\n    \"name\": \"Ciabatta\",\n    \"host\": \"http://localhost\",\n    \"port\": 4007,\n    \"runtimes\": [\n      \"go1_17\",\n      \"node14\",\n      \"node16\",\n      \"python3\"\n    ]\n  }\n]\n```\n\n| Key | Description |\n| --- | --- |\n| _id | Sandwich's id |\n| name | Human readable names for easily identifying when there is an error. |\n| host | URL the Sandwich will be reached by the Sandwich Shop process (how it will receive orders). |\n| port | The port on the designated host the Sandwich is reachable on |\n| runtimes | An array of runtime identifiers that show what this Sandwich is set to run. |\n\nRuntimes must be defined within the GraphQL resolver which means adding one will require a pull request. The above schema can also be stored within a MongoDB collection called `sandwiches` on the `sandwich-shop` database you are using to run this application.\n\n### Gowich\n\nA `Gowich` is a Sandwich written in Go. You may have as many of these running as needed as long as they are all running on separate ports. Each `Gowich` should have a `.env` file supplied to it as a CLI argument.\n\n```bash\n# .gowich1.env\n\nPORT=3001\nTIMEOUT=60\nJWT_SECRET=1234567890jwtsecretcode\nGO1_13=/usr/local/go/bin/go\nTENANTS=/path/to/tenants\n```\n\n| Key | Description |\n| --- | --- |\n| PORT | Port to run this Sandwich on. This will be overridden if --port is supplied as a command argument. |\n| TIMEOUT | Timeout, in seconds, to wait for an order to finish. |\n| JWT_SECRET | Must be the same JWT_SECRET as specified in the Sandwich Shop’s .env variables. This is used to ensure the request was generated and not changed between Sandwich Shop and the Gowich. |\n| GO1_13 | Absolute path to the runtime’s executable. |\n| TENANTS | Absolute path to tenants directory. |\n\n### Running\n\n```bash\ngowich [--env=\u003cpath\u003e] [--port=\u003cport\u003e]\n```\n\n| Flag | Description |\n| --- | --- |\n| --env | Path to a local, or remote .env this process should use for default configurations. |\n| --port | The port number to run on. |\n\n### Nodewich\n\nA `Nodewich` is a Sandwich written in Node. You may have as many of these running as needed as long as they are all running on separate ports. Each `Nodewich` should have a `.env` file supplied to it as a CLI argument.\n\n```bash\n#.nodewich1.env\n\nPORT=4001\nTIMEOUT=60\nJWT_SECRET=1234567890jwtsecretcode\nNODE9_10=/path/to/node9\nNODE12_7=/path/to/node12\nTENANTS=/path/to/tenants\n```\n\n| Key | Description |\n| --- | --- |\n| PORT | Port to run this Sandwich on. This will be overridden if --port is supplied as a command argument. |\n| TIMEOUT | Timeout, in seconds, to wait for an order to finish. |\n| JWT_SECRET | Must be the same JWT_SECRET as specified in the Sandwich Shop’s .env variables. This is used to ensure the request was generated and not changed between Sandwich Shop and the Nodewich. |\n| NODE9_10 | Absolute path to the runtime’s executable. |\n| TENANTS | Absolute path to tenants directory. |\n\n### Running\n\n```bash\nnodewich [--env=\u003cpath\u003e] [--port=\u003cport\u003e]\n```\n\n| Flag | Description |\n| --- | --- |\n| --env | Path to a local, or remote .env this process should use for default configurations. |\n| --port | The port number to run on. |\n\n### Tenants\n\nTenants are the buckets where your functional code is placed. Within the `tenant` directory, you will place the different tenant directories. Along with the functions a tenant wishes to run, there also must be an `orders.yml` file to hold the function configurations. There can also either be an optional `.key` file to hold the API key for this particular tenant, or their API key must be stored within the `tenants` collection on your `sandwich-shop` MongoDB database.\n\n```bash\ntenants\n└── b78682b3-36c8-4759-b8d1-5e62f029a1bc\n    ├── .key (optional)\n    ├── getSandwich.js\n    ├── make_sandwich.go\n    └── orders.yml\n```\n\n```yaml\n# order.yml\n---\ngetSandwich: # order name, GET https://shop.example/{tenantid}/getSandwich  \n  runtime: node9_10 # runtime needed, used to pick sandwich \n  path: getSandwich.js # path function is relative to tenant's root  \n  env: [] # any variables to include\nmakeSandwich:  \n  runtime: go1_13\n  path: make_sandwich.go\n  env: []\n```\n\n`.key` file\n\n```\nthis-is-my-api-key\n```\n\n**OR**\n\n`sandwich-shop.tenants` in your MongoDB cluster\n\n```json\n{\n  \"_id\": \"tenant id\",\n  \"key\": \"this-is-my-api-key\"\n}\n```\n\n## TL;DR - `sudo make me a gowich`\n\n```bash\nmkdir ~/sandwich-shop\ngo install github.com/suhay/sandwich-shop@latest\ngo install github.com/suhay/sandwich-shop/sandwiches/gowich@latest\n\ncd ~/sandwich-shop\nprintf \"JWT_SECRET=1234567890jwtsecretcode\\nTENANTS=~/sandwich-shop\" \u003e .env\nprintf \"JWT_SECRET=1234567890jwtsecretcode\\nGO1_17=/usr/local/go/bin/go\" \u003e .node1.env\necho '[{\"_id\": \"ciabatta\", \"name\": \"Ciabatta\", \"host\": \"http://127.0.0.1\", \"port\": 4007, \"runtimes\": [\"go1_17\"]}]' \u003e sandwiches.json\nmkdir myFunctions\n\ncd myFunctions\necho \"password1234\" \u003e .key\nprintf -- '---\\nhello:\\n  runtime: go1_17\\n  path: hello.go' \u003e orders.yml\nprintf 'package main\\nimport \"fmt\"\\nfunc main() {\\nfmt.Printf(\"%%s\", \"Hello!\")\\n}' \u003e hello.go\n```\n\n### Run\n\n```\nsandwich-shop --env ~/sandwich-shop/.env \u0026\ngowich --env ~/sandwich-shop/.node1.env --port 4007 \u0026\n```\n\n### Use\n\n```bash\ncurl -X POST --header \"Authorization: Bearer password1234\" http://localhost:3002/shop/myFunctions/hello\n```\n\n## Current Development\n\n- [x]  ~~Move tenant path into an environment variable~~\n- [ ]  CLI for adding `tenants`, standing up and tearing down Sandwiches, and changing `.key` values.\n- [ ]  A+B testing against a Serverless pattern (to see if this is even a thing or just something for fun)\n- [ ]  Security audit on tenants\n\n### Developing for Sandwich Shop\n\n```\nmake dev\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuhay%2Fsandwich-shop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsuhay%2Fsandwich-shop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuhay%2Fsandwich-shop/lists"}