Skip to content

Feature request: Add Disposable support to Metrics for automatic flushing via using keyword #5200

@svozza

Description

@svozza

Use case

The Metrics class has a fluent interface where methods like addMetric(), addDimension(), and publishStoredMetrics() return this. Currently, users who are not using the decorator or Middy middleware must either call publishStoredMetrics() manually or wrap their handler logic in try/finally to ensure metrics are flushed on error paths:

const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' });

export const handler = async () => {
  try {
    metrics.addMetric('successfulBooking', MetricUnit.Count, 1);
    // business logic
  } finally {
    metrics.publishStoredMetrics();
  }
};

JavaScript's Explicit Resource Management proposal (Stage 3, shipping in Node.js 24) introduces the using keyword, which calls [Symbol.dispose]() automatically when a binding goes out of scope — including when an exception is thrown. Since the fluent methods already return this, making Metrics implement Disposable is a natural fit.

Solution/User Experience

Implement [Symbol.dispose]() on the Metrics class, delegating to publishStoredMetrics(). This allows users to write:

const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' });

export const handler = async () => {
  using _ = metrics.addMetric('successfulBooking', MetricUnit.Count, 1);
  metrics.addDimension('region', 'us-west-2');
  // metrics are automatically flushed when scope exits, including on exceptions
};

The implementation is straightforward. Add the method to the Metrics class:

public [Symbol.dispose](): void {
  this.publishStoredMetrics();
}

And the corresponding signature to MetricsInterface:

[Symbol.dispose](): void;

No AsyncDisposable is needed since publishStoredMetrics() is synchronous. The method is safe to call on an already-flushed instance — publishStoredMetrics() checks hasStoredMetrics() and simply logs a warning if the buffer is empty, which is the same behaviour that already occurs with the decorator and middleware patterns.

On the TypeScript side, ESNext.Disposable needs to be added to the lib compiler option since the Disposable types have not yet been promoted to any yearly ES lib (including ES2024). This is purely a compile-time type concern — it does not change emitted code or require runtime polyfills:

{
  "compilerOptions": {
    "target": "ES2023",
    "lib": ["ES2023", "ESNext.Disposable"]
  }
}

The using keyword requires Node.js 24+. The [Symbol.dispose] method itself is harmless on older runtimes (the symbol exists from Node 18+, it simply won't be called without the using syntax). Unit and e2e tests for this feature should be gated with describe.skipIf(process.versions.node < '24').

Alternative solutions

The existing decorator (@metrics.logMetrics()) and Middy middleware already provide automatic flushing. The using pattern is a lighter-weight alternative for users who prefer not to adopt those patterns.

Acknowledgment

The using/IDisposable pattern is native to .NET and could be considered there. Python and Java do not have an equivalent language feature.

Future readers

Please react with 👍 and your use case to help us understand customer demand.

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-requestThis item refers to a feature request for an existing or new utilitytriageThis item has not been triaged by a maintainer, please wait

    Type

    No type

    Projects

    Status

    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions