Skip to content

Express

To integrate Exot Inspector into an Express app, you can utilize Exot’s Express module, which offers request tracing and error handling middlewares.

Install

Terminal window
npm install @exotjs/express

Usage

import express from 'express';
import { errorHandler, middleware } from '@exotjs/express';
import { Inspector } from '@exotjs/express/inspector';
import { MemoryStore } from '@exotjs/express/store';
const app = express();
const inspector = new Inspector({
store: new MemoryStore(),
});
app.use(middleware({
inspector,
}));
app.use(errorHandler({
inspector,
}));

For more details, visit the express module repository.

WebSocket server

To connect the Inspector App, you’ll need to expose a WebSocket server, to which the app will connect. The express module comes with a built-in server:

import { websocketServer } from '@exotjs/express';
websocketServer({
authorize: async (req) => {
// authorize request
},
inspector,
path: '/_inspector',
ws: {
port: 3001,
},
});

In the example above, the app would connect to ws://localhost:3001/_inspector.

Server configuration

  • authorize - An authorization function; throw an error for unauthorized access.
  • inspector - The Inspector instance.
  • path - The WebSocket URL path to which the app can connect.
  • ws - WebSocket server options, see ws docs.

You can use a custom HTTP server by passing { ws: { noServer: true, server } }. The request upgrade is handled automatically, and the authorize function works both ways. It is recommended to use noServer with authorize as authorization occurs before the request upgrade.

Security

Recommended steps to secure the WebSocket server:

  • Use a unique path with an unguessable token, e.g., /_inspector/K7hA9m16.... This makes it difficult for attackers to guess the path the server is listening on.
  • Utilize the authorize function to check the IP address, a cookie, or an access token. Note that WebSockets don’t support “basic auth”; you’ll need to send the user/password or a token in the query string.

How to

Authorization

To secure the WebSocket server, implement the authorize function and validate the client’s IP address, a cookie, or an access token:

import { websocketServer } from '@exotjs/express';
const ALLOWED_IPS = ['127.0.0.1'];
const ALLOWED_ACCESS_TOKENS = ['abc123'];
websocketServer({
authorize: async (req) => {
// Check the client IP address
if (!ALLOWED_IPS.includes(req.socket.remoteAddress)) {
throw new Error('Unauthorized.');
}
// Check the access token
const url = new URL(req.path, 'http://localhost');
const accessToken = url.searchParams.get('accessToken');
if (!accessToken || !ALLOWED_ACCESS_TOKENS.includes(accessToken)) {
throw new Error('Unauthorized.');
}
},
inspector,
path: '/_inspector',
ws: {
port: 3001,
},
});

Tracing

The middleware automatically traces all requests, and you can easily trace function calls within request handlers using the trace method from the traces instrument:

const { trace } = inspector.instruments.traces;
app.get('/user/:userId', async (req, res) => {
const user = await trace('db:getUser', () => prisma.users.findUnique({
where: {
id: req.params.userId,
},
}));
res.end({
user,
});
});

The example above will produce a trace:

- request method=GET path=/user/123 [3.45ms]
- db:getUser [3.12ms]

The trace function utilizes Node’s AsyncLocalStorage to automatically detect the context in which the function is executed, eliminating the need to pass any context or parent trace for correct trace nesting.