-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Make clickId required in SDK
#2820
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a clickId field to the POST /api/track/lead route schema (route-level accepts nullish), normalizes clickId to a non-null string before calling trackLead, tightens the exported Zod schema to require a trimmed string for clickId, and updates a comment in the track-lead conversion file to reflect empty-string semantics. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant API as POST /api/track/lead
participant Converter as trackLead()
Client->>API: POST { ..., clickId?: string|null|undefined }
note right of API #8fbf8f: Route-level schema accepts nullish\nthen normalizes to non-null string
API->>API: clickId = clickId ?? ""
API->>Converter: trackLead({ clickId: "", ... }) when missing/null
alt clickId == ""
Converter-->>API: handle empty-clickId lookup/path
else clickId != ""
Converter-->>API: proceed with provided clickId
end
API-->>Client: Response
Estimated code review effortπ― 2 (Simple) | β±οΈ ~10 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks (2 passed, 1 warning)β Failed checks (1 warning)
β Passed checks (2 passed)
Poem
π Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro π Files selected for processing (1)
π§ Files skipped from review as they are similar to previous changes (1)
β° 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). (2)
β¨ Finishing Touches
π§ͺ Generate unit tests
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
π§Ή Nitpick comments (1)
apps/web/lib/api/conversions/track-lead.ts (1)
52-60: Align error message with new "empty string sentinel" semanticsThe guard now explicitly treats empty string as "missing"; update the error text to avoid confusion.
- "The `clickId` property was not provided in the request, and no existing customer with the provided `customerExternalId` was found.", + "The `clickId` was empty or missing, and no existing customer with the provided `customerExternalId` was found.",
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (3)
apps/web/app/(ee)/api/track/lead/route.ts(2 hunks)apps/web/lib/api/conversions/track-lead.ts(1 hunks)apps/web/lib/zod/schemas/leads.ts(1 hunks)
β° 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). (2)
- GitHub Check: Vade Review
- GitHub Check: build
π Additional comments (1)
apps/web/lib/zod/schemas/leads.ts (1)
8-13: LGTM: clickId now required with empty-string sentinelThe description matches the new contract. Ensure callers that still pass nullish values coerce/trim to "" before invoking business logic.
If your API docs are generated from this schema, theyβll show clickId as required. Please confirm your docs generator uses this file (and not the route-local schema that marks it nullish). If unsure, I can help verify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional Comments:
apps/web/app/(ee)/api/track/lead/client/route.ts (lines 40-40):
The client route will now reject requests with missing or null clickId values, breaking backwards compatibility with existing integrations.
View Details
π Patch Details
diff --git a/apps/web/app/(ee)/api/track/lead/client/route.ts b/apps/web/app/(ee)/api/track/lead/client/route.ts
index 03ad1093c..588bfd2cb 100644
--- a/apps/web/app/(ee)/api/track/lead/client/route.ts
+++ b/apps/web/app/(ee)/api/track/lead/client/route.ts
@@ -9,6 +9,7 @@ import { parseRequestBody } from "@/lib/api/utils";
import { withPublishableKey } from "@/lib/auth/publishable-key";
import { trackLeadRequestSchema } from "@/lib/zod/schemas/leads";
import { NextResponse } from "next/server";
+import { z } from "zod";
// POST /api/track/lead/client β Track a lead conversion event on the client side
export const POST = withPublishableKey(
@@ -37,10 +38,17 @@ export const POST = withPublishableKey(
customerAvatar,
mode,
metadata,
- } = trackLeadRequestSchema.parse(body);
+ } = trackLeadRequestSchema
+ .extend({
+ // we if clickId is undefined/nullish, we'll coerce into an empty string
+ clickId: z.string().nullish(),
+ // add backwards compatibility
+ customerExternalId: z.string().nullish(),
+ })
+ .parse(body);
const response = await trackLead({
- clickId,
+ clickId: clickId ?? "",
eventName,
eventQuantity,
customerExternalId,
Analysis
API Route Inconsistency: clickId Field Validation Bug
Summary
A backwards compatibility issue exists between two lead tracking API routes where the /api/track/lead/client endpoint rejects requests with null or missing clickId values that the main /api/track/lead endpoint accepts.
Technical Details
Root Cause
The issue stems from inconsistent schema validation patterns:
- Main route (
/api/track/lead/route.ts): Uses an extended schema that allowsclickIdto benullish()for backwards compatibility - Client route (
/api/track/lead/client/route.ts): Uses the basetrackLeadRequestSchemadirectly, which requiresclickIdas a non-null string
Schema Validation Behavior
The base trackLeadRequestSchema defines clickId as:
clickId: z.string().trim()This requires a string value and rejects null, undefined, or missing values.
The main route extends this schema:
trackLeadRequestSchema.extend({
clickId: z.string().nullish(),
// other backwards compatibility fields...
})This allows null and undefined values, which are then coerced to empty strings.
Validation Evidence
Testing confirmed the inconsistency:
- Base schema (client route): β Rejects
nullclickId with "Expected string, received null" - Base schema (client route): β Rejects missing clickId with "Required"
- Extended schema (main route): β
Accepts both
nulland missing clickId values
Impact
Breaking Change for Client Integrations: Existing client applications using /api/track/lead/client that previously omitted the clickId field or sent null values will now receive validation errors, potentially breaking their lead tracking functionality.
API Inconsistency: The same request payload behaves differently across the two endpoints, violating the principle of least surprise and creating confusion for developers.
Solution
Applied the same schema extension pattern used in the main route to the client route:
-
Added Zod import:
import { z } from "zod"; -
Extended the schema: Applied the same
.extend()pattern withclickId: z.string().nullish() -
Added null coercion: Convert null/undefined
clickIdvalues to empty strings:clickId: clickId ?? ""
This ensures both routes handle clickId consistently while maintaining backwards compatibility with existing integrations.
References
- Zod Schema Extension Documentation - Confirms
.extend()is the correct pattern for schema modification - API Backwards Compatibility Best Practices - Validates that maintaining field compatibility across endpoints is essential
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional Comments:
apps/web/app/(ee)/api/track/lead/client/route.ts (lines 40-40):
The client route is broken because it uses the base schema that now requires clickId, but the intent is to make clickId optional.
View Details
π Patch Details
diff --git a/apps/web/app/(ee)/api/track/lead/client/route.ts b/apps/web/app/(ee)/api/track/lead/client/route.ts
index 03ad1093c..0d4b3c857 100644
--- a/apps/web/app/(ee)/api/track/lead/client/route.ts
+++ b/apps/web/app/(ee)/api/track/lead/client/route.ts
@@ -9,6 +9,7 @@ import { parseRequestBody } from "@/lib/api/utils";
import { withPublishableKey } from "@/lib/auth/publishable-key";
import { trackLeadRequestSchema } from "@/lib/zod/schemas/leads";
import { NextResponse } from "next/server";
+import { z } from "zod";
// POST /api/track/lead/client β Track a lead conversion event on the client side
export const POST = withPublishableKey(
@@ -37,10 +38,15 @@ export const POST = withPublishableKey(
customerAvatar,
mode,
metadata,
- } = trackLeadRequestSchema.parse(body);
+ } = trackLeadRequestSchema
+ .extend({
+ // we if clickId is undefined/nullish, we'll coerce into an empty string
+ clickId: z.string().trim().nullish(),
+ })
+ .parse(body);
const response = await trackLead({
- clickId,
+ clickId: clickId ?? "",
eventName,
eventQuantity,
customerExternalId,
Analysis
API Inconsistency: Client Route Requires Optional clickId Field
Bug Summary
The /api/track/lead/client endpoint incorrectly requires the clickId field, while the main /api/track/lead endpoint correctly treats it as optional. This creates an API inconsistency and breaks existing clients that don't provide clickId.
Technical Analysis
Schema Definition
The base trackLeadRequestSchema in apps/web/lib/zod/schemas/leads.ts defines clickId as:
clickId: z.string().trim()This makes clickId required by default (no .nullish() or .optional()).
Route Implementations
Main Route (/api/track/lead):
- β Correctly handles optional clickId
- Uses
trackLeadRequestSchema.extend({ clickId: z.string().trim().nullish() }) - Provides fallback:
clickId: clickId ?? ""
Client Route (/api/track/lead/client):
- β Incorrectly requires clickId
- Uses
trackLeadRequestSchema.parse(body)directly - No schema extension to make
clickIdoptional
Validation Results
Testing confirmed the issue using Zod schema validation:
// Client route behavior (BEFORE fix)
trackLeadRequestSchema.parse({
eventName: "Sign up",
customerExternalId: "user123"
// No clickId provided
});
// β Error: "Required"
// Main route behavior
trackLeadRequestSchema.extend({
clickId: z.string().trim().nullish()
}).parse({
eventName: "Sign up",
customerExternalId: "user123"
});
// β
Success: clickId = undefinedImpact
- Breaking Change: Existing clients using
/api/track/lead/clientwithoutclickIdwill fail with validation errors - API Inconsistency: Two endpoints with different requirements for the same field
- Developer Confusion: Inconsistent behavior between similar endpoints
Solution
Applied the same schema extension pattern used in the main route to the client route:
- Added Zod import:
import { z } from "zod" - Extended schema: Used
.extend({ clickId: z.string().trim().nullish() }) - Added fallback: Used
clickId: clickId ?? ""in thetrackLeadcall
This ensures both endpoints behave identically and maintain backward compatibility.
Verification
Post-fix testing shows both routes now handle optional clickId consistently:
- β
Requests without
clickIdsucceed (coerced to empty string) - β
Requests with
clickIdwork as before - β No breaking changes for existing clients
Summary by CodeRabbit
New Features
Bug Fixes