Skip to content

Native @aws-sdk/client-s3 (+ s3-request-presigner) and fastify multipart/form-data parsing #551

Description

@proggeramlug

Context

Porting sharity-api (Express + Mongoose, ~23k LoC) to Perry. Two
adjacent gaps block every upload flow: server-side multipart parsing
and AWS S3.

Multipart / form-data on fastify

The Fastify server in perry-ext-fastify handles JSON bodies, but every
upload route in the target app is multipart/form-data — avatars, cover
photos, item images. Need a multer-equivalent or fastify-multipart-style
plugin with:

  • Disk and memory storage modes
  • File size limits (limits: { fileSize: 5 * 1024 * 1024 })
  • MIME filter (fileFilter: (file) => /^image\//.test(file.mimetype))
  • Multiple field names (avatar, image, cover-photo)
  • Generated filenames (UUID + original extension)
  • Stream the file body without buffering the whole multipart payload in
    memory

AWS S3

Sharity-api uses S3 as the canonical image store. Local disk fallback
exists but isn't viable in production. Surface needed:

  • @aws-sdk/client-s3: PutObjectCommand, GetObjectCommand,
    DeleteObjectCommand, HeadObjectCommand
  • @aws-sdk/s3-request-presigner: getSignedUrl(command, {expiresIn})
  • Region + credential resolution from env (AWS_REGION,
    AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_S3_BUCKET_NAME)
  • ACL support (public-read is what the existing app uses)

Acceptance

A fastify route that accepts a multipart upload, streams it to S3 with
PutObjectCommand, and returns a presigned GET URL for the uploaded key.
Compiles and runs against real S3 (or LocalStack) under perry run.

Why both in one issue

The two together are how every upload endpoint in a typical Node API is
wired. Splitting them lets the multipart side ship without S3 (still
useful for disk-only setups), but the issue tracks both because
sharity-api needs both.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions