Skip to content

Conversation

@BilalG1
Copy link
Contributor

@BilalG1 BilalG1 commented Dec 12, 2025

Summary by CodeRabbit

  • Refactor

    • Centralized email-theme operations (create/read/list/update/delete) behind a unified handler, simplifying endpoints and improving consistency.
  • New Features

    • Theme updates now validate and render template previews before applying changes; create supports generated or provided IDs.
  • Chores

    • Added a reusable CRUD handler factory to standardize backend data-operation patterns and error handling.

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

@vercel
Copy link

vercel bot commented Dec 12, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
stack-backend Ready Ready Preview, Comment Jan 14, 2026 3:09am
stack-dashboard Ready Ready Preview, Comment Jan 14, 2026 3:09am
stack-demo Ready Ready Preview, Comment Jan 14, 2026 3:09am
stack-docs Ready Ready Preview, Comment Jan 14, 2026 3:09am

@cmux-agent
Copy link

cmux-agent bot commented Dec 12, 2025

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 12, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Centralized CRUD handlers were introduced via a new CUD infrastructure and applied to internal email-theme routes; route handlers now delegate create/read/update/delete operations to typed internalEmailThemesCudHandlers rather than performing inline tenancy/config mutations.

Changes

Cohort / File(s) Summary
CUD Infrastructure
apps/backend/src/route-handlers/cud-handler.tsx
New generic createCudHandlers factory, types (CudHandlers, ParamsSchema, QuerySchema), CudHandlerInvocationError, and programmatic invocation/testing support. Implements per-access handlers and validation/error wrapping.
Email Themes CRUD Implementation
apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
New internalEmailThemesCudHandlers using the CUD factory: defines ThemeItem, list/read helpers, and admin create/update/delete flows that validate IDs, render TSX previews, update environment overrides, and surface structured errors.
Email Themes Routes (list/create)
apps/backend/src/app/api/latest/internal/email-themes/route.tsx
Replaced inline config manipulation with calls to internalEmailThemesCudHandlers.adminList and .adminCreate; response shaping changed to return { id, display_name } from handler outputs.
Email Theme Route (single id)
apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
GET now exported as internalEmailThemesCudHandlers.readHandler; PATCH updated to call internalEmailThemesCudHandlers.adminUpdate, verify post-update presence, and return display_name from the updated item.
Cross-cutting imports/cleanup
.../email-themes/*
Removed direct tenancy/config/prisma/rendering imports from routes, added internalEmailThemesCudHandlers and error/assertion types; simplified handlers to delegate logic to CUD layer.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Route as API Route Handler
    participant Cud as CUD Handlers
    participant Env as Env Config / Overrides

    rect rgb(200,220,255)
    Note over Client,Env: CREATE flow
    Client->>Route: POST /email-themes (display_name)
    Route->>Cud: adminCreate(tenancy, {display_name, id?})
    Cud->>Cud: validate & generate id
    Cud->>Env: apply override (add theme TSX)
    Env-->>Cud: confirmation
    Cud-->>Route: created theme {id, display_name}
    Route-->>Client: 200 + theme
    end

    rect rgb(220,240,220)
    Note over Client,Env: READ flow
    Client->>Route: GET /email-themes or /email-themes/[id]
    Route->>Cud: adminList/adminRead(tenancy, params)
    Cud->>Env: read config/themes
    Env-->>Cud: theme data
    Cud-->>Route: theme(s)
    Route-->>Client: 200 + theme(s)
    end

    rect rgb(255,240,200)
    Note over Client,Env: UPDATE flow
    Client->>Route: PATCH /email-themes/[id] (tsx_source)
    Route->>Cud: adminUpdate(tenancy, params, data)
    Cud->>Cud: validate ids, render preview
    Cud->>Env: apply override (update TSX)
    Env-->>Cud: confirmation
    Cud->>Cud: fetch updated item
    Cud-->>Route: updated theme
    Route-->>Client: 200 + updated theme
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • N2D4

Poem

🐇 I stitched the handlers, neat and quick,
Hops of code that do the trick.
Themes now dance from single source,
No more inline, steady course. 🎉

🚥 Pre-merge checks | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is empty except for a template comment; it lacks any explanation of changes, objectives, or implementation details. Provide a comprehensive description covering what was changed, why these changes were made, and how the new CUD handler factory is used in email themes and other features.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'cud handler' is vague and generic, providing minimal insight into the changeset's purpose or scope. Use a more descriptive title that explains what the CUD handler does or which feature is being implemented (e.g., 'Add CUD handler factory for email themes API').

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 12, 2025

Greptile Overview

Greptile Summary

This PR refactors email theme management to use a new generic CUD (Create/Update/Delete) handler pattern, consolidating CRUD operations into reusable abstractions.

Key Changes:

  • Added apps/backend/src/route-handlers/cud-handler.tsx implementing a generic CUD handler framework with validation, error handling, and multi-access-level support
  • Created apps/backend/src/app/api/latest/internal/email-themes/cud.tsx implementing email theme-specific CRUD operations using the new framework
  • Refactored existing route handlers in [id]/route.tsx and route.tsx to use the new CUD handlers, removing duplicated logic and direct config access

Issues Found:

  • Stale data issue in onUpdate: after updating the database config, the code constructs the return value using auth.tenancy.config which hasn't been refreshed, potentially causing inconsistencies when listing all themes after an update

Confidence Score: 4/5

  • This PR is generally safe to merge with one logical issue that should be addressed
  • The PR introduces a well-structured abstraction for CUD operations with comprehensive validation and error handling. However, there's a stale data issue in the onUpdate function where the tenancy config isn't refreshed after database updates, which could lead to inconsistent return values when listing all themes after an update operation
  • Pay close attention to apps/backend/src/app/api/latest/internal/email-themes/cud.tsx - specifically the stale data issue in the onUpdate function at lines 158-164

Important Files Changed

File Analysis

Filename Score Overview
apps/backend/src/route-handlers/cud-handler.tsx 5/5 Added new generic CUD (Create/Update/Delete) handler framework with comprehensive validation, error handling, and routing logic
apps/backend/src/app/api/latest/internal/email-themes/cud.tsx 4/5 Implemented CUD handlers for email themes with validation and rendering checks; potential stale data issue in onUpdate after config override

Sequence Diagram

sequenceDiagram
    participant Client
    participant RouteHandler
    participant CudHandler
    participant EmailThemesCud
    participant Database
    
    Note over Client,Database: Email Theme Update Flow (PATCH)
    
    Client->>RouteHandler: PATCH /email-themes/:id
    RouteHandler->>CudHandler: adminUpdate(tenancy, id, data)
    CudHandler->>CudHandler: validate params & data
    CudHandler->>EmailThemesCud: onUpdate(auth, params, data)
    
    alt Empty data (Read/List)
        EmailThemesCud->>EmailThemesCud: getThemeOrThrow(tenancy, id)
        EmailThemesCud-->>CudHandler: return theme from tenancy.config
    else Update operation
        EmailThemesCud->>EmailThemesCud: validate tsx_source via renderEmailWithTemplate
        EmailThemesCud->>Database: overrideEnvironmentConfigOverride
        Database-->>EmailThemesCud: config updated
        Note over EmailThemesCud: Uses stale auth.tenancy.config
        EmailThemesCud->>EmailThemesCud: construct updatedThemes from stale config
        EmailThemesCud-->>CudHandler: return items with updated tsx_source
    end
    
    CudHandler->>CudHandler: validate output
    CudHandler-->>RouteHandler: return result
    RouteHandler->>RouteHandler: extract theme by id
    RouteHandler-->>Client: return display_name
    
    Note over Client,Database: Email Theme Create Flow (POST)
    
    Client->>RouteHandler: POST /email-themes
    RouteHandler->>CudHandler: adminCreate(tenancy, data)
    CudHandler->>EmailThemesCud: onCreate(auth, data)
    EmailThemesCud->>Database: overrideEnvironmentConfigOverride
    EmailThemesCud-->>CudHandler: return created id
    CudHandler->>EmailThemesCud: onUpdate with create-forward data
    EmailThemesCud-->>CudHandler: return created theme
    CudHandler-->>RouteHandler: return result
    RouteHandler-->>Client: return id
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@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/backend/src/route-handlers/cud-handler.tsx (1)

323-378: Consider using an ES6 Map for direct calls accumulation.

Per coding guidelines, prefer ES6 Maps over records where possible. The directCalls array is later spread into Object.fromEntries, but the overall pattern could benefit from using a Map for clarity.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3137d2 and a348b01.

📒 Files selected for processing (4)
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx (3 hunks)
  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx (1 hunks)
  • apps/backend/src/app/api/latest/internal/email-themes/route.tsx (3 hunks)
  • apps/backend/src/route-handlers/cud-handler.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Always add new E2E tests when changing the API or SDK interface
For blocking alerts and errors, never use toast; use alerts instead as they are less easily missed by the user
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error); use loading indicators and async callbacks instead, or use runAsynchronously/runAsynchronouslyWithAlert for error handling
Use ES6 maps instead of records wherever you can

Files:

  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
  • apps/backend/src/route-handlers/cud-handler.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/route.tsx
**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,css}: Keep hover/click transitions snappy and fast; avoid fade-in delays on hover. Apply transitions after action completion instead, like smooth fade-out when hover ends
Use hover-exit transitions instead of hover-enter transitions; for example, use 'transition-colors hover:transition-none' instead of fade-in on hover

Files:

  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
  • apps/backend/src/route-handlers/cud-handler.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/route.tsx
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if you can avoid them; prefer using client components and hooks like usePathname instead of await params to keep pages static

Files:

  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
  • apps/backend/src/route-handlers/cud-handler.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/route.tsx
{.env*,**/*.{ts,tsx,js}}

