Skip to content

updated sdk version to v0.16.2#14

Merged
agreea merged 10 commits into
mainfrom
update-sdk-0.16.2
Jan 14, 2026
Merged

updated sdk version to v0.16.2#14
agreea merged 10 commits into
mainfrom
update-sdk-0.16.2

Conversation

@lpmsh

@lpmsh lpmsh commented Jan 12, 2026

Copy link
Copy Markdown
Contributor

closes #13

Summary by CodeRabbit

  • Dependencies

    • Updated Flowglad library to v0.16.2 across example projects
  • Improvements

    • Switched consumption reporting from local API endpoints to the integrated billing API (client-side), simplifying usage-event creation and error signaling
  • Maintenance

    • Updated internal metadata and schema constraints used by examples (non-functional metadata changes)

✏️ Tip: You can customize this high-level summary in your review settings.

@lpmsh lpmsh self-assigned this Jan 12, 2026
@coderabbitai

coderabbitai Bot commented Jan 12, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The pull request updates example projects to use Flowglad SDK v0.16.2 by removing local /api/usage-events POST endpoints and replacing fetch-based usage-event creation with billing.createUsageEvent() calls; several Drizzle meta snapshots and package versions were also updated.

Changes

Cohort / File(s) Summary
Database metadata
nextjs/generation-based-subscription/drizzle/meta/0000_snapshot.json, nextjs/generation-based-subscription/drizzle/meta/_journal.json, nextjs/tiered-usage-gated-subscription/drizzle/meta/0000_snapshot.json, nextjs/tiered-usage-gated-subscription/drizzle/meta/_journal.json
Updated snapshot IDs and journal timestamps/tags; removed an accounts unique constraint in snapshots.
Package dependency bumps
nextjs/generation-based-subscription/package.json, nextjs/pay-as-you-go/package.json, nextjs/tiered-usage-gated-subscription/package.json, nextjs/usage-limit-subscription/package.json, tanstack-start/generation-based-subscription/package.json
Bumped @flowglad/nextjs (examples to 0.16.2 / tanstack to ^0.16.2).
Removed server API routes
nextjs/*/src/app/api/usage-events/route.ts (multiple examples), tanstack-start/generation-based-subscription/src/routes/api/usage-events.ts
Deleted POST handlers that previously validated input, managed sessions, resolved billing/pricing, and created usage events server-side (including idempotency and detailed error paths).
Client-side migration to SDK
nextjs/*/src/app/home-client.tsx (generation-based, pay-as-you-go, tiered-usage-gated, usage-limit), tanstack-start/generation-based-subscription/src/routes/index.tsx
Replaced fetch POSTs to /api/usage-events with billing.createUsageEvent(...), added runtime guard for createUsageEvent, removed local transactionId generation in most places, and switched error handling to inspect result.error.
Route tree cleanup
tanstack-start/generation-based-subscription/src/routeTree.gen.ts
Removed generated route references and types for /api/usage-events (ApiUsageEventsRoute) from route maps and type unions.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client UI
  participant SDK as useBilling SDK
  participant Flowglad as Flowglad API

  Client->>SDK: billing.createUsageEvent({usageMeterSlug, amount})
  SDK->>Flowglad: Create usage event request (auth handled by SDK)
  Flowglad-->>SDK: Result (success | error payload)
  SDK-->>Client: { error? , data? }
  Client->>Client: handle result (reload billing / show error)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly related PRs

Suggested reviewers

  • joeysabs
  • agreea

Poem

🐰 I nudged the routes and gave them rest,
SDK hops in, tidy and dressed.
No more fetch, a single call rings,
createUsageEvent—how the meadow sings! 🥕✨

🚥 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 'updated sdk version to v0.16.2' accurately summarizes the primary change across all files—upgrading the SDK version and migrating to the new createUsageEvent API.
Linked Issues check ✅ Passed The PR fully satisfies issue #13 requirements: it upgrades @flowglad/nextjs to v0.16.2, removes all dedicated /api/usage-events routes, and replaces manual fetch calls with billing.createUsageEvent across all affected example files.
Out of Scope Changes check ✅ Passed All changes align with the SDK upgrade objective. Database snapshot and journal file updates are necessary metadata changes from Drizzle migrations; there are no unrelated modifications.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@lpmsh lpmsh force-pushed the update-sdk-0.16.2 branch from 00c3e21 to 7bb0152 Compare January 12, 2026 04:16
@lpmsh lpmsh marked this pull request as ready for review January 12, 2026 04:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @nextjs/generation-based-subscription/src/app/home-client.tsx:
- Around line 221-231: The call to billing.createUsageEvent in this handler
lacks the same undefined guard used in handleGenerateFastImage; before invoking
billing.createUsageEvent (and before using amount/transactionId), check that
billing.createUsageEvent is a function and return or throw early if it's
undefined, mirroring the guard in handleGenerateFastImage so you avoid runtime
errors when createUsageEvent is not provided.

