carbonly-node
v0.2.3
Published
Official Node.js SDK for Carbonly — resource usage and sustainability tracking
Maintainers
Readme
carbonly-node
Official Node.js SDK for Carbonly — track resource usage and CO₂ impact of your software features in production.
Installation
npm install carbonly-node
# or
pnpm add carbonly-node
# or
yarn add carbonly-nodeQuick Start
import { CarbonlyClient } from 'carbonly-node';
const carbonly = new CarbonlyClient({
apiKey: process.env.CARBONLY_API_KEY!,
environment: 'production',
});
// Wrap any async function — execution time and memory are captured automatically
const result = await carbonly.track('checkout-flow', () => processCheckout(data));Configuration
const carbonly = new CarbonlyClient({
apiKey: 'carbonly_live_...', // Required. Get this from your Carbonly dashboard.
environment: 'production', // Required. Matches the environment slug in your project.
appVersion: '2.1.0', // Optional. Attached to all events for version tracking.
flushInterval: 5_000, // Optional. How often to send batched events in ms. Default: 5000.
maxBatchSize: 50, // Optional. Max events before forcing a flush. Default: 50.
baseUrl: 'https://...', // Optional. Override the API base URL.
onError: (err) => { // Optional. Called when a batch fails to send.
console.error('[carbonly]', err);
},
});API
carbonly.track(featureKey, fn, meta?)
Wraps an async function. Measures wall-clock execution time and heap memory delta, then records the event. The return value is passed through unchanged.
const user = await carbonly.track('get-user', () => db.users.findById(id));Records even if fn throws — failed calls still consume resources.
carbonly.record(event)
Record a pre-measured event manually. Useful when you already have timing data or are integrating with an existing instrumentation layer.
carbonly.record({
featureKey: 'image-resize',
executionTimeMs: 142,
memoryBytes: 1_048_576,
metadata: { format: 'webp', width: 800 },
});carbonly.flush()
Flush all buffered events immediately and stop the interval timer. Call this before your process exits to avoid dropping events.
process.on('SIGTERM', async () => {
await carbonly.flush();
process.exit(0);
});Express / Connect Middleware
Global middleware — tracks every request
import express from 'express';
import { CarbonlyClient } from 'carbonly-node';
const app = express();
const carbonly = new CarbonlyClient({ apiKey: '...', environment: 'production' });
// Mount once. Uses req.path as the feature key.
app.use(carbonly.use());Per-route middleware — tracks a named feature
app.post('/checkout', carbonly.trackFlow('checkout-flow'), checkoutHandler);
app.get('/products', carbonly.trackFlow('product-listing'), productsHandler);The featureKey passed to trackFlow must match the feature key defined in your Carbonly dashboard.
NestJS
Import CarbonlyModule once in your root AppModule, then use the @Track() decorator on any controller method.
Setup
import { Module } from '@nestjs/common';
import { CarbonlyModule } from 'carbonly-node/nestjs';
@Module({
imports: [
CarbonlyModule.forRoot({
apiKey: process.env.CARBONLY_API_KEY!,
environment: process.env.NODE_ENV ?? 'production',
}),
],
})
export class AppModule {}Async setup (with ConfigService)
import { ConfigModule, ConfigService } from '@nestjs/config';
import { CarbonlyModule } from 'carbonly-node/nestjs';
@Module({
imports: [
ConfigModule.forRoot(),
CarbonlyModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
apiKey: config.get('CARBONLY_API_KEY')!,
environment: config.get('NODE_ENV') ?? 'production',
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}Bind the interceptor globally
// main.ts
import { CARBONLY_CLIENT, CarbonlyInterceptor } from 'carbonly-node/nestjs';
import { Reflector } from '@nestjs/core';
const client = app.get(CARBONLY_CLIENT);
const reflector = app.get(Reflector);
app.useGlobalInterceptors(new CarbonlyInterceptor(client, reflector));@Track() decorator
import { Track } from 'carbonly-node/nestjs';
@Controller('checkout')
export class CheckoutController {
@Post()
@Track('checkout-flow')
async processCheckout(@Body() dto: CheckoutDto) {
// Execution time and memory are recorded automatically
}
}Inject the client directly
import { Inject } from '@nestjs/common';
import { CARBONLY_CLIENT } from 'carbonly-node/nestjs';
import type { CarbonlyClient } from 'carbonly-node';
@Injectable()
export class AnalyticsService {
constructor(@Inject(CARBONLY_CLIENT) private readonly carbonly: CarbonlyClient) {}
async runReport() {
return this.carbonly.track('analytics-report', () => this.buildReport());
}
}License
MIT
