@koryla/node
v0.1.2
Published
A/B testing middleware for Express and any Node.js server. Works on Railway, Render, Fly.io, Heroku, AWS EC2/ECS, or any Node host.
Readme
@koryla/node
A/B testing middleware for Express and any Node.js server. Works on Railway, Render, Fly.io, Heroku, AWS EC2/ECS, or any Node host.
Installation
npm install @koryla/nodeSetup
1. Add environment variables
KORYLA_API_KEY=sk_live_...
KORYLA_API_URL=https://koryla.com2. Add the middleware
Place it before your routes — Koryla rewrites req.url internally so Express routes to the correct handler:
// server.ts
import express from 'express'
import { korylaMiddleware } from '@koryla/node'
const app = express()
app.use(korylaMiddleware({
apiKey: process.env.KORYLA_API_KEY!,
apiUrl: process.env.KORYLA_API_URL!,
}))
// Routes come after — they receive the rewritten req.url
app.get('/', (req, res) => res.render('home-control'))
app.get('/variant-b', (req, res) => res.render('home-variant-b'))3. Create your variant pages
Variant pages must be real routes in the same Express app:
app/
views/
home-control.ejs ← control (base_url: https://acme.com/)
home-variant-b.ejs ← variant (target_url: https://acme.com/variant-b)4. Create an experiment in Koryla
Go to your Koryla dashboard → New experiment:
| Field | Value |
|-------|-------|
| Base URL | https://acme.com/ |
| Variant A — Control | https://acme.com/ · 50% |
| Variant B | https://acme.com/variant-b · 50% |
| Conversion URL | https://acme.com/thank-you |
Set it to Active and you're done.
How it works
User visits /
│
▼
Express middleware (in-process, ~1ms overhead)
│
├── calls Koryla API once every 60s to get experiment config
├── reads koryla_sid cookie
│ ├── cookie present → use existing variant (sticky)
│ └── no cookie → assign variant by traffic weight
│
├── rewrites req.url to /variant-b
└── sets Set-Cookie header for 30 days
│
▼
Express routes /variant-b to its handler
Browser sees original URL (/) — no redirect, no flickerWhy this is better than VWO / Optimizely
| | VWO / Optimizely | @koryla/node | |--|--|--| | When variant is decided | In the browser, after JS loads | On the server, before any HTML | | Flicker | Yes (page hides while swapping) | No | | Extra JS on page | ~80–150 KB | 0 KB | | Blockable by ad blockers | Yes | No | | Works without JS | No | Yes |