📄 CodeRabbit inference engine (AGENTS.md)

Prefix environment variables with STACK_ (or NEXT_PUBLIC_STACK_ if public) so changes are picked up by Turborepo and improves readability

Files:

  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
  • apps/backend/src/route-handlers/cud-handler.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/route.tsx
apps/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Check existing apps for inspiration when implementing new apps or pages; update apps-frontend.tsx and apps-config.ts to add new apps

Files:

  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
  • apps/backend/src/route-handlers/cud-handler.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/route.tsx
🧬 Code graph analysis (1)
apps/backend/src/app/api/latest/internal/email-themes/route.tsx (2)
apps/backend/src/app/api/latest/internal/email-themes/cud.tsx (1)
  • internalEmailThemesCudHandlers (68-203)
packages/stack-shared/src/utils/errors.tsx (1)
  • StatusError (152-261)
🪛 Biome (2.1.2)
apps/backend/src/route-handlers/cud-handler.tsx

[error] 134-134: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ 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). (11)
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: all-good
  • GitHub Check: Vercel Agent Review
  • GitHub Check: docker
  • GitHub Check: setup-tests
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: restart-dev-and-test
🔇 Additional comments (15)
apps/backend/src/app/api/latest/internal/email-themes/route.tsx (2)

8-44: LGTM!

