Skip to content

Add EmailTemplate model and CRUD endpoints#71

Merged
callofcode07 merged 1 commit into
mainfrom
feature/dynamic-email
Jun 16, 2026
Merged

Add EmailTemplate model and CRUD endpoints#71
callofcode07 merged 1 commit into
mainfrom
feature/dynamic-email

Conversation

@Harish-Naruto

@Harish-Naruto Harish-Naruto commented Jun 16, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features
    • Added email template management system enabling users to create, view, update, and delete customizable email templates
    • Templates support subject lines, HTML content, and optional plain text alternatives
    • Includes automatic audit tracking for template creation and modifications

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

A new EmailTemplate entity is added end-to-end: a Prisma migration and schema model define the table with content and audit fields linked to Member. A service module provides Prisma-backed CRUD with existence checks, a controller exposes five Express handlers with input validation, and an email router registers the endpoints under /email/templates.

Changes

EmailTemplate CRUD Feature

Layer / File(s) Summary
Database schema and Prisma model
prisma/migrations/.../migration.sql, prisma/schema.prisma
Migration creates the EmailTemplate table with name (unique), subject, htmlBody, optional textBody, audit FK columns to Member, timestamps, and associated constraints. Prisma schema adds the EmailTemplate model and back-relations createdEmailTemplates/updatedEmailTemplates on Member.
EmailTemplate service
src/services/emailTemplate.service.ts
Exports createTemplate, listTemplates, getTemplateById, updateTemplate, and deleteTemplate using Prisma. updateTemplate and deleteTemplate perform an existence check via findUnique and throw ApiError("Template not found", 404) before mutating.
Controller, router, and route registration
src/controllers/emailTemplate.controller.ts, src/routes/email.ts, src/routes/index.ts
Controller exports five async handlers with numeric id parsing and required-field validation via ApiError. emailRouter() maps GET/POST/PATCH/DELETE on /email/templates to those handlers with Swagger JSDoc. The main router registers /email with emailRouter().

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant emailRouter as emailRouter (/email/templates)
  participant Controller as emailTemplateController
  participant Service as emailTemplateService
  participant DB as Prisma/Database

  Client->>emailRouter: POST /email/templates {name, subject, htmlBody}
  emailRouter->>Controller: createTemplate(req, res)
  Controller->>Controller: validate required fields (ApiError 400 if missing)
  Controller->>Service: createTemplate(name, subject, htmlBody, textBody?, createdById?)
  Service->>DB: prisma.emailTemplate.create(...)
  DB-->>Service: created record
  Service-->>Controller: created record
  Controller-->>Client: 201 {success: true, data: record}

  Client->>emailRouter: PATCH /email/templates/:id {partial fields}
  emailRouter->>Controller: updateTemplate(req, res)
  Controller->>Controller: parse numeric id (ApiError 400 if invalid)
  Controller->>Service: updateTemplate(id, payload)
  Service->>DB: prisma.emailTemplate.findUnique({id})
  DB-->>Service: record or null
  alt record not found
    Service-->>Controller: throw ApiError("Template not found", 404)
    Controller-->>Client: 404 error
  else record found
    Service->>DB: prisma.emailTemplate.update(...)
    DB-->>Service: updated record
    Service-->>Controller: updated record
    Controller-->>Client: 200 {success: true, data: record}
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

A rabbit hops in with templates to spare,
EmailTemplate tables built fresh with great care,
CRUD routes now hum, from create down to delete,
Audit fields track each edit, tidy and neat.
🐇✉️ New emails await—let the templates compete!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding an EmailTemplate model and its CRUD endpoints across the schema, database, service, controller, and routing layers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/dynamic-email

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
src/services/emailTemplate.service.ts (2)

16-20: ⚖️ Poor tradeoff

Consider adding pagination to prevent performance issues.

The listTemplates function returns all templates without pagination. While this is acceptable for a small number of templates, it could lead to performance and memory issues as the template count grows.

📄 Suggested approach for pagination

Add optional skip and take parameters:

