POST Strategy
WIPPresigned multipart/form-data uploads for small and medium files.
PostStrategy sends a file as a single multipart/form-data request using presigned form
fields (S3 POST policy style). It is the simplest option and is not resumable — a failure
restarts from zero.
import { PostStrategy } from '@gentleduck/upload/strategies'
strategies.set(PostStrategy())
PostStrategy() takes no runtime arguments; the four type parameters are inferred from the
registry.
Intent shape
Your createIntent returns PostStrategy.Intent:
type Intent = {
strategy: 'post'
fileId: string
url: string // presigned POST endpoint (form action)
fields: Record<string, string> // presigned policy fields (key, policy, signature…)
expiresAt?: string // optional expiry hint
}
Cursor shape
There is nothing to resume, so the cursor is empty:
type Cursor = Record<string, never>
type Cursors = { post?: PostStrategy.Cursor }
How it runs
start() hands the file and presigned fields to ctx.transport.postForm, which builds the
FormData (fields first, file last, as S3 requires), POSTs it, and streams progress:
async start(ctx) {
await ctx.transport.postForm({
url: ctx.intent.url,
file: ctx.file,
fields: ctx.intent.fields,
filename: ctx.file.name,
signal: ctx.signal,
onProgress: (uploadedBytes, totalBytes) => ctx.reportProgress({ uploadedBytes, totalBytes }),
})
}
If the intent is missing its url, the strategy throws a validation error.
When to use
- Small to medium files where resumability isn't needed.
- Backends that issue S3-style presigned POST policies.
- The simplest possible upload path.
For large files or unreliable connections, reach for the
Multipart Strategy instead. A common pattern is to register
both and let createIntent choose per file by size.