{"id":50578736,"url":"https://github.com/nvelden/shinylive-d1-datatable","last_synced_at":"2026-06-05T00:03:31.062Z","repository":{"id":355728386,"uuid":"1229049113","full_name":"nvelden/shinylive-d1-datatable","owner":"nvelden","description":"Editable Shiny DataTable in the browser with Shinylive, persisted through a Cloudflare Worker and D1 database.","archived":false,"fork":false,"pushed_at":"2026-05-05T01:04:31.000Z","size":51721,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-05T02:29:00.289Z","etag":null,"topics":["cloudflare-d1","cloudflare-workers","datatable","github-pages","r","shiny","shinylive","webr"],"latest_commit_sha":null,"homepage":"https://nvelden.github.io/shinylive-d1-datatable/","language":"R","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/nvelden.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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-04T16:41:49.000Z","updated_at":"2026-05-05T01:03:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nvelden/shinylive-d1-datatable","commit_stats":null,"previous_names":["nvelden/shinylive-d1-datatable"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/nvelden/shinylive-d1-datatable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvelden%2Fshinylive-d1-datatable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvelden%2Fshinylive-d1-datatable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvelden%2Fshinylive-d1-datatable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvelden%2Fshinylive-d1-datatable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nvelden","download_url":"https://codeload.github.com/nvelden/shinylive-d1-datatable/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvelden%2Fshinylive-d1-datatable/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33924841,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"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":["cloudflare-d1","cloudflare-workers","datatable","github-pages","r","shiny","shinylive","webr"],"created_at":"2026-06-05T00:03:30.262Z","updated_at":"2026-06-05T00:03:31.057Z","avatar_url":"https://github.com/nvelden.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Shinylive D1 DataTable\n\n**Live demo:** \u003chttps://nvelden.github.io/shinylive-d1-datatable/\u003e\n\nA small example of an editable Shiny DataTable that runs entirely in the\nbrowser with [Shinylive](https://posit-dev.github.io/r-shinylive/), is hosted\non GitHub Pages, and persists rows through a [Cloudflare Worker](https://developers.cloudflare.com/workers/)\nbacked by [Cloudflare D1](https://developers.cloudflare.com/d1/).\n\nThis is a 2026 update of a [2019 tutorial](https://www.nielsvandervelden.com/blog/editable-datatables-in-r-shiny-using-sql/)\n([repository](https://github.com/nvelden/sql_table)) that built the same table\non a Shiny server with a local SQLite file.\n\nThe Worker is the only thing that talks to D1. The browser sends a shared\nsecret in an `X-API-Key` header. That secret is included in the static bundle,\nso it is a soft barrier against opportunistic abuse, not real authentication.\nUse this pattern for tutorials and prototypes. For production data, replace\nthe shared secret with per-user authentication and add rate limiting.\n\n## Project layout\n\n```text\napp.R                                 # Shinylive/Shiny DataTable app\nworker/src/index.js                   # Worker API for row CRUD\nworker/wrangler.toml.example          # Template for the Worker config (gitignored real one is copied from this)\nworker/.dev.vars.example              # Template for local secret file\nmigrations/0001_create_responses.sql  # Table schema and seed rows\ndocs/                                 # Generated by `npm run export`; GitHub Pages serves this\nscripts/export-shinylive.R            # Clean Shinylive export helper\nscripts/setup-local.sh                # Local setup helper\n```\n\n## Prerequisites\n\n- R with `shiny`, `DT`, `shinyjs`, and `shinylive`\n- Node.js 22 or newer\n- A Cloudflare account\n- A GitHub account\n\nInstall the R packages if needed:\n\n```r\ninstall.packages(c(\"shiny\", \"DT\", \"shinyjs\", \"shinylive\"))\n```\n\nInstall [Wrangler](https://developers.cloudflare.com/workers/wrangler/),\nCloudflare's CLI:\n\n```bash\nnpm install\n```\n\nLog in to Cloudflare:\n\n```bash\nnpx wrangler login\n```\n\n## Run locally\n\nThe local helper creates `worker/.dev.vars`, seeds the local D1, and exports\nthe Shinylive bundle to `docs/`:\n\n```bash\nnpm run local:setup\n```\n\nThen start the Worker and the static server in two terminals:\n\n```bash\nnpm run worker:dev    # Worker on http://localhost:8787\nnpm run site:dev      # docs/ on http://localhost:8000\n```\n\nOpen `http://localhost:8000`.\n\n## Deploy to GitHub Pages and Cloudflare\n\nCreate a D1 database (skip if you already have one):\n\n```bash\nnpm run d1:create\n```\n\nWrangler prints a `database_id`. The real `worker/wrangler.toml` is gitignored\nso the id never reaches the repo. If you ran `npm run local:setup` it was\nalready created from `worker/wrangler.toml.example`. Otherwise copy it\nmanually:\n\n```bash\ncp worker/wrangler.toml.example worker/wrangler.toml\n```\n\nOpen `worker/wrangler.toml` and paste the id in. Set `ALLOWED_ORIGIN` to your\nGitHub Pages origin (no trailing slash, no path):\n\n```toml\n[[d1_databases]]\nbinding = \"SQL_TABLE_DB\"\ndatabase_name = \"your-d1-database-name\"\ndatabase_id = \"your-d1-database-id\"\n\n[vars]\nALLOWED_ORIGIN = \"https://\u003cyour-username\u003e.github.io\"\n```\n\nSeed the remote D1 with the schema:\n\n```bash\nnpm run d1:migrate:remote\n```\n\nGenerate a shared secret:\n\n```bash\nopenssl rand -hex 32\n```\n\nStore it on the Worker (paste the same value when prompted):\n\n```bash\nnpm run worker:secret\n```\n\nDeploy the Worker:\n\n```bash\nnpm run worker:deploy\n```\n\nWrangler prints the deployed URL. Open `app.R` and replace the two\nplaceholders in the JavaScript helper:\n\n- `WORKER_URL` with the deployed URL above\n- `SHARED_SECRET` with the value from `openssl rand`\n\nRe-export the bundle so the new values land in `docs/`:\n\n```bash\nnpm run export\n```\n\nCommit `docs/` along with your source changes and push:\n\n```bash\ngit add docs worker app.R package.json\ngit commit -m \"Initial deploy\"\ngit push\n```\n\nIn the GitHub repository, open **Settings → Pages**. Under\n**Build and deployment**, set the source to the `main` branch and `/docs`\nfolder. After about a minute the site is live at:\n\n```text\nhttps://\u003cyour-username\u003e.github.io/\u003crepo-name\u003e/\n```\n\n## Notes\n\n- `docs/` is committed because GitHub Pages serves from a folder in the repo.\n  Re-export and commit again whenever `app.R` changes.\n- `worker/.dev.vars` is gitignored. Never commit it.\n- `SHARED_SECRET` ends up in the public JavaScript bundle on GitHub Pages.\n  Treat it as a tutorial guard, not real authentication.\n- To deploy on Cloudflare Pages instead of, or alongside, GitHub Pages, the\n  repository also includes an `npm run cloudflare:deploy` script that uploads\n  `docs/` directly to a Cloudflare Pages project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnvelden%2Fshinylive-d1-datatable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnvelden%2Fshinylive-d1-datatable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnvelden%2Fshinylive-d1-datatable/lists"}