Skip to content

ENG-1909 Add no-op Next.js content upsert endpoint#1220

Open
maparent wants to merge 2 commits into
eng-2036-simplify-the-cross-app-node-contractfrom
eng-1909-add-no-op-nextjs-content-upsert-endpoint
Open

ENG-1909 Add no-op Next.js content upsert endpoint#1220
maparent wants to merge 2 commits into
eng-2036-simplify-the-cross-app-node-contractfrom
eng-1909-add-no-op-nextjs-content-upsert-endpoint

Conversation

@maparent

@maparent maparent commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

https://linear.app/discourse-graphs/issue/ENG-1909/add-no-op-nextjs-content-upsert-endpoint

Content upsert, based on cross-app types.

This introduces a StandaloneContent type for upserting contents outside of concepts, following existing sync functions.

https://www.loom.com/share/202c49deb3014d2e8f60632f38ff71b4

@linear-code

linear-code Bot commented Jul 11, 2026

Copy link
Copy Markdown

ENG-1909

@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
discourse-graph Ready Ready Preview, Comment Jul 15, 2026 9:42pm

Request Review

@supabase

supabase Bot commented Jul 11, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project zytfjzqyijgagqxrzbmz because there are no changes detected in packages/database/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@maparent maparent force-pushed the eng-1909-add-no-op-nextjs-content-upsert-endpoint branch from 4cd8e3f to 7fc6f80 Compare July 11, 2026 13:05
@maparent maparent force-pushed the eng-1909-add-no-op-nextjs-content-upsert-endpoint branch from 7fc6f80 to d43d61e Compare July 11, 2026 13:14
@maparent maparent marked this pull request as ready for review July 11, 2026 13:19
@maparent maparent requested a review from mdroidian July 11, 2026 13:19
graphite-app[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

graphite-app[bot]

This comment was marked as resolved.

@maparent maparent marked this pull request as draft July 13, 2026 19:52
@maparent maparent force-pushed the eng-2017-add-converter-for-crossapp-schemas-to-localconceptdatainput branch 2 times, most recently from 1127f9a to 284254a Compare July 15, 2026 19:12
@maparent maparent force-pushed the eng-1909-add-no-op-nextjs-content-upsert-endpoint branch from 615145c to 9445331 Compare July 15, 2026 19:51
@maparent maparent force-pushed the eng-1909-add-no-op-nextjs-content-upsert-endpoint branch from 9445331 to 856b6fc Compare July 15, 2026 21:41
@maparent maparent changed the base branch from eng-2017-add-converter-for-crossapp-schemas-to-localconceptdatainput to eng-2036-simplify-the-cross-app-node-contract July 15, 2026 21:42
@maparent maparent marked this pull request as ready for review July 15, 2026 21:43

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +32 to +37
const userId = await getAccountId(supabase);
if (userId === undefined)
return createApiResponse(
request,
asPostgrestFailure("Please login", "invalid", 401),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Database errors during user account lookup crash the request instead of returning a formatted error

The user account lookup is performed outside the error-handling block (getAccountId(supabase) at apps/website/app/api/internal/space/[id]/content/route.ts:32) before the try-catch that starts at line 38, so a database failure during that lookup produces an unformatted 500 crash instead of a structured API error response.

Impact: Clients receive a raw server error instead of a JSON error message when the account database query fails.

Mechanism: getAccountId throws outside the try-catch scope

The getAccountId function at apps/website/app/utils/supabase/account.ts:20 explicitly throws when the database query errors:

if (accountReq.error) throw accountReq.error;

In the route handler, this call sits at line 32, while the try block that wraps the rest of the logic and calls handleRouteError only begins at line 38. Any PostgrestError thrown by the account lookup propagates as an unhandled exception.

The existing content route at apps/website/app/api/supabase/content/route.ts:43 wraps all logic inside its try-catch, making this new route inconsistent with the established pattern.

Prompt for agents
The getAccountId call at line 32 and its undefined-check at lines 33-37 are outside the try-catch block that starts at line 38. Since getAccountId can throw a PostgrestError (see account.ts:20), this call should be moved inside the try block so that handleRouteError can catch and format the error. Move the try statement (line 38) to before the getAccountId call (before line 32), so that lines 32-37 are inside the try-catch. This matches the pattern used in other API routes like apps/website/app/api/supabase/content/route.ts where all logic is inside the try-catch.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +39 to +52
const body = (await request.json()) as StandaloneCrossAppContent[];
// TODO: Zed validator
const content = body
.map((c) =>
crossAppStandaloneContentToDbContent(
{
...c,
createdAt: new Date(c.createdAt),
modifiedAt: new Date(c.modifiedAt || c.createdAt),
},
spaceId,
),
)
.filter((c) => c !== undefined);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟨 No input validation on untrusted request body before database processing

The POST handler at apps/website/app/api/internal/space/[id]/content/route.ts:39 casts the raw JSON body with as StandaloneCrossAppContent[] without any runtime validation. Arbitrary JSON payloads are spread into objects and passed to the database RPC. While the database may reject some malformed data, missing fields or unexpected types could cause unexpected behavior or partial writes. The code itself acknowledges this gap with a // TODO: Zed validator comment.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@maparent maparent requested a review from mdroidian July 15, 2026 21:49
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.

1 participant