Skip to content

feat: consolidate S3 client factory + presigned URL helpers into s3-utils#1024

Open
pyramation wants to merge 2 commits intomainfrom
devin/1776785805-s3-utils-consolidation
Open

feat: consolidate S3 client factory + presigned URL helpers into s3-utils#1024
pyramation wants to merge 2 commits intomainfrom
devin/1776785805-s3-utils-consolidation

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

@pyramation pyramation commented Apr 21, 2026

Summary

Makes @constructive-io/s3-utils the canonical, one-import package for S3 operations across the monorepo. Previously, three packages each created their own S3Client instances with duplicated provider-specific logic (path-style for MinIO, endpoint requirements for non-AWS providers, etc.).

New in s3-utils:

  • createS3Client(config) — unified factory supporting AWS S3, MinIO, R2, GCS, and DigitalOcean Spaces
  • presignPutUrl() / presignGetUrl() / headObject() — presigned URL helpers (wraps @aws-sdk/s3-request-presigner)
  • StorageConnectionConfig and StorageProvider types
  • S3ConfigError structured error class

Updated packages:

  • s3-streamergetS3() now delegates to createS3Client from s3-utils; imports S3Client type directly from @aws-sdk/client-s3 (moved from devDependencies to dependencies)
  • bucket-provisionercreateS3Client() now wraps s3-utils, re-throwing S3ConfigError as ProvisionerError for backward compat
  • graphile-settings/presigned-url-resolver.ts — uses createS3Client instead of new S3Client(...)
  • graphql/server/scripts/create-bucket.ts — uses createS3Client instead of new S3Client(...)

21 new unit tests cover the client factory and presigned URL helpers. All 84 existing bucket-provisioner tests continue to pass.

Updates since last revision

  • Removed anti-pattern: Removed export { S3Client } and export type { S3ClientConfig } re-exports from the s3-utils barrel. Third-party types are not re-exported — consumers that need the S3Client type import it directly from @aws-sdk/client-s3.

Review & Testing Checklist for Human

  • as any casts on client in presigned.ts:91,113client as any when calling getSignedUrl. Likely an AWS SDK version mismatch between @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner. Verify these resolve correctly at the locked versions
  • as any cast on cdn.provider in presigned-url-resolver.ts:70(cdn.provider || 'minio') as any bypasses type checking. Verify cdn.provider values from getEnvOptions() are actually valid StorageProvider strings, or add a proper type guard
  • err.code as ProvisionerErrorCode cast in bucket-provisioner/src/client.ts — assumes S3ConfigError codes map cleanly to ProvisionerErrorCode. Verify the union types align
  • Behavioral change in s3-streamer/s3.ts — old getS3() silently created a client with no credentials if keys were empty; new code throws S3ConfigError. Confirm no caller depends on the old lenient behavior
  • Type duplicationStorageConnectionConfig and StorageProvider are now defined in both s3-utils/src/client.ts and bucket-provisioner/src/types.ts. They're structurally identical but could drift. Consider whether bucket-provisioner should re-export from s3-utils instead

Suggested test plan: Run the create-bucket script against a local MinIO instance to verify client creation still works. If you have the graphile server running, test a presigned upload flow end-to-end to confirm the resolver changes don't regress.

Notes

  • The presigned URL helpers (presignPutUrl, presignGetUrl) are new API surface not yet consumed by any code in this PR — they're available for future use (e.g., to consolidate graphile-presigned-url-plugin/s3-signer.ts)
  • @aws-sdk/s3-request-presigner is a new dependency added to s3-utils
  • create-bucket.ts still has createS3Bucket(client as any, ...) — separate from the presigned casts, this may indicate a type mismatch between s3-utils' createS3Bucket param type and the S3Client returned by createS3Client

Link to Devin session: https://app.devin.ai/sessions/18879be982854a40abe5c9b915aa4a84
Requested by: @pyramation

…tils

- Add createS3Client() factory to @constructive-io/s3-utils as the
  canonical S3 client creation point for all providers (AWS, MinIO, R2,
  GCS, DigitalOcean Spaces)
- Add presignPutUrl(), presignGetUrl(), headObject() presigned URL helpers
- Re-export S3Client and S3ClientConfig from @aws-sdk/client-s3 so
  consumers don't need a direct dependency
- Update s3-streamer to delegate client creation to s3-utils
- Update bucket-provisioner to delegate client creation to s3-utils
  (with backward-compatible ProvisionerError re-throw)
- Update graphile-settings presigned-url-resolver to use createS3Client
- Update graphql/server create-bucket script to use createS3Client
- Add 21 unit tests for client factory + presigned URL helpers
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Remove S3Client and S3ClientConfig re-exports from s3-utils barrel.
Consumers should import these directly from @aws-sdk/client-s3.
Updated s3-streamer to import S3Client type from @aws-sdk/client-s3.
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.

1 participant