OutRay

Express Plugin

Automatically expose your Express dev server via OutRay tunnel

The @outray/express plugin automatically creates an OutRay tunnel when your Express development server starts, giving you a public URL to share your local development environment.

Installation

npm install @outray/express
pnpm add @outray/express
yarn add @outray/express

Quick Start

Add the plugin to your Express application:

// index.js
import express from 'express';
import outray from '@outray/express';

const app = express();

// Apply OutRay middleware
outray(app);

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

When you run your Express server in development mode, you'll see your tunnel URL:

Server running on port 3000
  Tunnel:  https://abc123.outray.dev

Configuration

The plugin accepts an options object to customize its behavior:

import express from 'express';
import outray from '@outray/express';

const app = express();

outray(app, {
  subdomain: 'my-api',
  apiKey: process.env.OUTRAY_API_KEY,
});

app.listen(3000);

Options

OptionTypeDefaultDescription
subdomainstringRequest a specific subdomain for your tunnel URL. Requires authentication.
customDomainstringUse a custom domain. Must be configured in the OutRay dashboard first.
apiKeystringprocess.env.OUTRAY_API_KEYAPI key for authentication.
serverUrlstringwss://api.outray.dev/OutRay server WebSocket URL. Only change this for self-hosted instances.
enabledbooleanprocess.env.OUTRAY_ENABLED !== "false"Enable or disable the tunnel.
silentbooleanfalseSuppress tunnel status logs.
onTunnelReady(url: string) => voidCallback fired when tunnel is successfully established.
onError(error: Error) => voidCallback fired when tunnel encounters an error.
onClose() => voidCallback fired when tunnel connection is closed.
onReconnecting() => voidCallback fired when tunnel is attempting to reconnect.

Environment Variables

The plugin respects the following environment variables:

VariableDescription
NODE_ENVSet to development to enable the tunnel (production is disabled by default)
OUTRAY_API_KEYAPI key for authentication (fallback for apiKey option)
OUTRAY_SUBDOMAINSubdomain to use (fallback for subdomain option)
OUTRAY_SERVER_URLServer URL (fallback for serverUrl option)
OUTRAY_ENABLEDSet to "false" to disable the tunnel

Examples

Custom Subdomain

Reserve a consistent subdomain for your API:

import express from 'express';
import outray from '@outray/express';

const app = express();

outray(app, {
  subdomain: 'my-api',
  apiKey: process.env.OUTRAY_API_KEY,
});

app.get('/api/health', (req, res) => {
  res.json({ status: 'ok' });
});

app.listen(3000);

Custom Domain

Use your own domain for the tunnel:

import express from 'express';
import outray from '@outray/express';

const app = express();

outray(app, {
  customDomain: 'api.example.com',
  apiKey: process.env.OUTRAY_API_KEY,
});

app.listen(3000);

Conditional Enabling

Only enable the tunnel in certain environments:

import express from 'express';
import outray from '@outray/express';

const app = express();

outray(app, {
  enabled: process.env.EXPOSE_TUNNEL === 'true',
});

app.listen(3000);

With Callbacks

React to tunnel events in your application:

import express from 'express';
import outray from '@outray/express';

const app = express();

outray(app, {
  onTunnelReady: (url) => {
    console.log(`API accessible at: ${url}`);
    // Send to Slack, update config, etc.
  },
  onError: (error) => {
    console.error('Tunnel error:', error.message);
  },
  onReconnecting: () => {
    console.log('Connection lost, reconnecting...');
  },
});

app.listen(3000);

Silent Mode

Disable all tunnel logs:

import express from 'express';
import outray from '@outray/express';

const app = express();

outray(app, {
  silent: true,
  onTunnelReady: (url) => {
    // Handle the URL silently
  },
});

app.listen(3000);

Dynamic Port

Works perfectly with dynamically assigned ports:

import express from 'express';
import outray from '@outray/express';

const app = express();

outray(app);

// Use port 0 for a random available port
const server = app.listen(0, () => {
  const port = server.address().port;
  console.log(`Server running on port ${port}`);
});

REST API Integration

Perfect for exposing your local API for webhook testing:

import express from 'express';
import outray from '@outray/express';

const app = express();

outray(app, {
  subdomain: 'webhook-test',
  onTunnelReady: (url) => {
    console.log(`Webhook endpoint: ${url}/webhooks`);
  },
});

app.use(express.json());

app.post('/webhooks', (req, res) => {
  console.log('Received webhook:', req.body);
  res.json({ received: true });
});

app.listen(3000);

TypeScript Support

The plugin includes full TypeScript definitions:

import express, { Application } from 'express';
import outray, { OutrayPluginOptions } from '@outray/express';

const app: Application = express();

const options: OutrayPluginOptions = {
  subdomain: 'my-api',
  apiKey: process.env.OUTRAY_API_KEY,
  onTunnelReady: (url: string) => {
    console.log(`Tunnel ready: ${url}`);
  },
};

outray(app, options);

app.listen(3000);

Middleware Stack

The OutRay plugin doesn't interfere with your middleware stack. You can place it anywhere:

import express from 'express';
import outray from '@outray/express';
import cors from 'cors';
import helmet from 'helmet';

const app = express();

// Apply OutRay before or after other middleware
outray(app);

app.use(cors());
app.use(helmet());
app.use(express.json());

app.listen(3000);

Express Compatibility

The plugin supports Express versions 4.x and 5.x.

How It Works

The plugin hooks into Express's app.listen() method to:

  1. Detect when your server starts listening
  2. Capture the port number (even for dynamic ports)
  3. Establish a WebSocket tunnel to OutRay servers
  4. Proxy all incoming HTTP requests to your local server

Troubleshooting

Tunnel not starting

  • Ensure NODE_ENV is set to development
  • Check that OUTRAY_ENABLED is not set to "false"
  • Verify your server is listening on a TCP port (not a Unix socket)

Authentication errors

  • Run outray login in your terminal to authenticate
  • Or set the OUTRAY_API_KEY environment variable

Connection issues

  • Check your internet connection
  • Verify the server URL is correct (default: wss://api.outray.dev/)
  • The plugin will automatically attempt to reconnect on connection loss

Not seeing tunnel URL

Make sure you're running in development mode:

NODE_ENV=development node app.js

Or add it to your package.json:

{
  "scripts": {
    "dev": "NODE_ENV=development node app.js"
  }
}

On this page