Skip to content

Document presigned storage upload flow and endpoints#133

Merged
yash-pouranik merged 4 commits intomainfrom
copilot/docs-update-presigned-url-endpoints
Apr 23, 2026
Merged

Document presigned storage upload flow and endpoints#133
yash-pouranik merged 4 commits intomainfrom
copilot/docs-update-presigned-url-endpoints

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 23, 2026

The storage guide still described the legacy server-proxied upload while the platform now uses a presigned URL three-step flow with new public endpoints. This updates the docs to reflect the current upload architecture, CORS requirements, and SDK behavior.

  • Storage guide: presigned flow + endpoints
    • Describe /api/storage/upload-request and /api/storage/upload-confirm, responses, and quota/size semantics.
    • Detail the three-step client flow and external S3/R2 CORS requirement.
  • Examples + troubleshooting alignment
    • Update browser example for request/PUT/confirm sequence with basic validation and error handling.
    • Adjust troubleshooting for new request/confirm payload expectations.

Example (presigned upload flow):

const requestRes = await fetch('https://api.ub.bitbros.in/api/storage/upload-request', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-api-key': 'YOUR_KEY' },
  body: JSON.stringify({ filename: file.name, contentType: file.type, size: file.size })
});
const { signedUrl, filePath } = await requestRes.json();

const uploadRes = await fetch(signedUrl, {
  method: 'PUT',
  headers: { 'Content-Type': file.type },
  body: file
});
if (!uploadRes.ok) throw new Error('Upload failed');

const confirmRes = await fetch('https://api.ub.bitbros.in/api/storage/upload-confirm', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-api-key': 'YOUR_KEY' },
  body: JSON.stringify({ filePath, size: file.size })
});

Copilot AI changed the title [WIP] Document presigned URL upload endpoints Document presigned storage upload flow and endpoints Apr 23, 2026
Copilot AI requested a review from yash-pouranik April 23, 2026 19:38
@yash-pouranik yash-pouranik marked this pull request as ready for review April 23, 2026 19:39
Copilot AI review requested due to automatic review settings April 23, 2026 19:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the Storage guide to reflect the current presigned URL upload architecture (request URL → direct PUT to storage → confirm upload), including new endpoints and client-side flow requirements.

Changes:

  • Replaces legacy multipart /api/storage/upload documentation with a 3-step presigned URL flow using /api/storage/upload-request and /api/storage/upload-confirm.
  • Adds an updated browser example demonstrating request/PUT/confirm sequencing.
  • Updates troubleshooting guidance and adds a CORS note for external S3/R2 PUT uploads.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

},
body: JSON.stringify({ filePath, size: file.size })
});

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The example also parses/logs the confirm response without checking confirmRes.ok. Since /api/storage/upload-confirm can return non-2xx (e.g., 409 UPLOAD_NOT_READY, 400 size mismatch), add an explicit status check and show how to handle the retryable 409 case before consuming the JSON.

Suggested change
if (!confirmRes.ok) {
if (confirmRes.status === 409) {
const retryableError = await confirmRes.json();
throw new Error(
`Upload not ready yet: ${retryableError.code || 'UPLOAD_NOT_READY'}. Retry the confirm request shortly.`
);
}
const errorBody = await confirmRes.json().catch(() => null);
throw new Error(
`Upload confirm failed: ${confirmRes.status} ${confirmRes.statusText}${
errorBody?.message ? ` - ${errorBody.message}` : ''
}`
);
}

Copilot uses AI. Check for mistakes.
Comment thread mintlify/docs/guides/storage.mdx Outdated
| `filePath` | Storage path — save this to confirm or delete the file later |

<Note>
This endpoint enforces the 10 MB max file size and checks quota headroom without charging it.
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The quota semantics described here should align with the documented project storage limit in the “Limits” section. The guide currently states 100 MB, but the server-side plan limits default to 20 MB on the free tier (packages/common/src/utils/planLimits.js). Please reconcile the docs (or clarify which plan the 100 MB value refers to) so the headroom/quota messaging is consistent.

Suggested change
This endpoint enforces the 10 MB max file size and checks quota headroom without charging it.
This endpoint enforces the 10 MB max file size and checks available headroom against your plan's project storage quota before the upload is confirmed. On the free tier, the default project storage limit is 20 MB.

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +39
{
"signedUrl": "https://storage.example.com/...",
"token": "optional-provider-token",
"filePath": "PROJECT_ID/avatar.png"
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The example filePath value is misleading. The API generates a unique path of the form ${projectId}/${uuid}_${sanitizedFilename} (spaces replaced with _), so PROJECT_ID/avatar.png is not representative. Consider documenting filePath as an opaque identifier and updating the example to match the actual format.

Copilot uses AI. Check for mistakes.
Comment on lines 85 to 90
```json
{
"message": "File uploaded successfully",
"url": "https://xyz.supabase.co/storage/v1/object/public/dev-files/PROJECT_ID/file.png",
"path": "PROJECT_ID/file.png",
"url": "https://xyz.supabase.co/storage/v1/object/public/dev-files/PROJECT_ID/avatar.png",
"provider": "internal"
}
```
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The documented confirm response shape doesn't match the API. POST /api/storage/upload-confirm returns at least message, path, and provider, and always includes url (which may be null) plus an optional warning field. Please update the response example/table to reflect the actual fields and to clarify that path is what you use for later deletion.

Copilot uses AI. Check for mistakes.
Comment thread mintlify/docs/guides/storage.mdx Outdated
| `url` | Publicly accessible CDN URL for the file |
| `path` | Storage path — save this to delete the file later |
| `provider` | The underlying storage provider used |
If the storage provider does not expose a public URL (for example, some external S3/R2 setups), the response includes a warning instead of `url`.
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

This sentence suggests warning replaces url, but the API returns url: null plus a warning message when a public URL isn't available. Reword to avoid implying url is omitted entirely, and mention the warning field explicitly.

Suggested change
If the storage provider does not expose a public URL (for example, some external S3/R2 setups), the response includes a warning instead of `url`.
If the storage provider does not expose a public URL (for example, some external S3/R2 setups), the response returns `url: null` and includes a `warning` field explaining that a public URL is not available.

Copilot uses AI. Check for mistakes.
Comment on lines +118 to +121
});

const { signedUrl, filePath } = await requestRes.json();

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The full browser example calls await requestRes.json() without checking requestRes.ok. If the server returns a non-2xx (e.g., 400/401/403/413), this will either throw or hide the actual error payload. Add an explicit status check (and ideally surface the returned error/message) before reading/parsing the JSON.

Copilot uses AI. Check for mistakes.
@yash-pouranik
Copy link
Copy Markdown
Collaborator

@copilot apply changes based on the comments in this thread

@yash-pouranik yash-pouranik temporarily deployed to copilot/docs-update-presigned-url-endpoints - urBackend-frankfrut PR #133 April 23, 2026 19:47 — with Render Destroyed
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 23, 2026

@copilot apply changes based on the comments in this thread

Applied the review-thread updates (response fields, filePath format, quota wording, and request/confirm error handling) in commit 49e6913.

Copilot AI requested a review from yash-pouranik April 23, 2026 19:48
@yash-pouranik yash-pouranik merged commit 152fa7d into main Apr 23, 2026
7 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.

docs(storage): document presigned URL upload endpoints introduced in PR #129

3 participants