Skip to content

fix: replace .single() with .maybeSingle() in 5 API routes#475

Closed
tankgxy wants to merge 1 commit into
profullstack:masterfrom
tankgxy:fix/single-to-maybeSingle
Closed

fix: replace .single() with .maybeSingle() in 5 API routes#475
tankgxy wants to merge 1 commit into
profullstack:masterfrom
tankgxy:fix/single-to-maybeSingle

Conversation

@tankgxy

@tankgxy tankgxy commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Replaced .single() with .maybeSingle() in profile, applications, gigs, conversations, and messages APIs to prevent crashes when queries return 0 results.

5 files changed, 28 insertions, 28 deletions.

Fixed routes:

  • GET/PUT /api/profile
  • POST/GET /api/applications
  • POST/GET /api/gigs
  • POST /api/conversations
  • POST /api/messages/send

Prevents crashes when Supabase queries return 0 or multiple rows.
.single() throws on empty results; .maybeSingle() returns null gracefully.

Fixed routes:
- GET/PUT /api/profile: handle missing profiles
- POST/GET /api/applications: handle missing applications
- POST/GET /api/gigs: handle missing gigs
- POST /api/conversations: handle missing conversations
- POST /api/messages/send: handle missing messages
@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces .single() with .maybeSingle() across five API routes to prevent Supabase from throwing when a query returns zero rows. Most call sites handle the null return correctly, but two routes introduce new null-dereference bugs in the process.

  • profile/route.ts (GET & PUT): The new if (error || !profile) guard calls error.message even when error is null (the "no profile found" case), crashing with a TypeError and returning a misleading 500 response.
  • applications/route.ts (POST): The INSERT result is guarded only with if (error), leaving application.id accessible without a null check — a TypeError if RLS or another condition causes maybeSingle() to return null without an error.
  • gigs/route.ts: Three em-dash characters in inline comments were corrupted to ? as a text-encoding side-effect of the diff.

Confidence Score: 3/5

Two routes introduce new null-dereference crashes that silently degrade into 500 responses; needs targeted fixes before merging.

The profile route's error handler now calls error.message on a null object whenever a profile simply doesn't exist, turning a legitimate "not found" into a swallowed TypeError and a 500. The same refactor in the applications route leaves application.id reachable without a null guard after the INSERT. The three remaining routes (conversations, messages/send, gigs) handled the switch correctly and are safe.

src/app/api/profile/route.ts (both GET and PUT handlers) and src/app/api/applications/route.ts (POST handler after INSERT).

Important Files Changed

Filename Overview
src/app/api/profile/route.ts Switches GET and PUT queries to maybeSingle(), but the new `if (error
src/app/api/applications/route.ts Replaces four .single() calls with .maybeSingle(); the INSERT result is only guarded with if (error), leaving application.id accessible without a null check and risking TypeError if the row is not returned.
src/app/api/conversations/route.ts Eight .single() calls replaced with .maybeSingle(); null returns are correctly handled at each call site through explicit null checks and early returns.
src/app/api/gigs/route.ts Three .single() calls changed to .maybeSingle() with correct null handling; three em-dash characters in comments were corrupted to '?' as a text-encoding side-effect.
src/app/api/messages/send/route.ts Five .single() calls replaced with .maybeSingle(); all null returns are guarded with explicit checks and early error responses throughout the handler.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[API Request] --> B{getAuthContext}
    B -->|Unauthorized| C[401 Response]
    B -->|Authorized| D[Supabase Query]
    D --> E{maybeSingle result}
    E -->|data + no error| F[Success Path]
    E -->|null + no error| G{Null Guard}
    E -->|null + error| H[Error Response]
    G -->|profile/route.ts incorrect guard| I["error.message → TypeError! error is null"]
    I --> J[Catch block → 500]
    G -->|conversations, messages, gigs correct guard| K[404 / meaningful error]
    G -->|applications/route.ts missing guard on INSERT| L["application.id → TypeError! application is null"]
    L --> M[Catch block → 500]
    F --> N[JSON Response]
Loading

Comments Outside Diff (1)

  1. src/app/api/applications/route.ts, line 88-101 (link)

    P1 Unchecked null access on application.id after INSERT with maybeSingle()

    The INSERT uses .maybeSingle() (changed from .single()), so application is now typed as T | null. The guard on line 99 only checks if (error) — it does not handle the case where application is null without a DB error (e.g., a row-level security policy silently filters the returned row). When application is null, the accesses to application.id and application at lines 134 and 161 will throw a TypeError. Add || !application to the guard and return a 500 error before the downstream uses of application.

Reviews (1): Last reviewed commit: "fix: replace .single() with .maybeSingle..." | Re-trigger Greptile

Comment on lines +23 to 25
if (error || !profile) {
return NextResponse.json({ error: error.message }, { status: 400 });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Null dereference on error.message when profile not found

With .maybeSingle(), when the query returns no rows, error is null and profile is null. The guard if (error || !profile) correctly catches the "no profile" case, but then error.message throws a TypeError because error is null. This uncaught exception propagates to the catch block, returning a generic 500 "An unexpected error occurred" response instead of the intended 400. The same pattern is broken in the PUT handler at line 125–129.

Comment thread src/app/api/gigs/route.ts
Comment on lines 73 to +76
)
.eq("status", "active");

// Apply filters use textSearch or individual filters to prevent PostgREST filter injection (#71)
// Apply filters �?use textSearch or individual filters to prevent PostgREST filter injection (#71)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Comment encoding corruption (em dash → ?)

Three comments in this file had their em dash character () corrupted to ? (e.g., // Apply filters ?use textSearch…, // No filter ?return both types, // Apply pagination ?ensure non-negative offset…). These are likely a text-encoding side-effect of the PR tooling. The comments should be restored to their original readable form.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@ralyodio ralyodio closed this Jun 14, 2026
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