api-upload-hono
@rtorcato/api-upload-hono is the Hono counterpart to
api-upload. It uploads a single file from a multipart Hono
request straight to S3 — parsing the body with c.req.parseBody() and writing
the object with the AWS SDK directly (Hono has no multer/multer-s3 to wrap) —
and resolves with the stored object. Failures come back as
api-errors HttpErrors so they slot into the error handler.
Install
pnpm add @rtorcato/api-upload-hono @aws-sdk/client-s3 hono
@aws-sdk/client-s3 (v3) and hono (v4) are peer dependencies — you bring your
own versions and S3 client.
Usage
import { S3Client } from '@aws-sdk/client-s3'
import { uploadFile } from '@rtorcato/api-upload-hono'
const s3 = new S3Client({ region: 'us-east-1' })
app.post('/avatar', async (c) => {
const file = await uploadFile(c, {
s3,
bucket: 'avatars',
field: 'avatar',
key: (ctx) => `users/${ctx.get('userId')}.png`,
isPublic: true,
maxSizeBytes: 5 * 1024 * 1024,
})
return c.json({ url: file.location })
})
key can be a string or a function (c, file) => string.
Errors
uploadFile rejects with an HttpError:
413 file_too_large— the upload exceedsmaxSizeBytes(checked viaContent-Lengthup front, then re-checked once the bytes are buffered).400 no_file— the field was empty or not a file.
Notes
- ACL —
isPublicsetspublic-read/private. Buckets with Object Ownership bucket-owner-enforced reject ACLs; leaveisPublicunset and use a bucket policy instead. - In memory — the file is buffered in memory (Hono's
parseBody()already buffers). Great for avatars and documents; hand out a presignedPUTfor multi-GB uploads. location— the virtual-hosted AWS URL from the client's region. For a custom endpoint (MinIO, R2, a CDN), build the URL yourself frombucket+key.
Related
- api-upload — the Express counterpart (multer-s3)
- api-errors — the
HttpErrorclasses failures are normalized to