The POST handler correctly delegates theme creation to the centralized CUD handler, generating a UUID upfront and passing the necessary tenancy context and error types.


46-83: LGTM!

The GET handler properly uses adminList and maps the result items to the expected response shape, filtering to only include id and display_name fields.

apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx (2)

6-42: LGTM!

The GET handler correctly delegates to adminRead and returns the theme data with the proper field names.


44-89: LGTM!

The PATCH handler properly wraps the update data in an array as expected by the batch update API and includes defensive error handling if the updated theme cannot be found in the result.

apps/backend/src/app/api/latest/internal/email-themes/cud.tsx (4)

15-35: LGTM!

The helper types and functions are well-structured. getThemeOrThrow properly uses Object.prototype.hasOwnProperty.call to safely check for theme existence.


37-66: LGTM!

The CRUD schema definition is well-organized with appropriate field types and hidden documentation flags.


72-87: LGTM!

The onCreate handler correctly generates a UUID if not provided and initializes the theme with LightEmailTheme as the default TSX source.


177-202: LGTM!

The onDelete handler properly validates theme existence before deletion and returns the remaining themes after the operation.

apps/backend/src/route-handlers/cud-handler.tsx (7)

1-44: LGTM!

The imports and type definitions are well-structured, providing clear typing for batch operations and CUD route handlers.


126-138: Static analysis false positive - .when() conditional is valid Yup syntax.