In @tanstack-start/generation-based-subscription/src/routes/index.tsx:
- Around line 178-194: The fast image generation path calls
billing.createUsageEvent without a transactionId, risking duplicate usage events
on retries; update the fast generation handler (where billing.createUsageEvent
is invoked) to generate a unique transactionId (e.g., crypto.randomUUID() or
existing helper used by the HD video handler) and include transactionId in the
createUsageEvent payload, ensuring types align with the billing.createUsageEvent
signature and matching the approach used in the HD video handler to guarantee
idempotency.
- Around line 231-242: Add the same defensive guard used in the fast image
handler before calling billing.createUsageEvent: check that
billing.createUsageEvent is defined (e.g., if (billing?.createUsageEvent)) and
only call it when present; if it's absent, skip the call or handle the fallback
accordingly, then keep the existing result/error handling logic for the response
variable (result) once the call is made.
- Around line 189-194: The error handling currently reads the wrong field from
the Flowglad API error object; update the thrown message construction to use the
nested message field instead of the top-level error object. Specifically,
replace uses of result.error.json?.error (cast to string) with
result.error.json?.error?.message (cast to string) in both error branches
handling the API response so the thrown Error uses
(result.error.json?.error?.message as string) || 'Failed to create usage event'.
🧹 Nitpick comments (1)
nextjs/tiered-usage-gated-subscription/src/app/home-client.tsx (1)

201-233: Consider removing unused parameters from the function signature.

The priceSlug and transactionIdPrefix parameters are defined but never used in the function body after migrating to billing.createUsageEvent. These appear to be leftovers from the previous fetch-based implementation.

