auth and db sync#3
Conversation
📝 WalkthroughWalkthroughThis PR adds Inngest event-driven infrastructure to synchronize users from Clerk authentication events into the database. It introduces a User model, configures Inngest with event handlers for user creation/deletion, and integrates CORS and Inngest middleware into the Express server with required environment variables. Changes
Sequence DiagramsequenceDiagram
participant Clerk as Clerk (Auth)
participant Inngest as Inngest (Event Hub)
participant Express as Express Server
participant DB as MongoDB
Clerk->>Inngest: Trigger user.created event
Inngest->>Express: Route to /api/inngest
Express->>Inngest: Process syncUser handler
Inngest->>DB: Create new User document
DB-->>Inngest: User saved with clerkId
Clerk->>Inngest: Trigger user.deleted event
Inngest->>Express: Route to /api/inngest
Express->>Inngest: Process deleteUserFromDB handler
Inngest->>DB: Delete User by clerkId
DB-->>Inngest: User removed
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
backend/src/lib/inngest.js (1)
23-31: Consider adding error handling for database operations.The
deleteOneoperation doesn't verify if a user was actually deleted. If the user doesn't exist in the database (e.g., sync failed during creation), this silently succeeds.💡 Optional: Add verification and logging
async ({ event }) => { await connectDB(); const { id } = event.data; - await User.deleteOne({ clerkId: id }); + const result = await User.deleteOne({ clerkId: id }); + if (result.deletedCount === 0) { + console.warn(`User with clerkId ${id} not found for deletion`); + } },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/src/lib/inngest.js` around lines 23 - 31, The deleteUserFromDB function using inngest.createFunction should add error handling and verify the delete result: wrap the connectDB and User.deleteOne call in try/catch, catch and log any DB errors (including the event.data.id) using your logger, and after User.deleteOne({ clerkId: id }) check the returned result (e.g., deletedCount or acknowledged) to detect when no document was removed and log a warning or take corrective action; ensure the handler still fails or signals an error appropriately when the DB operation throws.backend/src/lib/env.js (1)
9-13: Consider adding validation for required environment variables.These environment variables (especially
CLIENT_URLandINNGEST_SIGNING_KEY) are critical for the application to function correctly. If they're missing, the app will fail at runtime with unclear errors. Consider adding startup validation.💡 Optional: Add validation helper
const required = (key) => { const value = process.env[key]; if (!value) throw new Error(`Missing required env var: ${key}`); return value; }; export const ENV = { PORT: process.env.PORT, DB_URL: required("DB_URL"), NODE_ENV: process.env.NODE_ENV, CLIENT_URL: required("CLIENT_URL"), // ... };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/src/lib/env.js` around lines 9 - 13, Add startup validation by introducing a small helper function (e.g., required(key)) and use it when constructing the ENV object so critical vars like CLIENT_URL and INNGEST_SIGNING_KEY (and any other mandatory keys such as DB_URL or INNGEST_EVENT_KEY) are validated at module load; the required helper should read process.env[key] and throw a clear Error(`Missing required env var: ${key}`) when absent, and then replace direct uses of process.env.CLIENT_URL and process.env.INNGEST_SIGNING_KEY in ENV with required("CLIENT_URL") and required("INNGEST_SIGNING_KEY") respectively to fail fast with a clear message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@backend/src/lib/inngest.js`:
- Line 3: The import path for the User model in inngest.js is incorrect; update
the import that references User so it points to the actual module location
(backend/src/models/User.js) instead of ./models/User.js — locate the import
statement importing User in inngest.js and change its path to the correct
relative path to the models folder so the User symbol resolves correctly.
- Around line 12-18: The code is destructuring the wrong Clerk payload field and
lacks validation: replace uses of event.data.email_address with
event.data.email_addresses (an array) when building newUser (the
clerkId/email/name/profileImage object) and extract the primary email as
email_addresses[0]?.email_address (or find the verified one); then add
validation before returning/creating the user to ensure newUser.email is
non-empty and newUser.name falls back to a safe value (e.g., use first_name or
last_name individually or a placeholder) and fail fast with a clear error if
email is missing so the User creation won’t silently break.
In `@backend/src/server.js`:
- Line 7: The import currently only brings in inngest but the exported array
functions from the module is referenced later (e.g., on line 19); update the
import statement to also import functions from "./lib/inngest.js" (ensure the
named export matches the exported symbol in that module) so the referenced
functions identifier is defined at runtime.
- Line 17: The CORS setup uses ENV.CLIENT_URL directly which can be undefined;
update the app.use(cors(...)) configuration (the call where app.use and cors are
used) to validate ENV.CLIENT_URL and provide a safe fallback for local/dev (or
use a dynamic origin function) so origin never becomes undefined—e.g., check
ENV.CLIENT_URL and set a default like a localhost dev URL or a function that
conditionally allows the origin; ensure this change is applied where
app.use(cors({ origin: ENV.CLIENT_URL, credentials: true })) is defined.
---
Nitpick comments:
In `@backend/src/lib/env.js`:
- Around line 9-13: Add startup validation by introducing a small helper
function (e.g., required(key)) and use it when constructing the ENV object so
critical vars like CLIENT_URL and INNGEST_SIGNING_KEY (and any other mandatory
keys such as DB_URL or INNGEST_EVENT_KEY) are validated at module load; the
required helper should read process.env[key] and throw a clear Error(`Missing
required env var: ${key}`) when absent, and then replace direct uses of
process.env.CLIENT_URL and process.env.INNGEST_SIGNING_KEY in ENV with
required("CLIENT_URL") and required("INNGEST_SIGNING_KEY") respectively to fail
fast with a clear message.
In `@backend/src/lib/inngest.js`:
- Around line 23-31: The deleteUserFromDB function using inngest.createFunction
should add error handling and verify the delete result: wrap the connectDB and
User.deleteOne call in try/catch, catch and log any DB errors (including the
event.data.id) using your logger, and after User.deleteOne({ clerkId: id })
check the returned result (e.g., deletedCount or acknowledged) to detect when no
document was removed and log a warning or take corrective action; ensure the
handler still fails or signals an error appropriately when the DB operation
throws.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 7c8d7675-572c-44ba-819b-94685e17cd4a
⛔ Files ignored due to path filters (1)
backend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
backend/package.jsonbackend/src/lib/env.jsbackend/src/lib/inngest.jsbackend/src/models/User.jsbackend/src/server.js
| @@ -0,0 +1,33 @@ | |||
| import { Inngest } from "inngest"; | |||
| import { connectDB } from "./db.js"; | |||
| import User from "./models/User.js"; | |||
There was a problem hiding this comment.
Incorrect import path - module will not be found.
The file is located at backend/src/lib/inngest.js, so the import path ./models/User.js resolves to backend/src/lib/models/User.js, which doesn't exist. The User model is at backend/src/models/User.js.
🐛 Fix the import path
-import User from "./models/User.js";
+import User from "../models/User.js";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import User from "./models/User.js"; | |
| import User from "../models/User.js"; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/src/lib/inngest.js` at line 3, The import path for the User model in
inngest.js is incorrect; update the import that references User so it points to
the actual module location (backend/src/models/User.js) instead of
./models/User.js — locate the import statement importing User in inngest.js and
change its path to the correct relative path to the models folder so the User
symbol resolves correctly.
| const { id, email_address, first_name, last_name, image_url } = event.data; | ||
| const newUser = { | ||
| clerkId: id, | ||
| email: email_address[0]?.email_address, | ||
| name: `${first_name || ""} ${last_name || ""}`, | ||
| profileImage: image_url, | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Clerk webhook user.created event payload structure email_addresses field
💡 Result:
In a Clerk user.created webhook, data is the full User object, and email_addresses is an array of email address objects. Each entry looks like this (fields shown in Clerk’s example payload): [1]
"email_addresses": [
{
"email_address": "example@example.org",
"id": "idn_29w83yL7CwVlJXylYLxcslromF1",
"linked_to": [],
"object": "email_address",
"verification": {
"status": "verified",
"strategy": "ticket"
}
}
]The user object also includes primary_email_address_id, which references the id of the primary entry in email_addresses. [1]
Source
[1] Clerk Docs — Webhooks overview (example user.created payload) (clerk.com)
Citations:
🏁 Script executed:
cat -n backend/src/lib/inngest.js | head -30Repository: realACO/CodeMeet
Length of output: 1127
Fix critical Clerk webhook payload field name and add validation.
The code destructures email_address (singular), but Clerk's webhook payload provides email_addresses (plural array). This will cause email_address to be undefined, and the email extraction will fail.
Additionally, the code lacks validation for empty email addresses or missing name fields, which could cause User schema validation to fail silently.
Proposed fix
async ({ event }) => {
await connectDB();
- const { id, email_address, first_name, last_name, image_url } = event.data;
+ const { id, email_addresses, first_name, last_name, image_url } = event.data;
+
+ const primaryEmail = email_addresses?.[0]?.email_address;
+ if (!primaryEmail) {
+ throw new Error(`No email found for user ${id}`);
+ }
+
+ const fullName = [first_name, last_name].filter(Boolean).join(" ") || "Unknown";
+
const newUser = {
clerkId: id,
- email: email_address[0]?.email_address,
- name: `${first_name || ""} ${last_name || ""}`,
+ email: primaryEmail,
+ name: fullName,
profileImage: image_url,
};
await User.create(newUser);
},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/src/lib/inngest.js` around lines 12 - 18, The code is destructuring
the wrong Clerk payload field and lacks validation: replace uses of
event.data.email_address with event.data.email_addresses (an array) when
building newUser (the clerkId/email/name/profileImage object) and extract the
primary email as email_addresses[0]?.email_address (or find the verified one);
then add validation before returning/creating the user to ensure newUser.email
is non-empty and newUser.name falls back to a safe value (e.g., use first_name
or last_name individually or a placeholder) and fail fast with a clear error if
email is missing so the User creation won’t silently break.
| import { connectDB } from "./lib/db.js"; | ||
| import cors from "cors"; | ||
| import { serve } from "inngest/express"; | ||
| import { inngest } from "./lib/inngest.js"; |
There was a problem hiding this comment.
Missing import: functions is not imported but used on line 19.
The functions array is exported from ./lib/inngest.js but is not included in the import statement. This will cause a ReferenceError: functions is not defined at server startup.
🐛 Fix the import
-import { inngest } from "./lib/inngest.js";
+import { inngest, functions } from "./lib/inngest.js";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { inngest } from "./lib/inngest.js"; | |
| import { inngest, functions } from "./lib/inngest.js"; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/src/server.js` at line 7, The import currently only brings in inngest
but the exported array functions from the module is referenced later (e.g., on
line 19); update the import statement to also import functions from
"./lib/inngest.js" (ensure the named export matches the exported symbol in that
module) so the referenced functions identifier is defined at runtime.
| app.use(express.json()); | ||
|
|
||
| //credentials true meaning server allows browser to include cookies on request | ||
| app.use(cors({ origin: ENV.CLIENT_URL, credentials: true })); |
There was a problem hiding this comment.
CORS origin will be undefined if CLIENT_URL is not set.
If ENV.CLIENT_URL is not defined, the CORS middleware will have origin: undefined, which may not behave as expected (could block all cross-origin requests or allow all, depending on the cors library version).
Consider adding a fallback or validation for development environments:
💡 Suggested improvement
-app.use(cors({ origin: ENV.CLIENT_URL, credentials: true }));
+app.use(cors({
+ origin: ENV.CLIENT_URL || "http://localhost:5173",
+ credentials: true
+}));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| app.use(cors({ origin: ENV.CLIENT_URL, credentials: true })); | |
| app.use(cors({ | |
| origin: ENV.CLIENT_URL || "http://localhost:5173", | |
| credentials: true | |
| })); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/src/server.js` at line 17, The CORS setup uses ENV.CLIENT_URL
directly which can be undefined; update the app.use(cors(...)) configuration
(the call where app.use and cors are used) to validate ENV.CLIENT_URL and
provide a safe fallback for local/dev (or use a dynamic origin function) so
origin never becomes undefined—e.g., check ENV.CLIENT_URL and set a default like
a localhost dev URL or a function that conditionally allows the origin; ensure
this change is applied where app.use(cors({ origin: ENV.CLIENT_URL, credentials:
true })) is defined.
Summary by CodeRabbit