The Biome warning about "Do not add then to an object" is a false positive. The then here is part of Yup's .when() conditional validation API, not a property being added to a plain object. The schema correctly defines conditional pagination requirements.


93-159: LGTM!

The createCudHandlers function signature and helper functions (getBatchSchema, getListOutputSchema, getSchemas) are well-designed. The lazy batch schema properly handles both single items and arrays.


161-259: LGTM!

The operations mapping and access type filtering logic is sound. The invokeList and invokeRead functions properly validate params, handle the prepare hook, and route to the appropriate operation handlers.


366-371: Verify allowedErrorTypes check handles inheritance correctly.

The instanceof check works for direct instances, but ensure that subclasses of allowed error types are also properly handled if that's the intended behavior.


388-415: LGTM!

The CudHandlerInvocationError class provides clear error wrapping with cause chain, and the validate function properly handles Yup validation errors with descriptive messages.


417-589: Good test coverage for CUD handler operations.

The inline vitest tests cover list, read, update, create, and delete operations with proper assertions on call counts and response shapes.

@BilalG1 BilalG1 requested a review from N2D4 December 13, 2025 01:28
Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@apps/backend/src/app/api/latest/internal/email-themes/cud.tsx`:
- Around line 179-194: The onDelete handler currently sends the whole
emails.themes object to overrideEnvironmentConfigOverride; change it to delete
each theme using dot-notation keys consistent with onCreate/onUpdate: for each
id validated by getThemeOrThrow in onDelete, call
overrideEnvironmentConfigOverride with environmentConfigOverrideOverride mapping
"emails.themes.${id}" to null (or the API's deletion sentinel) instead of
sending the full themes object, mirroring the pattern used in onCreate/onUpdate
to avoid the inconsistent update style.
♻️ Duplicate comments (1)
apps/backend/src/app/api/latest/internal/email-themes/cud.tsx (1)

160-166: Stale tenancy data after config persistence - fragile pattern.

After overrideEnvironmentConfigOverride persists changes to the database (lines 151-158), the code still reads from the in-memory auth.tenancy.config.emails.themes which hasn't been refreshed. The merge logic at lines 162-165 partially compensates by overlaying the new tsxSource values, but this is fragile.

The current approach works because you're manually merging the updated values, but it's error-prone if more fields need updating in the future.

Consider building response purely from resolvedUpdates
-    const updatedThemes = {
-      ...auth.tenancy.config.emails.themes,
-      ...Object.fromEntries(resolvedUpdates.map((u) => {
-        const current = getThemeOrThrow(auth.tenancy, u.id!);
-        return [u.id!, { ...current, tsxSource: u.tsx_source }];
-      })),
-    };
-
-    const items: ThemeRead[] = typedEntries(updatedThemes).map(([id, theme]) => ({
-      id,
-      display_name: theme.displayName,
-      tsx_source: theme.tsxSource,
-    }));
+    // Capture display names before persisting (from stale tenancy)
+    const displayNames = new Map(resolvedUpdates.map((u) => {
+      const current = getThemeOrThrow(auth.tenancy, u.id!);
+      return [u.id!, current.displayName] as const;
+    }));
+
+    // Build response from the updates we just applied
+    const updatedItems: ThemeRead[] = resolvedUpdates.map((u) => ({
+      id: u.id!,
+      display_name: displayNames.get(u.id!) ?? '',
+      tsx_source: u.tsx_source,
+    }));
+
+    // For list operations, include unchanged themes from stale tenancy
+    const unchangedItems: ThemeRead[] = paramsId ? [] : typedEntries(auth.tenancy.config.emails.themes)
+      .filter(([id]) => !resolvedUpdates.some((u) => u.id === id))
+      .map(([id, theme]) => ({
+        id,
+        display_name: theme.displayName,
+        tsx_source: theme.tsxSource,
+      }));
+
+    const items = [...updatedItems, ...unchangedItems];
🧹 Nitpick comments (5)
apps/backend/src/route-handlers/cud-handler.tsx (4)

129-136: Biome error: then property on object can cause issues with Promise-like behavior.

The .when('is_paginated', { ... then: (schema) => ... }) syntax triggers a Biome lint error because adding a then property to an object can cause it to be treated as a thenable, leading to unexpected behavior in async contexts.

This is a yup-specific API pattern, so the warning is a false positive in this context. However, consider suppressing it explicitly to document the intentional usage.

Suppress the lint warning
     pagination: yupObject({
       next_cursor: yupString().nullable().defined().meta({ openapiField: { description: "The cursor to fetch the next page of results. null if there is no next page.", exampleValue: 'b3d396b8-c574-4c80-97b3-50031675ceb2' } }),
+    // biome-ignore lint/suspicious/noThenProperty: yup's .when() API uses `then` as a key, not as Promise thenable
     }).when('is_paginated', {
       is: true,
       then: (schema) => schema.defined(),
       otherwise: (schema) => schema.optional(),
     }),

335-371: Consider reducing any usage in makeDirectInvoke.

The destructured parameters and casts use any extensively without explanatory comments. Per coding guidelines, any should be accompanied by a comment explaining why it's necessary.

Add comments explaining `any` usage
 const makeDirectInvoke = (entry: (typeof accessTypeEntries) extends Map<unknown, infer V> ? V : never, accessType: "client" | "server" | "admin", directOperation: CudOperation) => {
+  // Using `any` here because the function signature is dynamically generated based on operation type
+  // and access level. Type safety is enforced at the schema validation layer via yup.
   return async ({ user, project, branchId, tenancy, data, query, allowedErrorTypes, ...params }: any) => {

384-384: Return type cast to any loses type safety.

The as any cast at the end of createCudHandlers bypasses TypeScript's type checking for the returned object. While the complex type signature makes this difficult to avoid, consider adding a comment explaining the limitation.

-  ) as any;
+  // Type assertion needed because Object.fromEntries loses the precise typing
+  // of the dynamically constructed handler map. Type safety is enforced via
+  // the CudHandlers<...> return type annotation.
+  ) as any;

513-518: Test helper uses type assertion without tenancy verification.

The makeAuth helper casts incomplete mock objects to any, which could mask issues if the real SmartRequestAuth shape changes.

Consider using a more complete mock or Partial<SmartRequestAuth> with explicit type assertions only where necessary.

apps/backend/src/app/api/latest/internal/email-themes/cud.tsx (1)

90-111: The onUpdate function handles multiple responsibilities.

This function handles three distinct cases: listing (empty data), create-forwarding (data with display_name), and actual updates. While functional, this overloading makes the logic harder to follow and maintain.

Consider documenting these cases with comments, or refactoring to separate concerns if the CUD handler design allows it.

 onUpdate: async ({ auth, params, data }) => {
   const paramsId = params.id;

+  // Case 1: Read/List operation (no update data)
   if (data.length === 0) {
     if (paramsId) {
       const theme = getThemeOrThrow(auth.tenancy, paramsId);
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a348b01 and 98a5cca.

📒 Files selected for processing (3)
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
  • apps/backend/src/route-handlers/cud-handler.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never use toast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. Use runAsynchronously or runAsynchronouslyWithAlert instead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g., transition-colors hover:transition-none)
Don't use Date.now() for measuring elapsed (real) time; instead use performance.now()
Use ES6 maps instead of records wherever possible

Files:

  • apps/backend/src/route-handlers/cud-handler.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., use usePathname instead of await params)
Code defensively using ?? throwErr(...) instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid the any type. When using any, leave a comment explaining why and how the type system fails or how errors would still be caught

Files:

  • apps/backend/src/route-handlers/cud-handler.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
{.env*,**/*.{ts,tsx,js,jsx}}

📄 CodeRabbit inference engine (AGENTS.md)

All environment variables should be prefixed with STACK_ (or NEXT_PUBLIC_STACK_ if public) to ensure Turborepo picks up changes and improve readability

Files:

  • apps/backend/src/route-handlers/cud-handler.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
🧠 Learnings (2)
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to packages/stack-shared/src/config/schema.ts : Whenever making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`

