Before you begin, make sure you have the following ready. The entire flow takes under 10 minutes on a clean machine.
Node.js 18+
Required by the SDK. Check: node --version
Aforo Account
Sign up for an Aforo account — free sandbox included
API Key
sk_test_… from Workspace Admin → API Keys (workspace scope rides on the key)
A billable unit
At least one metric (billable unit) defined in your dashboard
PRO TIP
You do not need a gateway plugin to start. The Node.js SDK sends events directly to Aforo's ingest endpoint. Deploy the gateway plugin later when you're ready to go to production.
Create a single client instance and reuse it across your application. track() only queues; one background sender ships batches every 5 seconds or as soon as 500 events are waiting — whichever comes first. If the ingestor is down, events stay queued and are sent when it is back.
aforo-client.ts
import { AforoClient } from '@aforoai/metering';
export const aforo = new AforoClient({
apiKey: process.env.AFORO_API_KEY!, // sk_test_… or sk_live_…; workspace scope rides on the key
productType: 'API', // default; sent as top-level productType on every event
// Optional — defaults to https://api.aforo.ai. Must be https:// (localhost excepted).
// baseUrl: 'https://api.aforo.ai',
});
INFO
Store your API key in an environment variable — never hardcode it. There is no tenantId argument: the key already scopes your workspace. customerId is the entity you bill within it.
Call aforo.track() anywhere in your request handler, background job, or webhook processor. The call is non-blocking — it returns immediately and the SDK queues the event for async delivery.
api-handler.ts
import { aforo } from './aforo-client';
export async function handleSearchRequest(req: Request) {
// Bill the customer your auth layer verified — never a header the caller can set.
const customerId = (await authenticate(req)).aforoCustomerId;
// Your existing business logic — unchanged
const results = await runSearch(req.body.query);
// Record the usage event — non-blocking, async
await aforo.track({
customerId, // the Aforo customer you're billing
metricName: 'api_calls', // a billable unit defined in your dashboard
quantity: 1,
endpointPath: '/v1/search', // optional top-level HTTP context
statusCode: 200,
responseTimeMs: results.latencyMs,
});
return Response.json(results.data);
}
Full Working Example (standalone script)
If you want to test without wiring into your application, pick your language and run this script directly. It fires a single usage event and flushes to confirm delivery before the process exits.
test-track.ts
import { AforoClient } from '@aforoai/metering';
const aforo = new AforoClient({ apiKey: 'sk_test_YOUR_KEY_HERE' });
async function main() {
console.log('Firing test usage event...');
await aforo.track({
customerId: 'cust_test_001',
metricName: 'api_calls',
quantity: 1,
metadata: { source: 'quick-start-test' },
});
// shutdown() sends what is queued, for at most shutdownTimeoutMs (5 s).
// Anything still queued then is counted in droppedCount (reason 'shutdown').
await aforo.shutdown();
console.log(aforo.droppedCount === 0 ? 'Event sent. Check your Aforo dashboard.' : 'Event not sent — see the WARN log.');
}
main().catch(console.error);
terminal
# Run the test script
npx tsx test-track.ts
# Expected output:
# Firing test usage event...
# Event sent. Check your Aforo dashboard.
PRO TIP
The SDK queues events and sends them in batches. Call await aforo.shutdown() before a script, serverless function or test exits — it sends what is queued within shutdownTimeoutMs. The SDK installs no SIGTERM handler, so a long-running service calls shutdown() from its own. If you meter with the Express / FastAPI / Go / Spring middleware instead of track(), the middleware needs a customer resolver or it fails at startup — see .
Events appear in the Event Log within 5 seconds of delivery. Rating (converting raw events to dollar amounts based on your rate plan) happens asynchronously and is reflected in the Usage tab within 30 seconds.