Configuration - Node.js
Options worth knowing
| Option | Default | Meaning |
|---|---|---|
token, privateKey | - | project credentials; both required to send anything |
url | https://dockray.io | address of the DockRay instance |
environment | NODE_ENV or production | environment column in the panel |
release | - | version of the deployed application |
sampleRate | 1 | share of error events sent |
tracesSampleRate | 0 | share of transactions sent; 0 disables tracing |
sendDefaultPii | false | keeps the IP address and user agent on events |
appRoot | process.cwd() | frames below this path are marked as application code |
contextLines | 3 | source lines kept around each application frame |
timeout | 5000 | send timeout, in milliseconds |
beforeSend | - | receives the event, returns it, a changed copy, or null to drop it |
Environments
Every environment should report under an unambiguous name - production, staging, preview. The name is a column in the panel and a filter on the error list, so without it a production outage looks exactly like an error someone triggered in a test. Leave your local environment without credentials: with no token and no key the integration loads and stays silent, so you do not need a separate switch to turn it off.
When events are sent
A visitor never waits on the monitoring service. report() queues a send without awaiting it, keeping a reference to the promise so the process cannot exit mid-flight:
ray.report(ray.captureException(error));
The framework adapters call this on an event that only fires after the response has been sent. The queue holds at most 50 concurrent reports for the whole process; anything beyond that is dropped silently, with no log entry. sampleRate and tracesSampleRate decide what fraction of events even reaches it: 1 always sends, 0 never does, anything in between is rolled independently per call. By default errors go out in full and transactions do not go out at all.
The Express and Fastify adapters
Both adapters name transactions after the route pattern rather than the concrete address - GET /orders/:id, not GET /orders/8123 - so one route stays one row in the panel no matter how many identifiers passed through it. By default they skip /health and /metrics (ignorePaths), report only 5xx responses (shouldReport), and strip Authorization, Cookie and X-Api-Key from the reported headers. The visitor's IP address is read from X-Forwarded-For or X-Real-IP before falling back to the socket address.
import express from 'express';
import { rayRequestHandler, rayErrorHandler } from '@dockcodes/dock-ray/express';
const app = express();
app.use(rayRequestHandler(ray)); // before the routes
// ... routes ...
app.use(rayErrorHandler(ray)); // after the routes
rayRequestHandler opens a transaction and reports it on the response's finish event. rayErrorHandler reports the error and passes it on - it never swallows one, so your own error page still renders.
import { rayPlugin } from '@dockcodes/dock-ray/fastify';
await fastify.register(rayPlugin, { client: ray });
The Fastify equivalent of finish is the onResponse hook - transactions are reported from there, and errors from onError.
Browser errors
A browser cannot authenticate with DockRay directly - the private key has to stay on the server. A report that reached one of your routes is relayed like this:
app.post('/api/ray/browser', async (req, res) => {
ray.report(ray.captureBrowserReport(req.body, {
pageUrl: req.headers.referer,
userAgent: req.headers['user-agent'],
}));
res.status(202).json({ success: true });
});
The payload is untrusted: unrecognised fields are dropped, every string is capped at 1 KB, the stack at 50 frames, and anything that cannot become an event returns false. The envelope - id, timestamp, environment, release - is filled in here, on the server, so a browser never chooses which environment its errors land in. The page-side collector is a separate entry point, @dockcodes/dock-ray/browser; you write the endpoint's guards yourself, as the shipped CMS integrations do.
Protecting the private key
The private key is a project secret, not an identifier. Keep it in environment variables, in a secrets manager or in the server configuration - never in the repository, in logs, in a screenshot or in code sent to the browser. One project can hold many keys, so production and staging should each get their own: either can be revoked on its own without interrupting the others. A suspicion that a key leaked is reason enough to revoke it and generate a new one.