-export const listTemplates = async () => {
-  return await prisma.emailTemplate.findMany({
+export const listTemplates = async (skip?: number, take?: number) => {
+  return await prisma.emailTemplate.findMany({
+    skip,
+    take,
     orderBy: { createdAt: "desc" },
   });
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/emailTemplate.service.ts` around lines 16 - 20, The
listTemplates function currently retrieves all email templates without
pagination, which can cause performance and memory issues as the template count
grows. Modify the listTemplates function to accept optional skip and take
parameters that allow callers to paginate through results. Pass these parameters
to the prisma.emailTemplate.findMany call to limit the number of records
returned in each request.

26-40: 💤 Low value

Consider removing the existence check to avoid TOCTOU race condition.

The existence check at line 36 followed by the update at line 39 creates a time-of-check-to-time-of-use (TOCTOU) race condition. Between the check and the update, another concurrent request could delete the template.

However, this is a common pattern for providing user-friendly error messages, and Prisma's update will throw P2025 (record not found) anyway if the template doesn't exist by the time the update executes. The race window is small and the impact is minimal (just an extra database query before the operation fails).

♻️ Alternative approach (optional)

Remove the existence check and catch Prisma's error:

 export const updateTemplate = async (
   id: number,
   payload: Partial<{
     name: string;
     subject: string;
     htmlBody: string;
     textBody: string;
     updatedById: string;
   }>,
 ) => {
-  const exists = await prisma.emailTemplate.findUnique({ where: { id } });
-  if (!exists) throw new ApiError("Template not found", 404);
-
-  return await prisma.emailTemplate.update({ where: { id }, data: payload });
+  try {
+    return await prisma.emailTemplate.update({ where: { id }, data: payload });
+  } catch (error) {
+    if (error.code === 'P2025') {
+      throw new ApiError("Template not found", 404);
+    }
+    throw error;
+  }
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/emailTemplate.service.ts` around lines 26 - 40, Remove the
TOCTOU race condition in the updateTemplate function by eliminating the separate
existence check. Instead of calling findUnique followed by update, wrap the
prisma.emailTemplate.update call in a try-catch block to handle Prisma's P2025
error (record not found). When catching this error, throw the ApiError with the
same "Template not found" message and 404 status code to maintain the
user-friendly error response while avoiding the race condition window between
the check and update operations.
src/controllers/emailTemplate.controller.ts (1)

22-39: ⚡ Quick win

Consider stricter validation for required fields.

The validation at lines 26-28 checks for truthy values, which means empty strings would pass the check but likely represent invalid input. While the database constraints will eventually reject these, client-side validation would provide better error messages.

🔒 Suggested validation improvement
-  if (!name || !subject || !htmlBody) {
+  if (!name?.trim() || !subject?.trim() || !htmlBody?.trim()) {
     throw new ApiError("name, subject and htmlBody are required", 400);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/controllers/emailTemplate.controller.ts` around lines 22 - 39, The
validation in the createTemplate function uses basic falsy checks which do not
account for whitespace-only strings that could pass through as valid input.
Implement stricter validation for the name, subject, and htmlBody fields by
checking not only for truthy values but also for meaningful content—use .trim()
to remove whitespace and validate that the trimmed strings have actual
content—to ensure that invalid inputs containing only whitespace are caught and
rejected with appropriate error messages before reaching the database layer.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/controllers/emailTemplate.controller.ts`:
- Around line 22-39: The validation in the createTemplate function uses basic
falsy checks which do not account for whitespace-only strings that could pass
through as valid input. Implement stricter validation for the name, subject, and
htmlBody fields by checking not only for truthy values but also for meaningful
content—use .trim() to remove whitespace and validate that the trimmed strings
have actual content—to ensure that invalid inputs containing only whitespace are
caught and rejected with appropriate error messages before reaching the database
layer.

In `@src/services/emailTemplate.service.ts`:
- Around line 16-20: The listTemplates function currently retrieves all email
templates without pagination, which can cause performance and memory issues as
the template count grows. Modify the listTemplates function to accept optional
skip and take parameters that allow callers to paginate through results. Pass
these parameters to the prisma.emailTemplate.findMany call to limit the number
of records returned in each request.
- Around line 26-40: Remove the TOCTOU race condition in the updateTemplate
function by eliminating the separate existence check. Instead of calling
findUnique followed by update, wrap the prisma.emailTemplate.update call in a
try-catch block to handle Prisma's P2025 error (record not found). When catching
this error, throw the ApiError with the same "Template not found" message and
404 status code to maintain the user-friendly error response while avoiding the
race condition window between the check and update operations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c62d6656-5459-491b-bae8-d5cc1a39cb28

📥 Commits

Reviewing files that changed from the base of the PR and between 6cc634b and 0fac966.

📒 Files selected for processing (6)
  • prisma/migrations/20260616135723_email_format_support/migration.sql
  • prisma/schema.prisma
  • src/controllers/emailTemplate.controller.ts
  • src/routes/email.ts
  • src/routes/index.ts
  • src/services/emailTemplate.service.ts

@callofcode07 callofcode07 merged commit 4cc4808 into main Jun 16, 2026
3 checks passed
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.

2 participants