Development

  1. Requirements
  2. Running from source
  3. Project layout
  4. Checks and tests
  5. Changing the database schema
  6. Changing the API
  7. Docker images
  8. Documentation

Requirements

  • Node.js 24+ and npm
  • Docker, for Postgres

Running from source

git clone https://github.com/bhumit070/send-anywebhook-to-discord.git
cd send-anywebhook-to-discord
npm install
cp .env.example .env

In .env, set APP_SECRET and POSTGRES_PASSWORD, and uncomment DATABASE_URL with the same password:

DATABASE_URL=postgres://send_any_webhook:<POSTGRES_PASSWORD>@localhost:5432/send_any_webhook
PRETTY_LOGS=true

Then, in separate terminals:

npm run db:up                      # Postgres in Docker, exposed on localhost:5432
npm run dev -w @send-any-webhook/backend   # API + worker on :5353, restarts on changes
npm run dev -w @send-any-webhook/frontend  # UI on http://localhost:5252 with hot reload

The Vite dev server forwards /api, /h, /docs and /healthz to localhost:5353, just like nginx does in Docker. Set PUBLIC_BASE_URL=http://localhost:5252 so relay URLs point at the dev server.

Both dev scripts build packages/shared first. If you change code in packages/shared while they’re running, rebuild it with npm run build -w @send-any-webhook/shared.

Project layout

backend/            Fastify API, Swagger, delivery worker
  src/routes/       auth, relays, templates/preview, ingest, queue
  src/services/     delivery (render + send + record attempts), worker (queue loop)
  src/db/           Drizzle schema, migration runner
  drizzle/          SQL migrations
  test/             unit + Postgres integration tests
frontend/           React + Vite + Tailwind + shadcn/ui
  src/api/          generated OpenAPI types and client
  src/components/   Discord preview, mapper, logs, shadcn ui/
  src/pages/        relays, wizard, relay detail, template editor, login
packages/shared/    used by both: mapping → Liquid compiler, renderer + Discord limits,
                    auto-map, JSON Schema → sample, payload paths
docs/               this site (GitHub Pages)

The template editor’s preview and the delivery worker call the same renderMessage from packages/shared, so the preview always matches what gets sent.

Checks and tests

npm run typecheck   # all three packages
npm test            # shared + backend unit tests
npm run build       # production builds

Backend integration tests need a Postgres database they can wipe. They’re skipped unless TEST_DATABASE_URL is set:

docker compose exec postgres psql -U send_any_webhook -c 'create database send_any_webhook_test'
TEST_DATABASE_URL=postgres://send_any_webhook:<password>@localhost:5432/send_any_webhook_test npm test

The integration tests drop and recreate every table in TEST_DATABASE_URL. Never point it at a database you care about.

They use a fake Discord sender, so nothing is ever sent to Discord. CI runs the same checks with a Postgres service on every push and pull request.

Changing the database schema

  1. Edit backend/src/db/schema.ts.
  2. Generate a migration: npm run db:generate -w @send-any-webhook/backend -- --name add_something
  3. Review the SQL in backend/drizzle/ and commit it with the schema change.

Migrations run automatically when the backend starts. To run them by hand: npm run db:migrate -w @send-any-webhook/backend.

Changing the API

Routes declare TypeBox schemas, which drive request validation, response serialization and Swagger. After changing a route or schema, regenerate the frontend’s types:

npm run api:gen -w @send-any-webhook/frontend

This writes frontend/src/api/openapi.json and schema.ts (it needs no database). Commit both.

Docker images

docker build -f backend/Dockerfile -t send-any-webhook-backend .
docker build -f frontend/Dockerfile -t send-any-webhook-frontend .

Both build from the repo root, because they need packages/shared.

Documentation

This site is the docs/ folder: Markdown pages built with Jekyll and the Just the Docs theme, and published to GitHub Pages by .github/workflows/docs.yml whenever docs/ changes on master.

Preview it locally with the same image GitHub uses:

docker run --rm -v "$PWD/docs:/src" -v "$PWD/_site:/out" \
  -e INPUT_SOURCE=/src -e INPUT_DESTINATION=/out \
  ghcr.io/actions/jekyll-build-pages:v1.0.13
npx serve _site   # then open the printed URL + /send-anywebhook-to-discord/

Jekyll treats double curly braces as its own Liquid. Wrap examples of message templates in a Liquid raw … endraw block so they show up as written (see docs/guides/templates.md).