{"id":20698001,"url":"https://github.com/fly-apps/js-sqlite-in-tigris","last_synced_at":"2025-03-11T03:33:39.238Z","repository":{"id":235478529,"uuid":"777682102","full_name":"fly-apps/js-sqlite-in-tigris","owner":"fly-apps","description":"Demo with SQLite DB stored in a Tigris bucket","archived":false,"fork":false,"pushed_at":"2024-04-18T11:54:48.000Z","size":260,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-01-17T18:54:34.008Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/fly-apps.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}},"created_at":"2024-03-26T10:14:48.000Z","updated_at":"2024-12-16T11:04:27.000Z","dependencies_parsed_at":"2024-04-23T15:28:23.427Z","dependency_job_id":"4825741d-b691-4401-8866-9dd563137178","html_url":"https://github.com/fly-apps/js-sqlite-in-tigris","commit_stats":null,"previous_names":["fly-apps/js-sqlite-in-tigris"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fly-apps%2Fjs-sqlite-in-tigris","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fly-apps%2Fjs-sqlite-in-tigris/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fly-apps%2Fjs-sqlite-in-tigris/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fly-apps%2Fjs-sqlite-in-tigris/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fly-apps","download_url":"https://codeload.github.com/fly-apps/js-sqlite-in-tigris/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242967656,"owners_count":20214280,"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":[],"created_at":"2024-11-17T00:21:33.868Z","updated_at":"2025-03-11T03:33:39.213Z","avatar_url":"https://github.com/fly-apps.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Multi-tenant apps with single-tenant SQLite databases in global Tigris buckets\n\nSee the accompanying [blog post](https://fly.io/javascript-journal/single-tenant-sqlite-in-tigris/).\n\nThis is an example Vanilla JS app that demonstrates a multi-tenant application built on top of single-tenant SQLite databases, running on single-tenant Fly Machines and stored in globally distributed Tigris. It also showcases how to leverage the Dynamic Request Routing (the `fly-replay` HTTP header) in conjunction with Machines metadata to route the traffic between Machines.\n\nThe following sections show how to:\n* set up Tigris,\n* run the app locally,\n* deploy and run the app to Fly.io,\n* query DNS to find Machines by the Metadata.\n\nTo get started, clone this repository.\n\n- [Multi-tenant apps with single-tenant SQLite databases in global Tigris buckets](#multi-tenant-apps-with-single-tenant-sqlite-databases-in-global-tigris-buckets)\n  - [Tigris setup](#tigris-setup)\n  - [Running locally](#running-locally)\n  - [Deploying to Fly.io](#deploying-to-flyio)\n  - [(Extra) Metadata-based DNS queries](#extra-metadata-based-dns-queries)\n\n## Tigris setup\n\nIssue `fly storage create` to create a Tigris project. This will output a bunch of environment variables that have to be available in the current shell and then at Fly Machines to run the app locally and at Fly.io respectively.\n\n\u003e This command will prompt you to log in or sign up to Fly.io\n\nTo run the app locally, either export the variables in the shell you will be running the app\n\n```shell\nexport AWS_ACCESS_KEY_ID=\u003cyou-access-key-id\u003e\nexport AWS_ENDPOINT_URL_S3=https://fly.storage.tigris.dev\nexport AWS_REGION=auto\nexport AWS_SECRET_ACCESS_KEY=\u003cyour-secret-access-key\u003e\nexport BUCKET_NAME=\u003cyour-bucket-name\u003e\n```\n\nor install a tool like [dotenv](https://direnv.net/) and put the exports into a `.envrc` file. `dotenv` will make the variables available for the shell commands run inside the current directory.\n\n## Running locally\n\n\n\n1. Install dependencies and start the server: `npm install \u0026\u0026 CUSTOMER_ID=100 npm run start`\n3. Visit the URL http://localhost:3000/customers/100 and note the counter incrementing on a page refresh\n4. Visit a URL for an \"unknown customer\" (e.g: http://localhost:3000/customers/200) and note the `fly-replay` header in the response\n   ![](./doc/fly_replay.png)\n   * It contains a fabricated `machineId` (`\"abcd1234\"`), but when deployed to Fly.io it will become an actual machine ID to route the request to\n   * To learn more check the corresponding section in the [blog post](https://fly.io/javascript-journal/single-tenant-sqlite-in-tigris/) or the docs on the [Dynamic Request Routing](https://fly.io/docs/networking/dynamic-request-routing/#)\n5. Shut down the app (e.g. with `Ctrl+C`), remove the DB file (`rm db.sqlite3`) and start it again - note the counter picked up where it left off\n   * the logs should indicate that we sent the database file to Tigris and then fetched it on the shutdown and startup respectively\n\n\n## Deploying to Fly.io\n\n1. Set up the app name in the [`fly.toml`](./fly.toml): `app = '\u003capp-name\u003e'`.\n2. Create the app at Fly.io: `fly apps create \u003capp-name\u003e` (from now on all the `fly ...` commands will be using the `\u003capp-name\u003e` from the `fly.toml` file).\n3. Allocate IPv4 for the app: `fly ips allocate-v4 --shared`.\n4. Set up the app secrets:\n    ```\n    fly secrets set AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID\n    fly secrets set AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY\n    ```\n5. Run machines for customers `100`, `200`, and `300`:\n    ```shell\n    for i in `seq 1 3`; do\n        fly machine run . --name customer${i}00 --port 443:3000/tcp:http:tls \\\n        --env CUSTOMER_ID=${i}00 \\\n        --env AWS_REGION=$AWS_REGION \\\n        --env AWS_ENDPOINT_URL_S3=$AWS_ENDPOINT_URL_S3 \\\n        --env BUCKET_NAME=$BUCKET_NAME \\\n        --metadata customer_id=${i}00\n    done\n    ```\n6. Visit the customers' pages while observing the logs with `fly logs`\n  * `https://\u003capp-name\u003e.fly.dev/customers/100`\n  * `https://\u003capp-name\u003e.fly.dev/customers/200`\n  * `https://\u003capp-name\u003e.fly.dev/customers/300`\n\n    Note that some requests are _replayed_ to their corresponding machines. Refer to the [blog post](https://fly.io/javascript-journal/single-tenant-sqlite-in-tigris/) for more details.\n\n7. Destroy the machines\n   \u003e The following snippet uses [jq](https://jqlang.github.io/jq/)\n    ```shell\n    for m in `fly machines list -j | jq -r '.[].id'`; do\n      fly machines destroy -f $m;\n    done\n    ```\n\n## (Extra) Metadata-based DNS queries\n\n\u003e This section demonstrates how to get a Machine ID based on its `CUSTOMER_ID` metadata value that is set on a Machine start (with the `--metadata`). This mechanism is used in the app to route the requests to the corresponding Machines (e.g.: `https://\u003capp-name\u003e.fly.dev/customers/300` should be routed to the customer `300` Machine). Refer to the [blog post](https://fly.io/javascript-journal/single-tenant-sqlite-in-tigris/) for more details.\n\n1. Grab one of the Machine's shells with `fly ssh console`.\n2. Get a DNS AAAA record of the customer `300` Machine:\n\n    ```\n    dig aaaa +short 300.customer_id.kv._metadata.jts.internal\n    # example response: fdaa:6:32a:a7b:23c8:3d9b:fe49:2\n    ```\n3. Get a Machine ID with a reverse DNS lookup:\n\n    ```\n    dig +short -x fdaa:6:32a:a7b:23c8:3d9b:fe49:2\n    # example response: 3d8dd15c1ed778.vm.jts.internal.\n    ```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffly-apps%2Fjs-sqlite-in-tigris","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffly-apps%2Fjs-sqlite-in-tigris","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffly-apps%2Fjs-sqlite-in-tigris/lists"}