Skip to content

Release v4.5.0 - Add Stats API#116

Open
piobeny wants to merge 2 commits intomainfrom
release-v4.5.0
Open

Release v4.5.0 - Add Stats API#116
piobeny wants to merge 2 commits intomainfrom
release-v4.5.0

Conversation

@piobeny
Copy link

@piobeny piobeny commented Mar 9, 2026

Motivation

  • Add support for the Email Sending Stats API (/api/accounts/{account_id}/stats) to the Node.js SDK, enabling users to retrieve aggregated email sending statistics.

Changes

  • Add SendingStats, SendingStatGroup, and StatsFilterParams types in src/types/api/stats
  • Add StatsApi class with 5 methods: get, byDomains, byCategories, byEmailServiceProviders, byDate
  • Add query param handling for array filters (sending_domain_ids[], sending_streams[], categories[], email_service_providers[])
  • Add usage example in examples/general/stats.ts
  • Update README with Stats API reference
  • Update CHANGELOG with v4.5.0 entry

How to test

  • statsClient.get() with different parameters (start_date, end_date, sending_domain_ids, sending_streams, categories, email_service_providers)
  • Test grouped endpoints (byDomains, byCategories, byEmailServiceProviders, byDate) with filters

Examples

import { MailtrapClient } from "mailtrap"

const client = new MailtrapClient({ token: "api_key", accountId: accountId })
const statsClient = client.general.stats

// Get aggregated stats
const result = await statsClient.get({
  start_date: "2026-01-01",
  end_date: "2026-01-31",
})
// { delivery_count: 11349, delivery_rate: 0.974, bounce_count: 295, ... }

// Get stats with optional filters
const filtered = await statsClient.get({
  start_date: "2026-01-01",
  end_date: "2026-01-31",
  categories: ["Welcome email"],
})

// Get stats grouped by date
const byDate = await statsClient.byDate({
  start_date: "2026-01-01",
  end_date: "2026-01-02",
})
// [{ name: "date", value: "2026-01-01", stats: { delivery_count: 2220, ... } }, ...]

// Get stats grouped by categories
const byCategories = await statsClient.byCategories({
  start_date: "2026-01-01",
  end_date: "2026-01-02",
})
// [{ name: "category", value: "Welcome email", stats: { ... } }, ...]

// Get stats grouped by email service providers with filters
const byEsp = await statsClient.byEmailServiceProviders({
  start_date: "2026-01-01",
  end_date: "2026-01-02",
  categories: ["Welcome email"],
})
// [{ name: "email_service_provider", value: "Google", stats: { ... } }, ...]

// Get stats grouped by domains with filters
const byDomains = await statsClient.byDomains({
  start_date: "2026-01-01",
  end_date: "2026-01-02",
  categories: ["Welcome email"],
  email_service_providers: ["Google"],
})
// [{ name: "sending_domain_id", value: 75581, stats: { ... } }]

Summary by CodeRabbit

  • New Features

    • Added Statistics API to fetch and group sending statistics by domains, categories, email service providers, and dates. Supports filtering by date range, sending domains, streams, and other criteria.
  • Tests

    • Added comprehensive test suite for the new statistics functionality.
  • Chores

    • Updated package version to 4.5.0.

piobeny and others added 2 commits March 4, 2026 13:43
…ers, byDate endpoints

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a new StatsApi feature to the Mailtrap client library. It adds a Stats API resource with five public endpoints (get, byDomains, byCategories, byEmailServiceProviders, byDate) for querying and grouping sending statistics, along with corresponding TypeScript types, a comprehensive test suite, example usage, and updates the package version to 4.5.0.

Changes

Cohort / File(s) Summary
Stats API Implementation
src/lib/api/resources/Stats.ts
New StatsApi class with public methods for fetching aggregated and grouped statistics. Includes query parameter building with array parameter notation and response mapping to SendingStatGroup format. Private groupedStats helper delegates to specific group endpoints.
Type Definitions
src/types/api/stats.ts
Three new exported types: SendingStats (metrics with counts and rates), SendingStatGroup (name, value, nested stats), and StatsFilterParams (date range and optional filter criteria).
General API Integration
src/lib/api/General.ts
Adds lazy-initialized singleton getter stats() that instantiates StatsApi with current accountId and imports StatsApi resource class.
Public API Surface
src/index.ts
Re-exports all types from stats type module to expose public API surface.
Test Suite
src/__tests__/lib/api/resources/Stats.test.ts
Comprehensive tests covering all five public methods, error handling (401/404), parameter transformation (array notation), and response mapping behavior.
Example Usage
examples/general/stats.ts
Demonstrates all StatsApi endpoints with sample calls showing basic usage, filter parameters, and error handling patterns.
Versioning & Changelog
CHANGELOG.md, package.json
Version bump to 4.5.0 and changelog entry documenting new StatsApi with all endpoints.

Sequence Diagram

