rembrembdocs

NestJS Plugin

Automatically expose your NestJS dev server via OutRay tunnel

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

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

Add the plugin to your NestJS application's main.ts:

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Start the server
  await app.listen(3000);

  // Start the tunnel in development
  if (process.env.NODE_ENV !== 'production') {
    await outray(app);
  }
}
bootstrap();

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

[Nest] Application is running on: http://localhost:3000
  ➜  Tunnel:  https://abc123.outray.dev

The plugin accepts an options object to customize its behavior:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

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

Options

OptionTypeDefaultDescription
port`numberstring`Auto-detected
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.NODE_ENV !== 'production'Enable or disable the tunnel. Disabled by default in production.
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.

The plugin respects the following environment variables:

VariableDescription
NODE_ENVSet to production to disable the tunnel automatically
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)

Custom Subdomain

Reserve a consistent subdomain for your API:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

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

Custom Domain

Use your own domain for the tunnel:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

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

Conditional Enabling

Only enable the tunnel in certain environments:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

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

With Callbacks

React to tunnel events in your application:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

  await 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...');
    },
  });
}
bootstrap();

Silent Mode

Disable all tunnel logs:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

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

Explicit Port

If automatic port detection doesn't work, you can specify the port explicitly:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const port = process.env.PORT || 3000;
  await app.listen(port);

  await outray(app, {
    port: port,
  });
}
bootstrap();

Perfect for exposing your GraphQL API for testing:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

  await outray(app, {
    subdomain: 'graphql-dev',
    onTunnelReady: (url) => {
      console.log(`GraphQL Playground: ${url}/graphql`);
    },
  });
}
bootstrap();

Perfect for testing webhooks from external services:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

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

The plugin includes full TypeScript definitions:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { outray, OutrayPluginOptions } from '@outray/nest';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);

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

  await outray(app, options);
}
bootstrap();

The plugin supports NestJS versions 8.x, 9.x, 10.x, and 11.x.

The plugin:

  1. Detects the port from the NestJS application's HTTP server
  2. Establishes a WebSocket tunnel to OutRay servers
  3. Proxies all incoming HTTP requests to your local server
  4. Handles cleanup on process exit (SIGINT, SIGTERM)

Tunnel not starting

Port detection issues

If the plugin can't detect the port automatically:

Authentication errors

Connection issues

Not seeing tunnel URL

Make sure you're running in development mode:

NODE_ENV=development npm run start:dev

Or ensure enabled is not set to false.