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
Install the package
npm install @outray/nestjs
Call outray() in main.ts
Import outray from @outray/nestjs and call it after await app.listen():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()
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():
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
| Option | Type | Default | Description |
|---|
port | number | string | Auto-detected | The port your NestJS app is running on. Automatically read from app.getHttpServer().address(). Specify this explicitly if auto-detection fails. |
subdomain | string | — | Request a specific subdomain. Requires authentication. |
customDomain | string | — | Use a custom domain configured in the OutRay dashboard. |
apiKey | string | process.env.OUTRAY_API_KEY | API key for authentication. |
serverUrl | string | wss://api.outray.dev/ | OutRay server WebSocket URL. Change this only for self-hosted instances. |
enabled | boolean | process.env.NODE_ENV !== 'production' | Enable or disable the tunnel. Disabled automatically in production. |
silent | boolean | false | Suppress all tunnel status logs. |
local | boolean | false | Enable LAN access via mDNS (.local domain) for devices on the same network. |
onTunnelReady | (url: string) => void | — | Called when the tunnel is established. |
onLocalReady | (info: LocalInfo) => void | — | Called when LAN access is available. |
onError | (error: Error) => void | — | Called when the tunnel encounters an error. |
onClose | () => void | — | Called when the tunnel connection closes. |
onReconnecting | () => void | — | Called when the tunnel is attempting to reconnect. |
Environment variables
| Variable | Description |
|---|
NODE_ENV | Set to production to disable the tunnel automatically. |
OUTRAY_API_KEY | API key for authentication — fallback for the apiKey option. |
OUTRAY_SUBDOMAIN | Subdomain to request — fallback for the subdomain option. |
OUTRAY_SERVER_URL | Server WebSocket URL — fallback for the serverUrl option. |
Examples
Custom subdomain
Reserve a consistent URL for your API:
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:
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:
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:
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:
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:
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:
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/).