Skip to content

feat: waitlist supabase api#33

Merged
aguilar1x merged 3 commits intoACTA-Team:developfrom
felipevega2x:feat/waitlist-supabase-api
Mar 5, 2026
Merged

feat: waitlist supabase api#33
aguilar1x merged 3 commits intoACTA-Team:developfrom
felipevega2x:feat/waitlist-supabase-api

Conversation

@felipevega2x
Copy link
Contributor

@felipevega2x felipevega2x commented Mar 5, 2026

Summary

Connects the waitlist form to our internal API so signups are stored in Supabase (public.waitlist) instead of Formspree.

Changes

API route POST /api/waitlist

  • Validates email (required, valid format); returns 400 if invalid
  • Inserts into public.waitlist using server-side Supabase client
  • Returns 201 on success
  • Returns 409 when email already exists (unique constraint)
  • Returns 503 when Supabase fails (invalid config or unavailable)
  • Returns 500 for unexpected errors

WaitlistForm

  • Submits to POST /api/waitlist with { email, company_name, use_case }
  • Keeps honeypot and client-side validation
  • Error handling:
    • 409: "This email is already on the waitlist."
    • 400: "Please check your email and try again."
    • 5xx / network: "Something went wrong. Please try again later."
  • Keeps loading state, success/error messages, and form reset on success

Supabase client

  • Adds fallbacks when env vars are missing so the build passes in CI

Summary by CodeRabbit

  • New Features

    • Backend waitlist endpoint added to process signups and return clear success/error statuses.
  • Improvements

    • Waitlist form now shows distinct messages for duplicate emails, validation errors, and server problems.
    • Minor UI formatting tweak to the "How It Works" section for consistency.

- Add POST /api/waitlist with email validation and Supabase insert
- Return 409 on duplicate email, 503 on Supabase errors
- Update WaitlistForm to submit to API (email, company_name, use_case)
- Add duplicate/error states with appropriate messages

Made-with: Cursor
@vercel
Copy link
Contributor

vercel bot commented Mar 5, 2026

@felipevega2x is attempting to deploy a commit to the ACTA Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 86cbc2de-adfc-45a1-b5bd-b2955cdaaae0

📥 Commits

Reviewing files that changed from the base of the PR and between 588dc1e and 9d1fb52.

📒 Files selected for processing (4)
  • src/app/api/waitlist/route.ts
  • src/features/how-it-works/HowItWorks.tsx
  • src/features/waitlist/WaitlistForm.tsx
  • src/lib/supabase.ts

📝 Walkthrough

Walkthrough

Adds a POST API endpoint at src/app/api/waitlist/route.ts that validates and normalizes waitlist payloads, inserts into Supabase public.waitlist, and returns appropriate HTTP statuses; updates the WaitlistForm to submit to this endpoint; and adds environment-variable fallbacks to Supabase client initialization.

Changes

Cohort / File(s) Summary
Waitlist API Implementation
src/app/api/waitlist/route.ts
New POST handler: validates email (regex), normalizes/trims fields (company_name, use_case), inserts a row into public.waitlist via service Supabase client, and returns 201 / 400 / 409 / 503 / 500 with structured JSON.
Waitlist Form Integration
src/features/waitlist/WaitlistForm.tsx
Switched submission from Formspree to /api/waitlist; maps companycompany_name, messageuse_case; adds "duplicate" status for 409 responses; introduces errorMessage state and enhanced error handling; preserves honeypot and resets on success.
Supabase Client Configuration
src/lib/supabase.ts
Reads env vars with optional chaining and .trim(), provides placeholder fallbacks for CI/build when unset, and conditionally constructs a service-role Supabase client when service key exists.
Formatting Minor Update
src/features/how-it-works/HowItWorks.tsx
Minor numeric formatting change (OPEN_DELAY 0.10 → 0.1) and small JSX formatting tweak.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant WaitlistForm
    participant API_Route as "API Route"
    participant Supabase
    participant Database

    User->>WaitlistForm: Fill email, company, use_case
    WaitlistForm->>WaitlistForm: Validate email + check honeypot
    WaitlistForm->>API_Route: POST /api/waitlist {email, company_name, use_case}
    API_Route->>API_Route: Validate & normalize payload
    API_Route->>Supabase: Insert into public.waitlist
    alt Insert succeeds
        Supabase->>Database: Persist row
        Database-->>Supabase: Confirm
        Supabase-->>API_Route: Success
        API_Route-->>WaitlistForm: 201 Created
        WaitlistForm->>User: Show success, reset form
    else Unique constraint violation
        Supabase-->>API_Route: Unique constraint error
        API_Route-->>WaitlistForm: 409 Conflict
        WaitlistForm->>User: Show "email already on the waitlist"
    else DB / config error
        Supabase-->>API_Route: Error
        API_Route-->>WaitlistForm: 503 / 500
        WaitlistForm->>User: Show generic error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • Connect WaitlistForm to Supabase (API + form) #28: Connect WaitlistForm to Supabase (API + form) — This PR implements the requested POST /api/waitlist, server-side insert into public.waitlist, 409 handling for duplicates, and client mapping of form fields.

Possibly related PRs

  • PR #33: Makes the same code-level changes (waitlist API, form submission to /api/waitlist, and Supabase fallbacks) and is highly related.
  • PR #32: Also modifies the waitlist flow and Supabase client setup; closely related to these edits.

Poem

🐰
I hopped upon a tiny route,
Sent emails to the supabase root,
A duplicate? I gave a tap—
409 sent back a clap,
Now the waitlist grows, nibble by nibble.

🚥 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 'feat: waitlist supabase api' clearly summarizes the main change: connecting the waitlist form to a Supabase API endpoint.
Linked Issues check ✅ Passed The PR implementation meets all objectives from issue #28: API route validates email and returns appropriate status codes (201, 400, 409, 503), WaitlistForm submits to /api/waitlist with correct payload mapping, honeypot and client validation preserved, error handling maps codes to user messages, and data persists only in database.
Out of Scope Changes check ✅ Passed Changes to HowItWorks.tsx (minor formatting: OPEN_DELAY constant and tag formatting) are minor cosmetic updates unrelated to waitlist Supabase functionality but pose no risk.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@gitguardian
Copy link

gitguardian bot commented Mar 5, 2026

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@felipevega2x felipevega2x force-pushed the feat/waitlist-supabase-api branch from 588dc1e to 9d1fb52 Compare March 5, 2026 04:20
Copy link

@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: 3

🧹 Nitpick comments (1)
src/features/waitlist/WaitlistForm.tsx (1)

38-53: Normalize email before client validation and submission.

Client-side and server-side normalization are slightly misaligned. Trim/lowercase on the client to avoid false validation failures.