Applied to files:

  • apps/backend/src/app/api/latest/internal/email-themes/cud.tsx
📚 Learning: 2026-01-08T20:30:36.983Z
Learnt from: nams1570
Repo: stack-auth/stack-auth PR: 1091
File: apps/backend/src/lib/email-rendering.tsx:49-54
Timestamp: 2026-01-08T20:30:36.983Z
Learning: In apps/backend/src/lib/email-rendering.tsx, the nodeModules constant intentionally pins React and react-dom to version 19.1.1 (rather than matching the project's React version) because pinning provides more reliability for email rendering in the sandbox environment.

Applied to files:

  • apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx
🧬 Code graph analysis (2)
apps/backend/src/route-handlers/cud-handler.tsx (8)
packages/stack-shared/src/crud.tsx (2)
  • CrudTypeOf (117-121)
  • CrudSchema (55-68)
apps/backend/src/route-handlers/smart-request.tsx (1)
  • SmartRequestAuth (23-30)
packages/stack-shared/src/interface/crud/users.ts (1)
  • UsersCrud (111-111)
packages/stack-shared/src/interface/crud/projects.ts (1)
  • ProjectsCrud (194-194)
apps/backend/src/lib/tenancies.tsx (2)
  • Tenancy (53-53)
  • getSoleTenancyFromProjectBranch (65-72)
packages/stack-shared/src/schema-fields.ts (6)
  • yupArray (213-216)
  • yupObject (247-251)
  • yupBoolean (195-198)
  • yupString (187-190)
  • yupNumber (191-194)
  • yupValidate (120-169)
packages/stack-shared/src/utils/errors.tsx (1)
  • StackAssertionError (69-85)
packages/stack-shared/src/utils/arrays.tsx (1)
  • typedIncludes (3-5)
apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx (3)
apps/backend/src/app/api/latest/internal/email-themes/route.tsx (1)
  • GET (46-83)
apps/backend/src/app/api/latest/internal/email-themes/cud.tsx (1)
  • internalEmailThemesCudHandlers (58-205)
packages/stack-shared/src/utils/errors.tsx (2)
  • StatusError (152-261)
  • StackAssertionError (69-85)
🪛 Biome (2.1.2)
apps/backend/src/route-handlers/cud-handler.tsx

[error] 133-133: Do not add then to an object.

(lint/suspicious/noThenProperty)

⏰ 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). (13)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: build (22.x)
  • GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: all-good
  • GitHub Check: build (22.x)
  • GitHub Check: setup-tests
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: docker
  • GitHub Check: setup-tests-with-custom-base-port
