Skip to content

Conversation

@mrcfps
Copy link
Contributor

@mrcfps mrcfps commented Dec 19, 2025

  • Updated the 'resend' package version from 4.0.1 to 6.6.0 for improved functionality.
  • Added new configuration options for email sending, including maxRetries, baseDelayMs, and minTimeBetweenEmailsMs.
  • Implemented rate limiting and retry logic in the NotificationService to handle rate limit errors more effectively.
  • Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added automatic retry logic with exponential backoff for email delivery
    • Implemented rate limiting for email sending
    • Added configurable email retry settings (max retries, backoff delay, minimum time between sends)
  • Updates

    • Updated email service dependency

✏️ Tip: You can customize this high-level summary in your review settings.

- Updated the 'resend' package version from 4.0.1 to 6.6.0 for improved functionality.
- Added new configuration options for email sending, including maxRetries, baseDelayMs, and minTimeBetweenEmailsMs.
- Implemented rate limiting and retry logic in the NotificationService to handle rate limit errors more effectively.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.
@coderabbitai
Copy link

coderabbitai bot commented Dec 19, 2025

Walkthrough

The pull request updates the resend email library to a major version, adds environment-driven configuration parameters for email retry behavior and rate limiting, and implements retry logic with exponential backoff in the notification service to handle rate-limit errors gracefully.

Changes

Cohort / File(s) Summary
Dependency upgrade
apps/api/package.json
Updated resend from ^4.0.1 to ^6.6.0 (major version bump)
Email configuration
apps/api/src/modules/config/app.config.ts
Added three numeric email configuration fields: maxRetries (default 3), baseDelayMs (default 500 ms), minTimeBetweenEmailsMs (default 500 ms)
Email retry and rate-limiting logic
apps/api/src/modules/notification/notification.service.ts
Implemented retry mechanism with exponential backoff for rate-limit errors, added rate-limit detection and enforcement of minimum time between sends, introduced helper methods: enforceRateLimit, isRateLimitError, sendEmailWithRetry

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant NS as NotificationService
    participant RL as Rate Limit<br/>Check
    participant Resend as Resend API
    participant Timer as Backoff<br/>Timer

    App->>NS: sendEmail(recipient, content)
    NS->>RL: enforceRateLimit()
    alt Within minimum interval
        RL-->>NS: Wait required
        NS->>Timer: delay until ready
        Timer-->>NS: delay complete
    else Ready to send
        RL-->>NS: Proceed
    end
    
    rect rgba(220, 240, 220, 0.3)
        note over NS,Resend: Retry loop (max attempts)
        NS->>Resend: Send email
        
        alt Rate limit error
            Resend-->>NS: 429/Rate limit
            NS->>NS: isRateLimitError() = true
            NS->>Timer: exponential backoff<br/>(attempt × baseDelayMs)
            Timer-->>NS: delay complete
            NS->>Resend: Retry send
        else Success
            Resend-->>NS: Email sent
            NS->>NS: Store last send time
            NS-->>App: Success
        else Non-rate-limit error
            Resend-->>NS: Error (not rate limit)
            NS-->>App: Throw error
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Retry logic complexity: Exponential backoff calculation and rate-limit error detection require careful verification of edge cases
  • Integration with upgraded dependency: Major version bump of resend (4→6) may introduce breaking changes or behavioral differences needing validation
  • Configuration interaction: Ensure environment variable parsing and defaults align across app.config.ts and notification.service.ts usage
  • Timing guarantees: Rate-limit enforcement and minimum interval between sends need review for race conditions and thread-safety concerns

Poem

🐰 A resend upgrade hops in with grace,
Retries and backoffs at a steady pace,
Rate limits respected, no more rushing ahead,
Our emails now pause, then smoothly are spread! 📧✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding a retry mechanism for email sending, which is the primary objective evidenced by config additions, rate limit handling, and retry logic implementation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/enhance-email-send

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: 1