🔧 Proposed fix
-    if (!validateEmail(email)) {
+    const normalizedEmail = email.trim().toLowerCase();
+    if (!validateEmail(normalizedEmail)) {
       setStatus("error");
       setErrorMessage("Please check your email and try again.");
       return;
     }
@@
         body: JSON.stringify({
-          email,
+          email: normalizedEmail,
           company_name: company,
           use_case: message,
         }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/waitlist/WaitlistForm.tsx` around lines 38 - 53, Normalize the
email before validating and submitting: create a normalizedEmail by trimming and
lowercasing the existing email variable, use that normalizedEmail when calling
validateEmail(email) (i.e., validateEmail(normalizedEmail)) and when building
the POST body to /api/waitlist (replace email with normalizedEmail) so
client-side validation and the server receive the same normalized value; update
any status/error handling (setStatus, setErrorMessage, setIsSubmitting) to
operate around this flow in WaitlistForm (functions: validateEmail, setStatus,
setIsSubmitting, fetch to /api/waitlist).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/api/waitlist/route.ts`:
- Around line 41-42: The console.error calls in src/app/api/waitlist/route.ts
are logging raw error objects/messages (seen around the Supabase call and the
second error path) which can contain user emails; change those logs to avoid
printing error.message/raw error objects. Instead, log a sanitized, minimal
message and non-sensitive fields (e.g., error.code or status) from the Supabase
response and optionally a redacted/error id; do not include the submitted email
or full error text. Update the error handling in the POST handler (the function
handling the Supabase insert and the subsequent NextResponse.json error path) to
redact sensitive values (use a regex to mask emails if you must include them) or
omit them entirely and return a generic NextResponse.json error to the client.
- Around line 14-22: The handler currently assumes request.json() returns a
valid object and treats parse failures as server errors; update the logic around
the body parsing (the code that sets const body = (await request.json()) as
WaitlistPayload and the similar block around lines referenced 51-56) to
explicitly catch JSON parse errors and validate that body is a non-null object
before using body.email; if JSON.parse fails or body is not an object return
NextResponse.json({ error: "Malformed or invalid request body." }, { status: 400
}) instead of letting it fall through to the 500 catch, and keep the existing
EMAIL_REGEX/email presence checks afterward.

In `@src/lib/supabase.ts`:
- Around line 5-6: The constant PLACEHOLDER_KEY currently contains a JWT-shaped
token which trips secret scanners; replace its value with a clearly non-secret
sentinel (e.g., "PLACEHOLDER_KEY" or "REDACTED_PLACEHOLDER") in the declaration
of PLACEHOLDER_KEY so it no longer resembles a JWT; update any tests or places
that rely on the exact string if needed to use the new sentinel, and keep the
constant name and usage (PLACEHOLDER_KEY) unchanged.

---

Nitpick comments:
In `@src/features/waitlist/WaitlistForm.tsx`:
- Around line 38-53: Normalize the email before validating and submitting:
create a normalizedEmail by trimming and lowercasing the existing email
variable, use that normalizedEmail when calling validateEmail(email) (i.e.,
validateEmail(normalizedEmail)) and when building the POST body to /api/waitlist
(replace email with normalizedEmail) so client-side validation and the server
receive the same normalized value; update any status/error handling (setStatus,
setErrorMessage, setIsSubmitting) to operate around this flow in WaitlistForm
(functions: validateEmail, setStatus, setIsSubmitting, fetch to /api/waitlist).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 960bbe20-aefa-41a4-9f50-02fe87aaf8a4

📥 Commits

Reviewing files that changed from the base of the PR and between fc8ecd2 and 588dc1e.

📒 Files selected for processing (4)
  • src/app/api/waitlist/route.ts
  • src/features/how-it-works/HowItWorks.tsx
  • src/features/waitlist/WaitlistForm.tsx
  • src/lib/supabase.ts

Comment on lines +14 to +22
const body = (await request.json()) as WaitlistPayload;

const email = body.email?.trim().toLowerCase();
if (!email || !EMAIL_REGEX.test(email)) {
return NextResponse.json(
{ error: "A valid email address is required." },
{ status: 400 }
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Return 400 for malformed/non-object request bodies.

If JSON parsing fails or body is not an object, the handler falls into the catch block and returns 500. That should be a 400 client error.

🔧 Proposed fix
 export async function POST(request: Request) {
   try {
-    const body = (await request.json()) as WaitlistPayload;
+    let body: WaitlistPayload;
+    try {
+      const parsed = await request.json();
+      if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
+        return NextResponse.json(
+          { error: "Invalid request body." },
+          { status: 400 }
+        );
+      }
+      body = parsed as WaitlistPayload;
+    } catch {
+      return NextResponse.json(
+        { error: "Invalid JSON body." },
+        { status: 400 }
+      );
+    }

Also applies to: 51-56

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/waitlist/route.ts` around lines 14 - 22, The handler currently
assumes request.json() returns a valid object and treats parse failures as
server errors; update the logic around the body parsing (the code that sets
const body = (await request.json()) as WaitlistPayload and the similar block
around lines referenced 51-56) to explicitly catch JSON parse errors and
validate that body is a non-null object before using body.email; if JSON.parse
fails or body is not an object return NextResponse.json({ error: "Malformed or
invalid request body." }, { status: 400 }) instead of letting it fall through to
the 500 catch, and keep the existing EMAIL_REGEX/email presence checks
afterward.

Comment on lines +41 to +42
console.error("[api/waitlist] Supabase error:", error.message);
return NextResponse.json(
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid logging raw error messages that may include submitted email.

Line 41 and Line 52 log raw error text/object. Database errors can contain duplicate key details with user email.

🔧 Proposed fix
-      console.error("[api/waitlist] Supabase error:", error.message);
+      console.error("[api/waitlist] Supabase error", {
+        code: error.code,
+        status: error.status,
+      });
@@
-    console.error("[api/waitlist] Unexpected error:", err);
+    console.error("[api/waitlist] Unexpected error", {
+      name: err instanceof Error ? err.name : "UnknownError",
+    });

Also applies to: 52-52

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/waitlist/route.ts` around lines 41 - 42, The console.error calls
in src/app/api/waitlist/route.ts are logging raw error objects/messages (seen
around the Supabase call and the second error path) which can contain user
emails; change those logs to avoid printing error.message/raw error objects.
Instead, log a sanitized, minimal message and non-sensitive fields (e.g.,
error.code or status) from the Supabase response and optionally a redacted/error
id; do not include the submitted email or full error text. Update the error
handling in the POST handler (the function handling the Supabase insert and the
subsequent NextResponse.json error path) to redact sensitive values (use a regex
to mask emails if you must include them) or omit them entirely and return a
generic NextResponse.json error to the client.

@aguilar1x aguilar1x merged commit 5ebf444 into ACTA-Team:develop Mar 5, 2026
3 of 5 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.

Connect WaitlistForm to Supabase (API + form)

2 participants