Skip to content

Local Development

This guide covers how to run your Stoma gateway locally during development. Choose the approach that matches your target runtime.

The fastest way to iterate on your gateway is using Node.js with tsx (TypeScript execute). This works for any gateway that doesn’t rely on Cloudflare-specific bindings.

Terminal window
npm install -D tsx

Create a simple entry point that starts a local server:

runner.ts
import { serve } from "@hono/node-server";
import { createGateway, rateLimit, health } from "@homegrower-club/stoma";
import { memoryAdapter } from "@homegrower-club/stoma/adapters";
const adapter = memoryAdapter();
const gateway = createGateway({
name: "dev-gateway",
basePath: "/api",
routes: [
health({ path: "/health" }),
{
path: "/users/*",
pipeline: {
policies: [
rateLimit({ max: 100, windowSeconds: 60, store: adapter.rateLimitStore }),
],
upstream: {
type: "url",
target: "https://jsonplaceholder.typicode.com",
},
},
},
],
});
const port = Number(process.env.PORT) || 3000;
console.log(`🚀 Gateway running at http://localhost:${port}`);
console.log(` Health: http://localhost:${port}/api/health`);
serve({
fetch: gateway.app.fetch,
port,
});
Terminal window
npx tsx runner.ts
  • All URL-based upstreams
  • Handler upstreams
  • In-memory stores (rate limiting, caching, circuit breaker)
  • All policies that don’t require Cloudflare-specific bindings
  • Service Bindings (Cloudflare-only)
  • Cloudflare KV, Durable Objects, or other Workers bindings
  • Browser Rendering bindings

Option 2: Cloudflare Workers with wrangler

Section titled “Option 2: Cloudflare Workers with wrangler”

If your gateway uses Cloudflare-specific features, run it with wrangler dev.

Terminal window
npm install -D wrangler
src/index.ts
import { createGateway, rateLimit, health, cors } from "@homegrower-club/stoma";
const gateway = createGateway({
name: "my-gateway",
basePath: "/api",
policies: [cors()],
routes: [
health({ path: "/health" }),
{
path: "/users/*",
pipeline: {
policies: [
rateLimit({ max: 100, windowSeconds: 60 }),
],
upstream: {
type: "url",
target: "https://jsonplaceholder.typicode.com",
},
},
},
],
});
export default gateway.app;

Create a wrangler.jsonc in your project root:

{
"name": "my-gateway",
"compatibility_date": "2025-01-01",
"main": "src/index.ts"
}
Terminal window
npx wrangler dev

This starts a local dev server at http://localhost:8787. Any changes to your code trigger an automatic rebuild.

If your gateway calls other Workers via Service Bindings, you can mock them in wrangler.jsonc:

{
"name": "my-gateway",
"compatibility_date": "2025-01-01",
"main": "src/index.ts",
"services": [
{
"binding": "AUTH_SERVICE",
"service": "auth-worker"
}
]
}

The Stoma repository includes a demo gateway showcasing multiple policies. You can run it locally to explore features:

Terminal window
cd examples/cloudflare-worker
npm install
npm run dev

This runs the demo with wrangler dev, giving you a live gateway with:

  • Rate limiting
  • Circuit breaker
  • Caching
  • Request/response transforms
  • Health checks
  • Markdown rendering (requires Browser Rendering binding)

Both Node.js and wrangler approaches support environment variables:

Create a .env file and use dotenv:

Terminal window
npm install dotenv
import "dotenv/config";
// Your gateway code uses process.env.JWT_SECRET

For projects that target both Node.js and Cloudflare, you can conditionally use different adapters:

import { createGateway, rateLimit, health, cors } from "@homegrower-club/stoma";
import { memoryAdapter } from "@homegrower-club/stoma/adapters";
const isCloudflare = process.env.WORKER_RUNTIME === "cloudflare";
const gateway = createGateway({
name: "my-gateway",
adapter: isCloudflare
? undefined // Use default (Cloudflare) adapter
: memoryAdapter(), // Use in-memory for Node.js dev
// ... rest of config
});