sequenceDiagram
    participant Client as User Code
    participant MailtrapClient as MailtrapClient
    participant StatsApi as StatsApi
    participant Axios as Axios
    participant Backend as Backend API

    Client->>MailtrapClient: client.stats.get(params)
    MailtrapClient->>StatsApi: initialize (lazy singleton)
    MailtrapClient->>StatsApi: get(params)
    StatsApi->>StatsApi: buildQueryParams(params)
    StatsApi->>Axios: GET /stats?start_date=...&filters[]
    Axios->>Backend: HTTP GET request
    Backend-->>Axios: JSON response
    Axios->>Axios: transform to response.data
    StatsApi-->>Client: SendingStats object

    alt Grouped Stats (byDomains, byCategories, etc.)
        Client->>MailtrapClient: client.stats.byDomains(params)
        MailtrapClient->>StatsApi: byDomains(params)
        StatsApi->>StatsApi: groupedStats('domains', params)
        StatsApi->>Axios: GET /stats/grouping/domains?...
        Axios->>Backend: HTTP GET request
        Backend-->>Axios: JSON array response
        StatsApi->>StatsApi: map to SendingStatGroup[]
        StatsApi-->>Client: SendingStatGroup[] array
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

feature request

Suggested reviewers

  • mklocek
  • VladimirTaytor
  • leonid-shevtsov

Poem

🐰 Hops through stats with glee,
Five new endpoints dance so free,
Domains, dates, and filters too,
Query params in proper hue,
StatsApi hops along with you! 📊

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: releasing version 4.5.0 with the new Stats API feature.
Description check ✅ Passed The description covers motivation, changes, testing instructions, and practical examples. While it doesn't include the 'Images and GIFs' section from the template, that section is non-critical for this type of API feature addition.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch release-v4.5.0

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Around line 1-2: Update the release header "## [4.5.0] - 2025-03-06" in
CHANGELOG.md to the correct 2026 release date (replace "2025-03-06" with the
actual 2026 date for the 4.5.0 release) so the chronology with "## [4.4.0] -
2025-12-08" is correct.

In `@src/lib/api/resources/Stats.ts`:
- Around line 100-121: The buildQueryParams method is manually appending "[]" to
array parameter keys (e.g., "sending_domain_ids[]") which causes
double-bracketed query strings when Axios serializes arrays; update
Stats.buildQueryParams (accepting StatsFilterParams) to use plain keys
("sending_domain_ids", "sending_streams", "categories",
"email_service_providers") without the "[]" suffix so axios.get(..., { params:
buildQueryParams(...) }) will serialize arrays correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d342a8a2-ed69-490b-9da4-f213fcbf0fa8

📥 Commits

Reviewing files that changed from the base of the PR and between 484d833 and 9b6f082.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • examples/general/stats.ts
  • package.json
  • src/__tests__/lib/api/resources/Stats.test.ts
  • src/index.ts
  • src/lib/api/General.ts
  • src/lib/api/resources/Stats.ts
  • src/types/api/stats.ts

Comment on lines +1 to +2
## [4.5.0] - 2025-03-06
- Add StatsApi with get, byDomains, byCategories, byEmailServiceProviders, byDate endpoints
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the 4.5.0 release date.

4.5.0 is listed as 2025-03-06, but the next entry below is 4.4.0 on 2025-12-08. That chronology can't be correct, so this should be updated to the actual 2026 release date before publishing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 1 - 2, Update the release header "## [4.5.0] -
2025-03-06" in CHANGELOG.md to the correct 2026 release date (replace
"2025-03-06" with the actual 2026 date for the 4.5.0 release) so the chronology
with "## [4.4.0] - 2025-12-08" is correct.

