https://github.com/nvelden/shinylive-d1-datatable
Editable Shiny DataTable in the browser with Shinylive, persisted through a Cloudflare Worker and D1 database.
https://github.com/nvelden/shinylive-d1-datatable
cloudflare-d1 cloudflare-workers datatable github-pages r shiny shinylive webr
Last synced: 8 days ago
JSON representation
Editable Shiny DataTable in the browser with Shinylive, persisted through a Cloudflare Worker and D1 database.
- Host: GitHub
- URL: https://github.com/nvelden/shinylive-d1-datatable
- Owner: nvelden
- Created: 2026-05-04T16:41:49.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-05-05T01:04:31.000Z (about 1 month ago)
- Last Synced: 2026-05-05T02:29:00.289Z (about 1 month ago)
- Topics: cloudflare-d1, cloudflare-workers, datatable, github-pages, r, shiny, shinylive, webr
- Language: R
- Homepage: https://nvelden.github.io/shinylive-d1-datatable/
- Size: 49.3 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Shinylive D1 DataTable
**Live demo:**
A small example of an editable Shiny DataTable that runs entirely in the
browser with [Shinylive](https://posit-dev.github.io/r-shinylive/), is hosted
on GitHub Pages, and persists rows through a [Cloudflare Worker](https://developers.cloudflare.com/workers/)
backed by [Cloudflare D1](https://developers.cloudflare.com/d1/).
This is a 2026 update of a [2019 tutorial](https://www.nielsvandervelden.com/blog/editable-datatables-in-r-shiny-using-sql/)
([repository](https://github.com/nvelden/sql_table)) that built the same table
on a Shiny server with a local SQLite file.
The Worker is the only thing that talks to D1. The browser sends a shared
secret in an `X-API-Key` header. That secret is included in the static bundle,
so it is a soft barrier against opportunistic abuse, not real authentication.
Use this pattern for tutorials and prototypes. For production data, replace
the shared secret with per-user authentication and add rate limiting.
## Project layout
```text
app.R # Shinylive/Shiny DataTable app
worker/src/index.js # Worker API for row CRUD
worker/wrangler.toml.example # Template for the Worker config (gitignored real one is copied from this)
worker/.dev.vars.example # Template for local secret file
migrations/0001_create_responses.sql # Table schema and seed rows
docs/ # Generated by `npm run export`; GitHub Pages serves this
scripts/export-shinylive.R # Clean Shinylive export helper
scripts/setup-local.sh # Local setup helper
```
## Prerequisites
- R with `shiny`, `DT`, `shinyjs`, and `shinylive`
- Node.js 22 or newer
- A Cloudflare account
- A GitHub account
Install the R packages if needed:
```r
install.packages(c("shiny", "DT", "shinyjs", "shinylive"))
```
Install [Wrangler](https://developers.cloudflare.com/workers/wrangler/),
Cloudflare's CLI:
```bash
npm install
```
Log in to Cloudflare:
```bash
npx wrangler login
```
## Run locally
The local helper creates `worker/.dev.vars`, seeds the local D1, and exports
the Shinylive bundle to `docs/`:
```bash
npm run local:setup
```
Then start the Worker and the static server in two terminals:
```bash
npm run worker:dev # Worker on http://localhost:8787
npm run site:dev # docs/ on http://localhost:8000
```
Open `http://localhost:8000`.
## Deploy to GitHub Pages and Cloudflare
Create a D1 database (skip if you already have one):
```bash
npm run d1:create
```
Wrangler prints a `database_id`. The real `worker/wrangler.toml` is gitignored
so the id never reaches the repo. If you ran `npm run local:setup` it was
already created from `worker/wrangler.toml.example`. Otherwise copy it
manually:
```bash
cp worker/wrangler.toml.example worker/wrangler.toml
```
Open `worker/wrangler.toml` and paste the id in. Set `ALLOWED_ORIGIN` to your
GitHub Pages origin (no trailing slash, no path):
```toml
[[d1_databases]]
binding = "SQL_TABLE_DB"
database_name = "your-d1-database-name"
database_id = "your-d1-database-id"
[vars]
ALLOWED_ORIGIN = "https://.github.io"
```
Seed the remote D1 with the schema:
```bash
npm run d1:migrate:remote
```
Generate a shared secret:
```bash
openssl rand -hex 32
```
Store it on the Worker (paste the same value when prompted):
```bash
npm run worker:secret
```
Deploy the Worker:
```bash
npm run worker:deploy
```
Wrangler prints the deployed URL. Open `app.R` and replace the two
placeholders in the JavaScript helper:
- `WORKER_URL` with the deployed URL above
- `SHARED_SECRET` with the value from `openssl rand`
Re-export the bundle so the new values land in `docs/`:
```bash
npm run export
```
Commit `docs/` along with your source changes and push:
```bash
git add docs worker app.R package.json
git commit -m "Initial deploy"
git push
```
In the GitHub repository, open **Settings → Pages**. Under
**Build and deployment**, set the source to the `main` branch and `/docs`
folder. After about a minute the site is live at:
```text
https://.github.io//
```
## Notes
- `docs/` is committed because GitHub Pages serves from a folder in the repo.
Re-export and commit again whenever `app.R` changes.
- `worker/.dev.vars` is gitignored. Never commit it.
- `SHARED_SECRET` ends up in the public JavaScript bundle on GitHub Pages.
Treat it as a tutorial guard, not real authentication.
- To deploy on Cloudflare Pages instead of, or alongside, GitHub Pages, the
repository also includes an `npm run cloudflare:deploy` script that uploads
`docs/` directly to a Cloudflare Pages project.