Add EmailTemplate model and CRUD endpoints#71
Conversation
📝 WalkthroughWalkthroughA new ChangesEmailTemplate CRUD Feature
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
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/services/emailTemplate.service.ts (2)
16-20: ⚖️ Poor tradeoffConsider adding pagination to prevent performance issues.
The
listTemplatesfunction 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
skipandtakeparameters:-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 valueConsider 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
updatewill throwP2025(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 winConsider 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
📒 Files selected for processing (6)
prisma/migrations/20260616135723_email_format_support/migration.sqlprisma/schema.prismasrc/controllers/emailTemplate.controller.tssrc/routes/email.tssrc/routes/index.tssrc/services/emailTemplate.service.ts
Summary by CodeRabbit