SHARDWIRE

Getting started

The fastest way to see Shardwire working: scaffold, set two secrets, run two commands.

You are two small programs away from a working bot: one starts The Connection on the bot side, the other connects your app and listens for events.

Choose your path

PathBest forHow
npm create shardwire → Express Server (default)New project anywhere; shardwire from npm; TypeScript + tsx + Express (GET /health)Run npm create shardwire (interactive default is Express Server). Or npm create shardwire my-app, then cd into the folder and follow the printed steps. Template source: express-server on GitHub
npm create shardwire → React AppBrowser dashboard / UI with @shardwire/reactChoose React App in the prompt, or npm create shardwire -t react-vite. Template source: react-vite on GitHub

Scaffold from npm (golden path)

The default Express Server template is TypeScript entrypoints (src/bot.ts, src/app.ts) run with tsx. The app process starts Express on PORT (default 3000) with GET /health, then connects the bridge client. After npm install, copy .env.example.env, set DISCORD_TOKEN and SHARDWIRE_SECRET, run npm run register once for the included /hello command, then run npm run bot and npm run app in two terminals. Slash-command registration lives in src/register.ts — see First slash command.

Prerequisites

  • Express Server template / any Node app using connectBotBridge: Node.js 22+ (that app process relies on native globalThis.WebSocket).
  • React App template: follow your bundler’s runtime floor (the official Vite scaffold uses ^20.19.0 || >=22.12.0).
  • A Discord bot token and a shared secret string you invent (any long random string).

Create a .env next to the scaffold with DISCORD_TOKEN and the secret variables your template expects. The Express Server scaffold uses one shared SHARDWIRE_SECRET for the Node app process. The React App scaffold instead creates a dedicated browser-scoped secret on the bot side (SHARDWIRE_BROWSER_SECRET) and expects VITE_SHARDWIRE_SECRET plus VITE_SHARDWIRE_SECRET_ID=browser in the browser bundle — see Connection and authentication if the app cannot connect.

Run npm run bot — this starts the bot bridge (Discord + bridge server).

Run npm run app (or npm run dev for Vite) — this connects your app over The Connection and subscribes to events.

Common mistakes (quick checks)

  • Secret mismatch — the string in server.secrets (bot) must equal secret (app). For the React/Vite scaffold, VITE_SHARDWIRE_SECRET must match SHARDWIRE_BROWSER_SECRET, and VITE_SHARDWIRE_SECRET_ID must match the configured secret id (browser in the official template). See Connection and authentication.
  • Wrong URL — path is usually /shardwire; loopback ws:// vs remote wss://. See App URL protocol and Non-loopback requires wss.
  • Bot not running first — start the bot process before the app. See Connection and authentication.

The two tiny programs

Bot side (holds Discord + exposes the bridge)—under 10 lines of core setup:

import { createBotBridge } from 'shardwire';

const bridge = createBotBridge({
  token: process.env.DISCORD_TOKEN!,
  intents: ['Guilds', 'GuildMessages', 'GuildMembers', 'MessageContent'],
  server: { port: 3001, secrets: [process.env.SHARDWIRE_SECRET!] },
});
await bridge.ready();

App side (your logic)—also under 10 lines to connect and subscribe:

import { connectBotBridge } from 'shardwire/client';

const app = connectBotBridge({
  url: 'ws://127.0.0.1:3001/shardwire',
  secret: process.env.SHARDWIRE_SECRET!,
  appName: 'my-app',
});
app.on('messageCreate', ({ message }) => console.log(message.content));
await app.ready();

If you used npm create shardwire (Express Server template), appName is already set in src/app.ts to match your project’s generated manifest name.

Browser apps should use scoped secrets

If your app runs in the browser, do not expose a broad internal secret. Use object-form server.secrets with an id plus allow derived from generateSecretScope(manifest), then pass that secret value and id from the browser.

Import path on the app

In Vite / browser bundles, always import from shardwire/client so the heavy bot runtime is not pulled into the browser. In Node-only scripts you can still use shardwire/client—it is the same app API.

Migration paths from raw discord.js

If you already have a discord.js codebase, you can migrate incrementally:

  • Hybrid mode: keep your existing client logic while adding app-side Shardwire subscriptions/actions.
  • Single-process mode: keep bot + app APIs in one process now, split later when you need scale.
  • Raw passthrough: call supported discord.js methods through app.raw(...) as a break-glass fallback.
import { createBotBridge } from 'shardwire';

const bot = createBotBridge({
  mode: 'hybrid',
  exposeClient: true,
  token: process.env.DISCORD_TOKEN!,
  intents: ['Guilds', 'GuildMessages', 'MessageContent'],
  raw: { enabled: true, allow: ['guilds.fetch', 'channels.fetch'] },
  server: { port: 3001, secrets: [process.env.SHARDWIRE_SECRET!] },
});
await bot.ready();

const client = bot.client(); // optional underlying discord.js Client
import { createBotBridge } from 'shardwire';

const bridge = createBotBridge({
  mode: 'single-process',
  token: process.env.DISCORD_TOKEN!,
  intents: ['Guilds', 'GuildMessages', 'MessageContent'],
  raw: { enabled: true, allow: ['guilds.fetch'] },
});
const app = bridge.app();
await bridge.ready();

if (!app) throw new Error('single-process app bridge unavailable');
await app.raw('guilds.fetch', ['123456789012345678']);

What “good” looks like

  • The bot terminal prints that the bridge is ready (bot bridge ready in official templates).
  • The app terminal prints capabilities (what events and actions you can use), or the Vite page shows ready with capabilities JSON.
  • Sending a message your bot can see shows up in the app logs (or UI).

Deep dives (Reference)

When something errors with See: https://shardwire.js.org/docs/troubleshooting#…, open Troubleshooting and jump to that anchor—each one maps to a short explanation plus links back here or to Reference.

On this page