Welcome to The Beanstalk!
This application is entirely build and deployed using the Cloudflare Worker platform. Wrangler & Miniflare power the local development environment making it trivial to iterate on the UI and APIs. Pricing is also quite generous at just $5 a month for their standard tier.
The API is using Cloudflare's awesome itty-router-openapi project, which makes it trivial to create an OpenAPI spec directly from Typescript + Zod objects. Using this project works great with the CF workers and trivializes keeping the OpenAPI spec up to date.
You can see the Beanstalk's API spec at https://netrunner-beanstalk.net/api/docs or redoc.
Using this OpenAPI spec, we entirely generate our Typescript based UI clients using https://github.com/ferdikoomen/openapi-typescript-codegen, allowing us to just write the API once and immediately use it on the UI. This client is fully take safe and enables us to import shared types between the API and the UI. This technical design choice has seriously saved some time and effort.
The UI is built with modern React and TailwindCSS for styling. Various components and design system elements can be viewed and developed without needing the run the API and depend upon real data. The Webapp is built using rspack which is a replacement for webpack written in Rust. It's crazy fast and only going to get so much better. Yes, this is a small app, but I wanted to try out the new hotness.
The API, deployed entirely as Cloudflare workers, means we're confined to the Cloudflare Workers platform. Fortunately, these days it has everything you would expect, including async cron scheduled handler, a pub-sub based queue, a key-value blob store, and even a relational database product that works perfectly with their worker platform. Like I said, Beanstalk is all-in on the CF Worker platform, and it's been great so far. Take a look at the wrangler.toml file which has the full configuration of the API, showing you what Cloudflare products are set up.
Cloudflare has some limitations which is why there's a decent amount of logic in the background.ts. There's a CPU time limit as well as a concurrency limit.
At Beanstalk we have a free-tier Sentry domain up and running and we should try hard to keep it free. Currently, this is set up using Cloudflare's Sentry Integration configured to create issues for every 400, 4xx (!401), and every 5xx.
If you become a collaborator on the project, I'm happy to grant access to our Sentry project -> https://netrunner-beanstalk.sentry.io/issues/
This project uses pnpm as it's package manager which is again, the new hotness. Symlinks ftw!
There are number of things that all need to running in order to have a smooth developer experience. We are using PM2 to help facilitate the local dev orchestration.
- api: the cf worker api that uses the wrangler cli under the hood
- app: the main React app that uses rspack to bundle and serve the webapp during development
- tailwind: the CSS framework/utility we use to create automatically generated css
- spec: Using the OpenAPI spec we codegen a client for the react app to use. This will run whenever openapi.ts changes.
# Install pnpm however you'd like, then run the following
pnpm install
# To start all of the things run and open a PM2 monit view run
pnpm start
# To start individual sub-systems run the following
pnpm start-tailwind
pnpm start-api
pnpm start-app
pnpm spec
It's important to have real data to start developing locally. You can use Wrangler to export the db locally, but will
likely have issues if you've already applied migrations. Delete the sqlite db from api/.wrangler/state/**/*.sqlite
and import data with the given commands below. You may need to adjuste the order of when tables are getting created, i.e.
move the users section to the top.
# export the backfile file from the remote
pnpm --filter api exec wrangler d1 export beanstalk-api --output backup.sql --remote
# import the data locally
pnpm --filter api exec wrangler d1 execute beanstalk-api --file backup.sql
PM2 is an alternative to supervisord if you are familiar with that product. Based upon a config file PM2 can orchestrate running these "apps" locally for you w/o needing to open N number of terminal windows. This means once you stand it all up, it will stay running. There's a few pnpm commands that abstract away the need to learn PM2 commands directly, but it's certainly recommended to read up on the product.
# starts all of the services and runs the monit command
pnpm start
# tails logs and formats the lines with the name of the app
pnpm logs
# brings all of the service down
pnpm stop
# to run a specific PM2 command
pnpm exec pm2 <command>
Be sure to write them! We use Miniflare locally to test the http handlers. Unfortunately, Miniflare requires a fully built and packaged app, so you need to run a full build prior to running the tests. You can run all the tests with the command below.
pnpm -r test
Move the .dev.vars.example file to api/.dev.vars and edit any secrets to test certain features like OAauth w/ NRDB which requires a specific client secret and token.
mv api/.dev.vars.example api/.dev.vars
This repo has some pre-commits installed to help ensure the repo stays looking fresh. These should come pre-installed and ready to go out of the box after a pnpm install
.
We use Biomejs as our linter & formatter. Install the BiomeJS plugin into your editor, and you will get autoformatting out of the box. To run all lints run
pnpm lint
VSCode workspace file should be good to go. All your really need to do is run pnpm install
and ensure the Biome plugin is installed. In order for the api and app to play nicely together in the same sandbox, you should open the multiroot-workspace in the root of the repo.
Github Actions are set up for this repo. The CI job will install dependencies, run lints, and run any tests configured in any of the pnpm workspaces. Tests will run on your Pull Requests and give and post test statuses. We expect tests to pass prior to merging.