🔇 Additional comments (7)
apps/backend/src/route-handlers/cud-handler.tsx (2)

364-369: Error wrapping logic may suppress useful error types.

The catch block wraps errors in CudHandlerInvocationError unless they match allowedErrorTypes or are StackAssertionError. This could hide important error context from callers who expect specific error types not in their allowed list.

Consider whether other common error types (e.g., KnownErrors.*) should also propagate unwrapped by default, or document this behavior clearly for consumers.


92-103: Good implementation of a centralized CUD handler factory.

The createCudHandlers function provides a well-structured abstraction for generating CRUD handlers with consistent validation, access control, and response formatting. The type parameterization enables strong typing for consumers.

apps/backend/src/app/api/latest/internal/email-themes/[id]/route.tsx (2)

6-6: Good simplification of GET handler.

Directly exporting internalEmailThemesCudHandlers.readHandler is cleaner than wrapping it in a custom handler. This addresses the previous review feedback.


31-44: Post-update verification is defensive but could indicate a design gap.

The pattern of calling adminUpdate then searching for the updated item in the result suggests the CUD handler's update response format may not be ideal for single-item updates. Consider whether the CUD handler could return the single updated item directly for single-item operations.

That said, the current implementation is safe and handles the edge case appropriately.

apps/backend/src/app/api/latest/internal/email-themes/cud.tsx (3)

74-88: onCreate implementation looks correct.

Generates UUID if not provided, persists the new theme with default LightEmailTheme source, and returns the ID. The use of overrideEnvironmentConfigOverride with dot-notation path is appropriate.


139-149: Good validation pattern: render templates before persisting.

Validating each TSX source by calling renderEmailWithTemplate before persisting changes prevents invalid templates from being saved. This is a solid defensive approach.


21-35: Helper functions are clean and focused.

listThemes and getThemeOrThrow provide clear abstractions for reading theme data from tenancy config. The 404 error for missing themes is appropriate.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@BilalG1 BilalG1 requested a review from N2D4 January 14, 2026 03:41
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Jan 14, 2026
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