🧹 Nitpick comments (1)
apps/api/src/modules/notification/notification.service.ts (1)

14-17: Consider distributed rate limiting for scaled deployments.

The lastEmailSentAt state is instance-local, meaning each service instance tracks its own rate independently. In horizontally scaled deployments, the combined request rate could still exceed the Resend API rate limit. Consider using Redis-based rate limiting if this becomes an issue.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70d7872 and c8c483d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • apps/api/package.json (1 hunks)
  • apps/api/src/modules/config/app.config.ts (1 hunks)
  • apps/api/src/modules/notification/notification.service.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{js,ts,jsx,tsx,css,json}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Maximum line length of 100 characters

Files:

  • apps/api/package.json
  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Use 2 spaces for indentation, no tabs

Files:

  • apps/api/package.json
  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml,md}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

No trailing whitespace at the end of lines

Files:

  • apps/api/package.json
  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{tsx,ts,json}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

Support dynamic content with placeholders in translations

Files:

  • apps/api/package.json
  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{js,ts,jsx,tsx}: Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Always validate object properties before destructuring
Always use single quotes for string literals in JavaScript/TypeScript code

**/*.{js,ts,jsx,tsx}: Use semicolons at the end of statements
Include spaces around operators (e.g., a + b instead of a+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement

**/*.{js,ts,jsx,tsx}: Group import statements in order: React/framework libraries, third-party libraries, internal modules, relative path imports, type imports, style imports
Sort imports alphabetically within each import group
Leave a blank line between import groups
Extract complex logic into custom hooks
Use functional updates for state (e.g., setCount(prev => prev + 1))
Split complex state into multiple state variables rather than single large objects
Use useReducer for complex state logic instead of multiple useState calls

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}: All code comments MUST be written in English
All variable names, function names, class names, and other identifiers MUST use English words
Comments should be concise and explain 'why' rather than 'what'
Use proper grammar and punctuation in comments
Keep comments up-to-date when code changes
Document complex logic, edge cases, and important implementation details
Use clear, descriptive names that indicate purpose
Avoid abbreviations unless they are universally understood

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

Use JSDoc style comments for functions and classes in JavaScript/TypeScript

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/01-code-style.mdc)

**/*.{js,jsx,ts,tsx}: Use single quotes for string literals in TypeScript/JavaScript
Always use optional chaining (?.) when accessing object properties in TypeScript/JavaScript
Always use nullish coalescing (??) or default values for potentially undefined values in TypeScript/JavaScript
Always check array existence before using array methods in TypeScript/JavaScript
Validate object properties before destructuring in TypeScript/JavaScript
Use ES6+ features like arrow functions, destructuring, and spread operators in TypeScript/JavaScript
Avoid magic numbers and strings - use named constants in TypeScript/JavaScript
Use async/await instead of raw promises for asynchronous code in TypeScript/JavaScript

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)

**/*.{ts,tsx}: Avoid using any type whenever possible - use unknown type instead with proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new types
Use TypeScript utility types (Partial<T>, Pick<T, K>, Omit<T, K>, Readonly<T>, Record<K, T>) to derive new types
Use union types and intersection types to combine existing types
Always import types explicitly using the import type syntax
Group type imports separately from value imports
Minimize creating local type aliases for imported types

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{css,scss,sass,less,js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)

