Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/outray-tunnel/outray/llms.txt

Use this file to discover all available pages before exploring further.

The @outray/nestjs package exports an outray() function you call after app.listen() in your main.ts. It detects the port your application is running on, establishes a WebSocket tunnel, and proxies public traffic to your local server.
The tunnel is disabled by default when NODE_ENV=production. It runs in all other environments unless you set enabled: false.

Setup

1

Install the package

npm install @outray/nestjs
2

Call outray() in main.ts

Import outray from @outray/nestjs and call it after await app.listen():
main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { outray } from '@outray/nestjs'

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

Start in development mode

Run your NestJS app outside of production. The tunnel URL appears in the terminal:
[Nest] Application is running on: http://localhost:3000
  Tunnel:  https://abc123.outray.dev
Always await app.listen() before calling await outray(app). The plugin reads the bound port from the running HTTP server. If the server hasn’t started yet, port detection will fail.

Configuration

Pass options as the second argument to outray():
main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { outray } from '@outray/nestjs'

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
portnumber | stringAuto-detectedThe port your NestJS app is running on. Automatically read from app.getHttpServer().address(). Specify this explicitly if auto-detection fails.
subdomainstringRequest a specific subdomain. Requires authentication.
customDomainstringUse a custom domain configured in the OutRay dashboard.
apiKeystringprocess.env.OUTRAY_API_KEYAPI key for authentication.
serverUrlstringwss://api.outray.dev/OutRay server WebSocket URL. Change this only for self-hosted instances.
enabledbooleanprocess.env.NODE_ENV !== 'production'Enable or disable the tunnel. Disabled automatically in production.
silentbooleanfalseSuppress all tunnel status logs.
localbooleanfalseEnable LAN access via mDNS (.local domain) for devices on the same network.
onTunnelReady(url: string) => voidCalled when the tunnel is established.
onLocalReady(info: LocalInfo) => voidCalled when LAN access is available.
onError(error: Error) => voidCalled when the tunnel encounters an error.
onClose() => voidCalled when the tunnel connection closes.
onReconnecting() => voidCalled when the tunnel is attempting to reconnect.

Environment variables

VariableDescription
NODE_ENVSet to production to disable the tunnel automatically.
OUTRAY_API_KEYAPI key for authentication — fallback for the apiKey option.
OUTRAY_SUBDOMAINSubdomain to request — fallback for the subdomain option.
OUTRAY_SERVER_URLServer WebSocket URL — fallback for the serverUrl option.

Examples

Custom subdomain

Reserve a consistent URL for your API:
main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { outray } from '@outray/nestjs'

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 a domain you’ve configured in the OutRay dashboard:
main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { outray } from '@outray/nestjs'

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()

Explicit port

If auto-detection fails, pass the port directly:
main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { outray } from '@outray/nestjs'

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

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

Callbacks

React to tunnel lifecycle events:
main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { outray } from '@outray/nestjs'

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

  await outray(app, {
    onTunnelReady: (url) => {
      console.log(`API accessible at: ${url}`)
    },
    onError: (error) => {
      console.error('Tunnel error:', error.message)
    },
    onReconnecting: () => {
      console.log('Connection lost, reconnecting...')
    },
  })
}
bootstrap()

Silent mode

Suppress all console output and handle the URL yourself:
main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { outray } from '@outray/nestjs'

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

  await outray(app, {
    silent: true,
    onTunnelReady: (url) => {
      // store or forward url as needed
    },
  })
}
bootstrap()

Webhook testing

Expose a specific endpoint for receiving webhooks:
main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { outray } from '@outray/nestjs'

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()

TypeScript

The package exports OutrayPluginOptions for typed configuration:
main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { outray, OutrayPluginOptions } from '@outray/nestjs'

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 full OutrayPluginOptions interface:
interface OutrayPluginOptions {
  port?: number | string
  subdomain?: string
  customDomain?: string
  apiKey?: string
  serverUrl?: string
  enabled?: boolean
  silent?: boolean
  local?: boolean
  onTunnelReady?: (url: string) => void
  onLocalReady?: (info: {
    hostname: string
    ip: string
    httpUrl?: string
    httpsUrl?: string
  }) => void
  onError?: (error: Error) => void
  onReconnecting?: () => void
  onClose?: () => void
}

Compatibility

  • NestJS: 8.x, 9.x, 10.x, 11.x

Troubleshooting

Tunnel not starting
  • Confirm NODE_ENV is not set to production.
  • Make sure you await app.listen() before calling await outray(app).
  • Verify your server is listening on a TCP port.
Port detection failing If you see [Outray] Could not determine port from application, either:
  • Ensure await app.listen(port) completes before outray(app) is called.
  • Pass the port explicitly: outray(app, { port: 3000 }).
Not seeing the tunnel URL Make sure you’re running in development mode:
NODE_ENV=development npm run start:dev
Authentication errors
  • Run outray login to authenticate, or set OUTRAY_API_KEY.
Connection issues
  • The plugin reconnects automatically on connection loss.
  • Confirm the server URL is correct (default: wss://api.outray.dev/).