🧹 Suggested cleanup
 const handleUsageEvent = async ({
-    priceSlug,
     usageMeterSlug,
     hasFeatureAccess,
     hasUsageMeterAccess,
     isUnlimited,
     remaining,
     setIsLoading,
     setMessageError,
     billing,
-    transactionIdPrefix,
     userMessage,
     assistantMessage,
     modelName,
     errorMessage = 'Failed to send message. Please try again.',
     alwaysCreateUsageEvent = false,
   }: {
-    priceSlug: string;
     usageMeterSlug: string;
     hasFeatureAccess: boolean;
     hasUsageMeterAccess: boolean;
     isUnlimited: boolean;
     remaining: number;
     setIsLoading: (loading: boolean) => void;
     setMessageError: (error: string | null) => void;
     billing: ReturnType<typeof useBilling>;
-    transactionIdPrefix: string;
     userMessage: string;
     assistantMessage: string;
     modelName: string;
     errorMessage?: string;
     alwaysCreateUsageEvent?: boolean;
   }) => {

Then update all call sites (lines 287-401) to remove the priceSlug and transactionIdPrefix arguments.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6cd5a9 and 23c4722.

⛔ Files ignored due to path filters (5)
  • nextjs/generation-based-subscription/bun.lock is excluded by !**/*.lock
  • nextjs/pay-as-you-go/bun.lock is excluded by !**/*.lock
  • nextjs/tiered-usage-gated-subscription/bun.lock is excluded by !**/*.lock
  • nextjs/usage-limit-subscription/bun.lock is excluded by !**/*.lock
  • tanstack-start/generation-based-subscription/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (22)
  • nextjs/generation-based-subscription/drizzle/0000_open_maria_hill.sql
  • nextjs/generation-based-subscription/drizzle/meta/0000_snapshot.json
  • nextjs/generation-based-subscription/drizzle/meta/_journal.json
  • nextjs/generation-based-subscription/package.json
  • nextjs/generation-based-subscription/src/app/api/usage-events/route.ts
  • nextjs/generation-based-subscription/src/app/home-client.tsx
  • nextjs/pay-as-you-go/package.json
  • nextjs/pay-as-you-go/src/app/api/usage-events/route.ts
  • nextjs/pay-as-you-go/src/app/home-client.tsx
  • nextjs/tiered-usage-gated-subscription/drizzle/0000_pale_starhawk.sql
  • nextjs/tiered-usage-gated-subscription/drizzle/meta/0000_snapshot.json
  • nextjs/tiered-usage-gated-subscription/drizzle/meta/_journal.json
  • nextjs/tiered-usage-gated-subscription/package.json
  • nextjs/tiered-usage-gated-subscription/src/app/api/usage-events/route.ts
  • nextjs/tiered-usage-gated-subscription/src/app/home-client.tsx
  • nextjs/usage-limit-subscription/package.json
  • nextjs/usage-limit-subscription/src/app/api/usage-events/route.ts
  • nextjs/usage-limit-subscription/src/app/home-client.tsx
  • tanstack-start/generation-based-subscription/package.json
  • tanstack-start/generation-based-subscription/src/routeTree.gen.ts
  • tanstack-start/generation-based-subscription/src/routes/api/usage-events.ts
  • tanstack-start/generation-based-subscription/src/routes/index.tsx
💤 Files with no reviewable changes (6)
  • nextjs/pay-as-you-go/src/app/api/usage-events/route.ts
  • nextjs/generation-based-subscription/src/app/api/usage-events/route.ts
  • tanstack-start/generation-based-subscription/src/routeTree.gen.ts
  • nextjs/usage-limit-subscription/src/app/api/usage-events/route.ts
  • nextjs/tiered-usage-gated-subscription/src/app/api/usage-events/route.ts
  • tanstack-start/generation-based-subscription/src/routes/api/usage-events.ts
🔇 Additional comments (14)
nextjs/generation-based-subscription/package.json (1)

23-23: LGTM!

The SDK version bump to 0.16.2 aligns with the PR objectives to adopt the new createUsageEvent method. The pinned version (without ^ or ~) ensures reproducible builds for this example project.

nextjs/usage-limit-subscription/package.json (1)

25-25: LGTM!

Consistent SDK version update to 0.16.2 matching the other example projects.

nextjs/pay-as-you-go/package.json (1)

23-23: LGTM!

SDK version update to 0.16.2 is consistent with the PR objectives.

nextjs/tiered-usage-gated-subscription/package.json (1)

23-23: LGTM!

SDK version update to 0.16.2 completes the consistent upgrade across all Next.js example projects.

nextjs/tiered-usage-gated-subscription/src/app/home-client.tsx (1)

248-261: LGTM on the SDK migration pattern.

The runtime guard and error handling are correctly implemented for the new billing.createUsageEvent API. The 'error' in result check properly handles the SDK's result-based error signaling.

nextjs/pay-as-you-go/src/app/home-client.tsx (1)

112-126: SDK migration looks correct; note inconsistent error property access.

The migration to billing.createUsageEvent is properly implemented with the amount parameter. However, the error message extraction uses result.error.json?.message (Line 123), while the other files in this PR use result.error.json?.error. This inconsistency may cause different error messages to display depending on which property the SDK actually returns.

Verify which property the SDK returns and align all files to use the same pattern.

nextjs/usage-limit-subscription/src/app/home-client.tsx (1)

147-158: LGTM on the SDK migration.

The migration to billing.createUsageEvent is correctly implemented. The error handling pattern using result.error.json?.error is consistent with the tiered-usage example.

nextjs/generation-based-subscription/drizzle/meta/_journal.json (1)

8-9: LGTM!

Migration journal metadata updated with regenerated timestamp and tag, consistent with schema changes in this PR.

nextjs/generation-based-subscription/src/app/home-client.tsx (2)

168-184: LGTM!

The runtime guard for createUsageEvent is good defensive programming, and the new SDK-based usage event creation aligns with the v0.16.2 migration objectives.


36-70: LGTM!

Good use of useRef to track previous user ID and trigger billing reload only when the user changes, preventing unnecessary refetches.

nextjs/tiered-usage-gated-subscription/drizzle/meta/_journal.json (1)

8-9: This review comment is based on a false premise and should be dismissed.

The tiered-usage-gated-subscription example is newly added in this commit. The migration file 0000_pale_starhawk.sql is not a regenerated entry but rather the initial migration for a fresh example. The assumed prior migration tag 0000_dizzy_micromax does not exist in this example's history. Since this example has no prior migration history, there is no risk of migration conflicts across environments or re-application of migrations.

Likely an incorrect or invalid review comment.

tanstack-start/generation-based-subscription/package.json (1)

20-22: LGTM! Version bumps are consistent and published.

The Flowglad SDK packages are correctly updated to v0.16.2 across all three dependencies. All packages are published on npm and properly maintain version consistency—@flowglad/react and @flowglad/server both depend on @flowglad/shared@0.16.2, matching the versions specified here.

nextjs/tiered-usage-gated-subscription/drizzle/meta/0000_snapshot.json (1)

107-107: Same unique constraint removal as in generation-based-subscription.

This mirrors the constraint removal in the other example project. If the removal in generation-based-subscription is confirmed intentional, this change is consistent.

nextjs/generation-based-subscription/drizzle/meta/0000_snapshot.json (1)

107-107: The unique constraint is not present in the current schema definition. The accounts table in src/server/db/schema.ts defines providerId and accountId as notNull fields but does not include a unique constraint on their combination. The snapshot at line 107 correctly reflects this with an empty uniqueConstraints object. No constraint was removed; the schema simply does not enforce uniqueness on (providerId, accountId).

Likely an incorrect or invalid review comment.

Comment on lines +221 to 231
const result = await billing.createUsageEvent({
usageMeterSlug: 'hd_video_minutes',
amount,
transactionId,
});

if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Failed to create usage event');
if ('error' in result) {
throw new Error(
(result.error.json?.error as string) || 'Failed to create usage event'
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing guard for createUsageEvent availability.

handleGenerateFastImage guards against billing.createUsageEvent being undefined (lines 168-170), but this handler does not. For consistency and to prevent potential runtime errors, add the same guard here.

Proposed fix
 const handleGenerateHDVideo = async () => {
   if (!hasHDVideoMinutesAccess || hdVideoMinutesRemaining === 0) {
     return;
   }

   setIsGeneratingHDVideo(true);
   setHdVideoError(null);

   try {
+    if (!billing.createUsageEvent) {
+      throw new Error('createUsageEvent is not available');
+    }
+
     // Generate a unique transaction ID for idempotency
     const transactionId = `hd_video_${Date.now()}_${Math.random().toString(36).substring(7)}`;
📝 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.

Suggested change
const result = await billing.createUsageEvent({
usageMeterSlug: 'hd_video_minutes',
amount,
transactionId,
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Failed to create usage event');
if ('error' in result) {
throw new Error(
(result.error.json?.error as string) || 'Failed to create usage event'
);
}
const handleGenerateHDVideo = async () => {
if (!hasHDVideoMinutesAccess || hdVideoMinutesRemaining === 0) {
return;
}
setIsGeneratingHDVideo(true);
setHdVideoError(null);
try {
if (!billing.createUsageEvent) {
throw new Error('createUsageEvent is not available');
}
// Generate a unique transaction ID for idempotency
const transactionId = `hd_video_${Date.now()}_${Math.random().toString(36).substring(7)}`;
const result = await billing.createUsageEvent({
usageMeterSlug: 'hd_video_minutes',
amount,
transactionId,
});
if ('error' in result) {
throw new Error(
(result.error.json?.error as string) || 'Failed to create usage event'
);
}
🤖 Prompt for AI Agents
In @nextjs/generation-based-subscription/src/app/home-client.tsx around lines
221 - 231, The call to billing.createUsageEvent in this handler lacks the same
undefined guard used in handleGenerateFastImage; before invoking
billing.createUsageEvent (and before using amount/transactionId), check that
billing.createUsageEvent is a function and return or throw early if it's
undefined, mirroring the guard in handleGenerateFastImage so you avoid runtime
errors when createUsageEvent is not provided.

Comment on lines +178 to 194
if (!billing.createUsageEvent) {
throw new Error('createUsageEvent is not available')
}
// Random amount between 3-5
const amount = Math.floor(Math.random() * 3) + 3

const response = await fetch('/api/usage-events', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
usageMeterSlug: 'fast_generations',
amount,
transactionId,
}),
const result = await billing.createUsageEvent({
usageMeterSlug: 'fast_generations',
amount,
})

if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Failed to create usage event')
if ('error' in result) {
throw new Error(
(result.error.json?.error as string) ||
'Failed to create usage event',
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing transactionId for idempotency in fast image generation.

The HD video handler (line 227-234) generates a transactionId to ensure idempotent usage event creation, but this handler does not. If the network fails after the event is created server-side but before the client receives the response, a retry would create a duplicate usage event and incorrectly decrement the user's balance twice.

Proposed fix to add transactionId
     try {
       if (!billing.createUsageEvent) {
         throw new Error('createUsageEvent is not available')
       }
+      // Generate a unique transaction ID for idempotency
+      const transactionId = `fast_image_${Date.now()}_${Math.random().toString(36).substring(7)}`
       // Random amount between 3-5
       const amount = Math.floor(Math.random() * 3) + 3

       const result = await billing.createUsageEvent({
         usageMeterSlug: 'fast_generations',
         amount,
+        transactionId,
       })
🤖 Prompt for AI Agents
In @tanstack-start/generation-based-subscription/src/routes/index.tsx around
lines 178 - 194, The fast image generation path calls billing.createUsageEvent
without a transactionId, risking duplicate usage events on retries; update the
fast generation handler (where billing.createUsageEvent is invoked) to generate
a unique transactionId (e.g., crypto.randomUUID() or existing helper used by the
HD video handler) and include transactionId in the createUsageEvent payload,
ensuring types align with the billing.createUsageEvent signature and matching
the approach used in the HD video handler to guarantee idempotency.

Comment on lines +189 to 194
if ('error' in result) {
throw new Error(
(result.error.json?.error as string) ||
'Failed to create usage event',
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

@flowglad/react createUsageEvent error response structure v0.16.2

💡 Result:

Short answer: The @flowglad/react docs for v0.16.2 do not document a specific JSON error payload for createUsageEvent. The createUsageEvent API/SDK docs only show the successful response (usageEvent object) and list possible HTTP error statuses (400/401/403/500) but no canonical error body. [1][2]

What I found:

  • Create Usage Event (request + successful response shown; errors only by HTTP status codes). [1]
  • @flowglad/react / server SDK docs reference that usage events must be created from the server and show examples but do not define an error JSON schema. [2][3]
  • The Express SDK example shows how Flowglad routes return errors to your app and a suggested custom error handler shape (example returning { error: 'Internal server error', message: ... }), but this is an example for your server, not the Flowglad API error contract. [4]

Sources:

  1. Create Usage Event API docs (request + success schema; HTTP 400/401/403/500 listed). [Create Usage Event]. [3]
  2. Usage-based billing / createUsageEvent usage examples in SDK docs. [Features: Usage]. [2]
  3. @flowglad/react & @flowglad/server SDK pages (notes that createUsageEvent should be called from server). [@flowglad/react]. [2]
  4. Express SDK error-handling example (shows how server-side errors might be returned from your express app). [Express SDK — Error Handling]. [4]

If you need the exact error JSON returned by the live Flowglad API for a particular error case (validation, auth, idempotency, etc.), I can:

  • make a sample curl request to the Create Usage Event endpoint and show the exact error body for a failing example, or
  • you can paste a real error response you received and I’ll explain its fields. Which would you prefer?

🌐 Web query:

flowglad React SDK v0.16.2 API documentation

💡 Result:

I found the @flowglad/react SDK API docs (components, hooks, and examples). See the SDK reference and API pages here: (docs.flowglad.com)

If you specifically need the v0.16.2 release notes or the package at that exact version (npm tarball or changelog), I can fetch the npm/GitHub release page—would you like me to do that?

Citations:


🌐 Web query:

@flowglad/react npm package error handling

💡 Result:

Short answer: @flowglad/react surfaces errors via the provider/hook and expects you to handle them in UI; server-side SDKs provide their own error middleware. Key points:

  • useBilling() returns an errors field (Error[] | null), plus loaded and reload helpers — you should check errors and show fallback UI or call reload. [1]
  • FlowgladProvider accepts loadBilling to opt-out of loading billing data; when loadBilling is true the hook’s loaded/errors indicate lifecycle and failure state. [1]
  • Server-side endpoints (e.g., createFlowgladExpressRouter) include built-in error handling; docs show adding a custom Express error handler to log and return safe messages. Handle server errors there and return appropriate status/messages to the frontend. [2]
  • For implementation examples and full API, see the Flowglad React SDK docs and the flowglad repo/examples. [1][3]

If you want, I can paste the exact useBilling example and a small React pattern for rendering error UI (with reload) based on the SDK.


🌐 Web query:

@flowglad/react v0.16.2 npm package source code GitHub

💡 Result:

The source for @flowglad/react is in the Flowglad monorepo on GitHub: https://github.com/flowglad/flowglad (see packages/react). [1][2]

If you need the exact v0.16.2 commit/files, open the repository, go to packages/react and switch to the tag or commit for v0.16.2 in the GitHub UI (Releases/Tags). [1][2]

Sources:

  • Flowglad GitHub repo (packages folder). [1]
  • Flowglad SDK docs (package @flowglad/react). [2]

🌐 Web query:

flowglad createUsageEvent server SDK implementation error shape

💡 Result:

Short answer — The server SDK throws/propagates Flowglad API errors (not a custom SDK-only shape). Expect the REST API error JSON of the form:

{
"error": {
"code": "SOME_ERROR_CODE",
"message": "Human readable message",
"details": { /* optional object with field-level info */ },
"request_id": "req_…",
"timestamp": "2024-01-15T10:30:00Z"
}
}

So when calling flowglad(...).createUsageEvent(...) catch the exception and inspect the HTTP/axios error payload (e.g. error.response.data.error) or error.message. The docs also describe createUsageEvent params, idempotency via transactionId, and SDK/Express built-in error handling. [1][2][3]

Sources:

  • Flowglad Server SDK — createUsageEvent & params. [1]
  • Feature Access & Usage / Usage-based billing (createUsageEvent details, transactionId idempotency). [2]
  • Flow-style REST API error response format (error.code, message, details, request_id, timestamp). [3]

Fix error response path to access the message field from the error object.

The Flowglad API error response structure is { "error": { "code": "...", "message": "...", ... } }. The current code accesses result.error.json?.error and casts it to a string, but this returns the entire error object, not a message string.

Update both locations (lines 189-194 and 237-242) to use result.error.json?.error?.message:

(result.error.json?.error?.message as string) || 'Failed to create usage event'
🤖 Prompt for AI Agents
In @tanstack-start/generation-based-subscription/src/routes/index.tsx around
lines 189 - 194, The error handling currently reads the wrong field from the
Flowglad API error object; update the thrown message construction to use the
nested message field instead of the top-level error object. Specifically,
replace uses of result.error.json?.error (cast to string) with
result.error.json?.error?.message (cast to string) in both error branches
handling the API response so the thrown Error uses
(result.error.json?.error?.message as string) || 'Failed to create usage event'.

Comment on lines +231 to 242
const result = await billing.createUsageEvent({
usageMeterSlug: 'hd_video_minutes',
amount,
transactionId,
})

if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Failed to create usage event')
if ('error' in result) {
throw new Error(
(result.error.json?.error as string) ||
'Failed to create usage event',
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing guard check for billing.createUsageEvent availability.

The fast image handler (lines 178-180) defensively checks if billing.createUsageEvent exists before calling it, but this handler calls it directly without the guard. For consistency and to avoid a potential runtime error if the method is unavailable, add the same check here.

Proposed fix to add guard check
     try {
+      if (!billing.createUsageEvent) {
+        throw new Error('createUsageEvent is not available')
+      }
       // Generate a unique transaction ID for idempotency
       const transactionId = `hd_video_${Date.now()}_${Math.random().toString(36).substring(7)}`
🤖 Prompt for AI Agents
In @tanstack-start/generation-based-subscription/src/routes/index.tsx around
lines 231 - 242, Add the same defensive guard used in the fast image handler
before calling billing.createUsageEvent: check that billing.createUsageEvent is
defined (e.g., if (billing?.createUsageEvent)) and only call it when present; if
it's absent, skip the call or handle the fallback accordingly, then keep the
existing result/error handling logic for the response variable (result) once the
call is made.

@BrooksFlannery

Copy link
Copy Markdown
Collaborator

Code Review - PR #14: SDK Upgrade to v0.16.2

Issues by Severity


Issue #1: Multiple Files

Severity: Low
Description: Inconsistent transactionId usage across usage event handlers - some handlers explicitly generate idempotency tokens while others rely on SDK auto-generation. While functionally valid (the SDK creates a transactionId automatically if not provided), the inconsistency may cause confusion for developers using these examples as reference.

Affected locations:

  • nextjs/generation-based-subscription/src/app/home-client.tsx:175-178 (SDK auto-generates)
  • nextjs/generation-based-subscription/src/app/home-client.tsx:221-225 (explicitly provided)
  • nextjs/pay-as-you-go/src/app/home-client.tsx:116-119 (SDK auto-generates)
  • tanstack-start/generation-based-subscription/src/routes/index.tsx:184-187 (SDK auto-generates)
  • tanstack-start/generation-based-subscription/src/routes/index.tsx:231-235 (explicitly provided)

Recommendation: Consider either removing all explicit transactionId generation (since SDK handles it) or adding it consistently with a comment explaining when manual IDs are useful (e.g., correlating with external systems).


Issue #2: Multiple Files

Severity: Low
Description: Inconsistent error property access - some files access result.error.json?.error while others access result.error.json?.message.

Question for author: Which property is the correct one to check per the SDK's error response schema?

Affected locations:

  • nextjs/generation-based-subscription/src/app/home-client.tsx:182 - uses .error
  • nextjs/pay-as-you-go/src/app/home-client.tsx:123 - uses .message
  • nextjs/tiered-usage-gated-subscription/src/app/home-client.tsx:258 - uses .error
  • nextjs/usage-limit-subscription/src/app/home-client.tsx:179 - uses .error

Current code (pay-as-you-go using .message):

if ('error' in result) {
  throw new Error(
    (result.error.json?.message as string) ||
      'Failed to create usage event'
  );
}

Current code (others using .error):

if ('error' in result) {
  throw new Error(
    (result.error.json?.error as string) || 'Failed to create usage event'
  );
}

Issue #3: Multiple Files

Severity: Low
Description: Unsafe type assertion as string on error property that may not exist or may not be a string.

Affected locations:

  • nextjs/generation-based-subscription/src/app/home-client.tsx:182
  • nextjs/generation-based-subscription/src/app/home-client.tsx:229
  • nextjs/pay-as-you-go/src/app/home-client.tsx:123
  • nextjs/tiered-usage-gated-subscription/src/app/home-client.tsx:258
  • nextjs/usage-limit-subscription/src/app/home-client.tsx:179
  • tanstack-start/generation-based-subscription/src/routes/index.tsx:191
  • tanstack-start/generation-based-subscription/src/routes/index.tsx:239

Current code:

if ('error' in result) {
  throw new Error(
    (result.error.json?.error as string) || 'Failed to create usage event'
  );
}

Fix:

if ('error' in result) {
  const errorMsg = result.error.json?.error ?? result.error.json?.message;
  throw new Error(
    (typeof errorMsg === 'string' ? errorMsg : null) || 
    'Failed to create usage event'
  );
}

Issue #4: tanstack-start/generation-based-subscription/src/routes/index.tsx:67

Severity: Low
Description: Optional chaining inconsistency - session?.user.id should be session?.user?.id for safety.

Current code:

useEffect(() => {
  const currentUserId = session?.user.id
  if (
    currentUserId &&
    currentUserId !== previousUserIdRef.current &&
    billing.loaded &&
    billing.reload
  ) {
    // ...
  }
}, [session?.user.id, billing])

Fix:

useEffect(() => {
  const currentUserId = session?.user?.id
  if (
    currentUserId &&
    currentUserId !== previousUserIdRef.current &&
    billing.loaded &&
    billing.reload
  ) {
    // ...
  }
}, [session?.user?.id, billing])

Summary

Severity Count
Critical 0
High 0
Medium 0
Low 4

Quality Gate: PASS - No blocking issues. The PR represents a clean SDK upgrade with good simplification of the example code. The issues noted are minor consistency improvements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @tanstack-start/generation-based-subscription/src/routes/index.tsx:
- Around line 226-241: The code calls billing.createUsageEvent without checking
it exists; add the same defensive guard used in handleGenerateFastImage by
verifying billing.createUsageEvent is a function before awaiting it, and if
missing either skip usage tracking or throw a clear error (e.g.,
“billing.createUsageEvent not available”), updating the try block around
billing.createUsageEvent to mirror the guard in handleGenerateFastImage and keep
the existing error-handling logic intact.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23c4722 and 78c906d.

📒 Files selected for processing (5)
  • nextjs/generation-based-subscription/src/app/home-client.tsx
  • nextjs/pay-as-you-go/src/app/home-client.tsx
  • nextjs/tiered-usage-gated-subscription/src/app/home-client.tsx
  • nextjs/usage-limit-subscription/src/app/home-client.tsx
  • tanstack-start/generation-based-subscription/src/routes/index.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • nextjs/generation-based-subscription/src/app/home-client.tsx
  • nextjs/pay-as-you-go/src/app/home-client.tsx
  • nextjs/tiered-usage-gated-subscription/src/app/home-client.tsx
🔇 Additional comments (5)
nextjs/usage-limit-subscription/src/app/home-client.tsx (2)

147-152: LGTM! Clean migration to SDK-managed usage events.

The runtime guard ensures createUsageEvent is available before invocation, and omitting transactionId correctly delegates idempotency key generation to the SDK as intended by the v0.16.2 upgrade.


154-160: Well-handled error normalization.

This correctly addresses the previous review feedback:

  • Checks both result.error.json?.error and result.error.json?.message for SDK schema flexibility
  • Uses typeof === 'string' validation instead of unsafe type assertion
  • Provides a sensible fallback message

Clean implementation.

tanstack-start/generation-based-subscription/src/routes/index.tsx (3)

17-22: LGTM!

The trailing comma is a good practice for cleaner git diffs.


66-81: LGTM!

The optional chaining session?.user?.id correctly guards against undefined access when session exists but user does not. Dependency array is consistent with the accessed property.


178-195: LGTM! Clean migration to SDK method with proper error handling.

The implementation correctly:

  • Guards against createUsageEvent unavailability before use
  • Uses nullish coalescing to check both error and message properties
  • Validates error message type before using it
  • Falls back to a descriptive default message

This aligns well with the PR objective to replace manual fetch calls with billing.createUsageEvent().

Comment on lines 226 to 241
try {
// Generate a unique transaction ID for idempotency
const transactionId = `hd_video_${Date.now()}_${Math.random().toString(36).substring(7)}`
// Random amount between 1-3 minutes
const amount = Math.floor(Math.random() * 3) + 1

const response = await fetch('/api/usage-events', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
usageMeterSlug: 'hd_video_minutes',
amount,
transactionId,
}),
const result = await billing.createUsageEvent({
usageMeterSlug: 'hd_video_minutes',
amount,
})

if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Failed to create usage event')
if ('error' in result) {
const errorMsg = result.error.json?.error ?? result.error.json?.message
throw new Error(
(typeof errorMsg === 'string' ? errorMsg : null) ||
'Failed to create usage event',
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add guard for createUsageEvent availability for consistency.

handleGenerateFastImage checks billing.createUsageEvent existence before use (lines 178-180), but this handler does not. For consistency and defensive coding, add the same guard.

Proposed fix
     try {
+      if (!billing.createUsageEvent) {
+        throw new Error('createUsageEvent is not available')
+      }
       // Random amount between 1-3 minutes
       const amount = Math.floor(Math.random() * 3) + 1

       const result = await billing.createUsageEvent({
📝 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.

Suggested change
try {
// Generate a unique transaction ID for idempotency
const transactionId = `hd_video_${Date.now()}_${Math.random().toString(36).substring(7)}`
// Random amount between 1-3 minutes
const amount = Math.floor(Math.random() * 3) + 1
const response = await fetch('/api/usage-events', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
usageMeterSlug: 'hd_video_minutes',
amount,
transactionId,
}),
const result = await billing.createUsageEvent({
usageMeterSlug: 'hd_video_minutes',
amount,
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Failed to create usage event')
if ('error' in result) {
const errorMsg = result.error.json?.error ?? result.error.json?.message
throw new Error(
(typeof errorMsg === 'string' ? errorMsg : null) ||
'Failed to create usage event',
)
}
try {
if (!billing.createUsageEvent) {
throw new Error('createUsageEvent is not available')
}
// Random amount between 1-3 minutes
const amount = Math.floor(Math.random() * 3) + 1
const result = await billing.createUsageEvent({
usageMeterSlug: 'hd_video_minutes',
amount,
})
if ('error' in result) {
const errorMsg = result.error.json?.error ?? result.error.json?.message
throw new Error(
(typeof errorMsg === 'string' ? errorMsg : null) ||
'Failed to create usage event',
)
}
🤖 Prompt for AI Agents
In @tanstack-start/generation-based-subscription/src/routes/index.tsx around
lines 226 - 241, The code calls billing.createUsageEvent without checking it
exists; add the same defensive guard used in handleGenerateFastImage by
verifying billing.createUsageEvent is a function before awaiting it, and if
missing either skip usage tracking or throw a clear error (e.g.,
“billing.createUsageEvent not available”), updating the try block around
billing.createUsageEvent to mirror the guard in handleGenerateFastImage and keep
the existing error-handling logic intact.

@agreea agreea merged commit 3377976 into main Jan 14, 2026
2 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.

update examples to use sdk v0.16.2

3 participants