**/*.{css,scss,sass,less,js,jsx,ts,tsx}: Primary color (#155EEF) should be used for main brand color in buttons, links, and accents
Error color (#F04438) should be used for error states and destructive actions
Success color (#12B76A) should be used for success states and confirmations
Warning color (#F79009) should be used for warnings and important notifications
Info color (#0BA5EC) should be used for informational elements

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

**/*.{tsx,ts}: Use the translation wrapper component and useTranslation hook in components
Ensure all user-facing text is translatable

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{tsx,ts,jsx,js,vue,css,scss,less}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue,css,scss,less}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Primary buttons should be solid with the primary color
Secondary buttons should have a border with transparent or light background
Danger buttons should use the error color
Use consistent padding, border radius, and hover states for all buttons
Follow fixed button sizes based on their importance and context
Use consistent border radius (rounded-lg) for all cards
Apply light shadows (shadow-sm) for card elevation
Maintain consistent padding inside cards (p-4 or p-6)
Use subtle borders for card separation
Ensure proper spacing between card elements
Apply consistent styling to all form inputs
Use clear visual indicators for focus, hover, and error states in form elements
Apply proper spacing between elements using 8px, 16px, 24px increments
Ensure proper alignment of elements (left, center, or right)
Use responsive layouts that work across different device sizes
Maintain a minimum contrast ratio of 4.5:1 for text

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{tsx,ts,jsx,js,vue}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue}: Include appropriate loading states for async actions in buttons
Group related form elements with appropriate spacing
Provide clear validation feedback for forms
Ensure proper labeling and accessibility for form elements
Ensure all interactive elements are keyboard accessible
Include appropriate ARIA attributes for complex components
Provide alternative text for images and icons
Support screen readers with semantic HTML elements

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/08-contributing-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Follow the TypeScript/JavaScript style guidelines
Ensure code is well-tested and documented

Files:

  • apps/api/src/modules/config/app.config.ts
  • apps/api/src/modules/notification/notification.service.ts
apps/api/src/**/*.{controller,service}.ts

📄 CodeRabbit inference engine (.cursor/rules/06-api-structure.mdc)

Implement proper error handling in API modules

Files:

  • apps/api/src/modules/notification/notification.service.ts
🧬 Code graph analysis (1)
apps/api/src/modules/notification/notification.service.ts (1)
apps/api/src/utils/guard.ts (1)
  • guard (36-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build / Build
🔇 Additional comments (5)
apps/api/src/modules/config/app.config.ts (1)

96-98: LGTM!

The new email configuration options are well-structured with sensible defaults. The comment accurately documents the rate limit calculation (2 QPS = 500ms between emails). The implementation follows the existing patterns in the file.

apps/api/src/modules/notification/notification.service.ts (3)

25-28: LGTM!

Configuration initialization correctly uses nullish coalescing (??) for fallback values, following the coding guidelines.


263-273: LGTM!

The sendEmail method now cleanly delegates to sendEmailWithRetry, and the timing log provides useful observability for monitoring email delivery performance.


70-108: The code implementation is correct and will work at runtime.

The guard.retry method exists in apps/api/src/utils/guard.ts (lines 216–257) with the exact signature and chainable API used in the notification service. It returns a GuardWrapper<Promise<T>> with both orThrow() and orElse() methods, supporting retry configuration via RetryConfig including maxAttempts, initialDelay, maxDelay, backoffFactor, retryIf, and onRetry callbacks. The code at lines 70–108 correctly leverages this implementation with exponential backoff for rate limit handling. No runtime errors will occur from the retry mechanism itself.

However, consider: the rate limiting strategy relies on instance-level state (lastEmailSentAt), which provides no protection across multiple service instances in scaled deployments. For distributed systems, centralized rate limiting (Redis, shared cache) is recommended to prevent API quota exhaustion.

apps/api/package.json (1)

132-132: Verify breaking changes in major version bump (4.x → 6.x) for the resend package.

This is a significant major version upgrade. While the imports of ErrorResponse and Attachment types appear compatible, confirm that property access patterns (such as error.name and error.message on ErrorResponse) match the v6 API. Review the resend GitHub releases for detailed changelog and breaking changes between versions 4, 5, and 6.

@mrcfps mrcfps merged commit abc2de1 into main Dec 19, 2025
2 checks passed
@mrcfps mrcfps deleted the feat/enhance-email-send branch December 19, 2025 09:47
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.

3 participants