Comment on lines +100 to +121
private static buildQueryParams(
params: StatsFilterParams
): Record<string, unknown> {
const query: Record<string, unknown> = {
start_date: params.start_date,
end_date: params.end_date,
};

if (params.sending_domain_ids) {
query["sending_domain_ids[]"] = params.sending_domain_ids;
}
if (params.sending_streams) {
query["sending_streams[]"] = params.sending_streams;
}
if (params.categories) {
query["categories[]"] = params.categories;
}
if (params.email_service_providers) {
query["email_service_providers[]"] = params.email_service_providers;
}

return query;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import io
import json
import tarfile
import urllib.request

def inspect(version: str) -> None:
    meta = json.load(urllib.request.urlopen(f"https://registry.npmjs.org/axios/{version}"))
    tarball_url = meta["dist"]["tarball"]
    tarball_bytes = urllib.request.urlopen(tarball_url).read()

    with tarfile.open(fileobj=io.BytesIO(tarball_bytes), mode="r:gz") as tar:
        member = next(m for m in tar.getmembers() if m.name.endswith("lib/helpers/buildURL.js"))
        source = tar.extractfile(member).read().decode()

    print(f"\n--- axios {version} ---")
    for line_no, line in enumerate(source.splitlines(), 1):
        if "key = key + '[]'" in line or 'key = key + "[]"' in line:
            print(f"{line_no}: {line.strip()}")

for version in ("0.27.2", "1.12.0"):
    inspect(version)
PY

Repository: mailtrap/mailtrap-nodejs

Length of output: 2687


🏁 Script executed:

# Check if the Stats.ts file exists and inspect the implementation
git ls-files | grep -i stats

Repository: mailtrap/mailtrap-nodejs

Length of output: 193


🌐 Web query:

Axios array parameter serialization behavior how does axios handle array values in params

💡 Result:

Axios serializes params into the URL query string. For array values, the default behavior (Axios v1.x) is “empty brackets” format—i.e., it repeats the key and appends []:

axios.get("/search", { params: { tags: ["a", "b"] } });
// -> /search?tags[]=a&tags[]=b

Axios exposes this via paramsSerializer.indexes, where the documented options are: null = no brackets, false (default) = empty brackets, true = indexed brackets. [1]

Examples:

// tags=a&tags=b
axios.get("/search", {
  params: { tags: ["a", "b"] },
  paramsSerializer: { indexes: null }
});

// tags[]=a&tags[]=b  (default)
axios.get("/search", {
  params: { tags: ["a", "b"] },
  paramsSerializer: { indexes: false }
});

// tags[0]=a&tags[1]=b
axios.get("/search", {
  params: { tags: ["a", "b"] },
  paramsSerializer: { indexes: true }
});

If you need other formats (e.g., comma-separated), you must provide a custom paramsSerializer.serialize (often implemented with qs.stringify(...)). [1][2]

Sources:
[1] Axios GitHub README – paramsSerializer and indexes options https://github.com/axios/axios
[2] Stack Overflow – using custom paramsSerializer / qs for alternate array formats https://stackoverflow.com/questions/74025557/correct-way-of-using-axios-paramsserializer-in-its-version-1-x


🏁 Script executed:

# Read the Stats.ts file to confirm the implementation
sed -n '90,125p' src/lib/api/resources/Stats.ts | cat -n

Repository: mailtrap/mailtrap-nodejs

Length of output: 1167


🏁 Script executed:

# Check how axios is being used in this file
rg -A 3 "axios\.(get|post|request)" src/lib/api/resources/Stats.ts

Repository: mailtrap/mailtrap-nodejs

Length of output: 50


🏁 Script executed:

# Look at the test file to see what's being tested
cat -n src/__tests__/lib/api/resources/Stats.test.ts | head -100

Repository: mailtrap/mailtrap-nodejs

Length of output: 3267


🏁 Script executed:

# Get more context around the axios call in Stats.ts
sed -n '85,105p' src/lib/api/resources/Stats.ts | cat -n

Repository: mailtrap/mailtrap-nodejs

Length of output: 744


🏁 Script executed:

# Search for the axios.get call in Stats.ts
rg -B 5 -A 5 "axios\.get|\.get\(" src/lib/api/resources/Stats.ts | head -30

Repository: mailtrap/mailtrap-nodejs

Length of output: 50


🏁 Script executed:

# Check the test assertions - look for specific test cases with array filters
rg -A 20 "sending_domain_ids|byDomains" src/__tests__/lib/api/resources/Stats.test.ts

Repository: mailtrap/mailtrap-nodejs

Length of output: 2482


🏁 Script executed:

# Check the Stats.ts class definition and client property
head -50 src/lib/api/resources/Stats.ts | cat -n

Repository: mailtrap/mailtrap-nodejs

Length of output: 1558


🏁 Script executed:

# Also check if there's a custom paramsSerializer being used anywhere
rg -i "paramsserializer|params.*serializ" src/lib/api/resources/

Repository: mailtrap/mailtrap-nodejs

Length of output: 50


Remove manual [] suffix from array parameter keys.

The code pre-suffixes array parameter keys with [] (e.g., sending_domain_ids[]), but axios.get(..., { params }) already appends [] for array values. This results in malformed query strings like sending_domain_ids[][]=... instead of the expected sending_domain_ids[]=.... The current tests only inspect the params object before serialization, so they won't catch this wire-format issue.

Fix by removing the manual [] suffix and letting Axios handle the serialization:

Proposed fix
   private static buildQueryParams(
     params: StatsFilterParams
   ): Record<string, unknown> {
     const query: Record<string, unknown> = {
       start_date: params.start_date,
       end_date: params.end_date,
     };
 
     if (params.sending_domain_ids) {
-      query["sending_domain_ids[]"] = params.sending_domain_ids;
+      query.sending_domain_ids = params.sending_domain_ids;
     }
     if (params.sending_streams) {
-      query["sending_streams[]"] = params.sending_streams;
+      query.sending_streams = params.sending_streams;
     }
     if (params.categories) {
-      query["categories[]"] = params.categories;
+      query.categories = params.categories;
     }
     if (params.email_service_providers) {
-      query["email_service_providers[]"] = params.email_service_providers;
+      query.email_service_providers = params.email_service_providers;
     }
 
     return query;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/api/resources/Stats.ts` around lines 100 - 121, The buildQueryParams
method is manually appending "[]" to array parameter keys (e.g.,
"sending_domain_ids[]") which causes double-bracketed query strings when Axios
serializes arrays; update Stats.buildQueryParams (accepting StatsFilterParams)
to use plain keys ("sending_domain_ids", "sending_streams", "categories",
"email_service_providers") without the "[]" suffix so axios.get(..., { params:
buildQueryParams(...) }) will serialize arrays correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant