ReadMe Metrics SDK for Node.js
The `readmeio` package provides a Node.js SDK for integrating server-side API metrics with ReadMe.com's API Metrics Dashboard. This library, currently at version 6.2.1, allows developers to track API usage, troubleshoot issues, and gain deep insights into API performance. It supports both generic Node.js integrations and specific middleware for frameworks like Express.js, enabling the capture of incoming request and outgoing response details. Key differentiators include its tight integration with the ReadMe.com platform for centralized API documentation and metrics visualization, and robust features for redacting sensitive parameters or headers before logs are sent. The SDK helps teams monitor aggregate usage data and analyze specific API calls within the ReadMe ecosystem. While no explicit release cadence is published, the project is actively maintained, receiving regular updates to support new features and address issues.
Common errors
-
Error: You must provide an API key. Please visit https://dash.readme.com/project/YOUR_PROJECT/v1.0/api-metrics to get one.
cause The `apiKey` option was not provided or was empty during the `Metrics` class instantiation.fixEnsure `apiKey` is passed to the `Metrics` constructor, typically from an environment variable (e.g., `new Metrics({ apiKey: process.env.README_API_KEY })`). -
TypeError: (0 , readmeio__WEBPACK_IMPORTED_MODULE_0__.Metrics) is not a constructor
cause This error often occurs in bundled environments (like Webpack or Vite) when mixing CommonJS `require` semantics with ESM `import` syntax, or incorrectly trying to import a named export as a default export.fixEnsure you are using `import { Metrics } from 'readmeio';` for named imports. Verify your bundler configuration correctly handles ESM and CJS interop, especially if your project is `type: 'module'`. -
TypeError: metrics.log is not a function
cause The `metrics` object was not correctly instantiated, or `log` was called before the object was fully initialized.fixDouble-check that `new Metrics({ ... })` was successfully called and that the resulting instance is available when `log` is invoked. Ensure there are no asynchronous initialization issues preventing `metrics` from being assigned. -
ERR_REQUIRE_ESM
cause Attempting to use `require('readmeio')` in a pure ESM context (e.g., in a Node.js project with `"type": "module"` in `package.json` where `readmeio` might not provide a CommonJS entry point for that specific environment).fixMigrate your import statements from `const { Metrics } = require('readmeio');` to `import { Metrics } from 'readmeio';`. Ensure your project and build tools are configured to handle ESM.
Warnings
- gotcha Always protect your `apiKey` and ensure it's not exposed client-side or committed directly into source control. Use environment variables (e.g., `process.env.README_API_KEY`) and secure secret management practices.
- gotcha Improperly configured `redact` options can lead to sensitive data (like PII, authentication tokens, or internal IDs) being sent to ReadMe.com. Thoroughly test your redaction rules.
- gotcha The SDK buffers logs and sends them in batches. If your application terminates abruptly without calling `metrics.sendQueue()`, some logs might be lost. This is particularly relevant in serverless or short-lived processes.
- breaking While Node.js >=14 supports both CommonJS and ESM, future major versions of this SDK (or Node.js itself) may enforce ESM-only. Relying solely on `require()` could lead to breaking changes.
Install
-
npm install readmeio -
yarn add readmeio -
pnpm add readmeio
Imports
- Metrics
const Metrics = require('readmeio');import { Metrics } from 'readmeio'; - expressMiddleware
const expressMiddleware = require('readmeio').expressMiddleware;import { expressMiddleware } from 'readmeio'; - MetricsOptions
import { MetricsOptions } from 'readmeio';import type { MetricsOptions } from 'readmeio';
Quickstart
import { Metrics } from 'readmeio';
import http from 'http';
// Initialize the ReadMe Metrics SDK
// In a real application, retrieve process.env.README_API_KEY and process.env.NODE_ENV securely
const metrics = new Metrics({
apiKey: process.env.README_API_KEY ?? 'your_readme_api_key_here',
development: process.env.NODE_ENV !== 'production',
});
// Create a simple HTTP server to simulate API calls
const server = http.createServer(async (req, res) => {
// Simulate an incoming request
const requestBody = { user: 'testuser', data: 'some data', sensitive_id: '12345' };
const requestHeaders = {
'content-type': 'application/json',
'x-api-key': 'super-secret-internal-key', // Example of a header that might be redacted
'user-agent': 'node-http-client',
};
// Simulate an outgoing response
const responseBody = { status: 'success', message: 'Hello from API', internal_ref: 'xyz' };
const responseHeaders = {
'content-type': 'application/json',
'x-response-id': 'uuid-12345',
};
// Log the request and response to ReadMe
try {
await metrics.log({
// Full URL is important for ReadMe metrics to categorize endpoints
url: new URL(`http://localhost:3000${req.url}`),
method: req.method ?? 'GET',
api: {
key: 'user-id-123', // Identifier for the user making the API call
// label: 'Optional label for this user/API key'
},
// Redaction example: prevent 'x-api-key' header from being sent to ReadMe
// and prevent 'user' field in request body
redact: {
headers: ['x-api-key'],
body: ['user', 'sensitive_id'],
},
request: {
headers: requestHeaders,
body: JSON.stringify(requestBody),
},
response: {
status: 200,
headers: responseHeaders,
body: JSON.stringify(responseBody),
},
});
} catch (error) {
console.error('Failed to log metrics:', error);
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(responseBody));
});
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
console.log('Send a request, e.g., curl http://localhost:3000/api/test');
console.log('Check your ReadMe.com dashboard for metrics.');
});
// Example of how to shut down cleanly
process.on('SIGINT', () => {
console.log('Shutting down server...');
server.close(async () => {
// Ensure any buffered logs are sent before exiting
try {
await metrics.sendQueue();
console.log('Buffered logs sent.');
} catch (error) {
console.error('Failed to send remaining logs:', error);
}
console.log('Server gracefully shut down.');
process.exit(0);
});
});