feat: per-table storage middleware — replace global mutations with typed per-table hooks#1064
Merged
pyramation merged 4 commits intomainfrom May 7, 2026
Merged
Conversation
…elete middleware on file tables Replace global requestUploadUrl/requestBulkUploadUrls mutations with per-table fields on @storageBuckets-tagged types. Replace global deleteFile mutation with middleware that wraps PostGraphile's generated delete* mutations on @storageFiles- tagged tables with S3 cleanup. Key changes: - plugin.ts: rewritten with GraphQLObjectType_fields hook for upload fields and GraphQLObjectType_fields_field hook for delete middleware - storage-module-cache.ts: add loadAllStorageModules() and resolveStorageConfigFromCodec() for codec-based scope resolution (no probing) - download-url-field.ts: use codec-based resolution instead of app-level only - s3-signer.ts: add deleteS3Object() function - upload.integration.test.ts: update to use per-table bucket query pattern - s3-signer.integration.test.ts: add deleteS3Object tests
- queries.ts: replace global mutation with buildRequestUploadUrlQuery() builder - upload.ts: query bucket by key, extract upload payload from nested response - types.ts: add bucketQueryField option, replace status with previousVersionId - index.ts: export new query builder + DEFAULT_BUCKET_QUERY_FIELD - upload.test.ts: mock executor returns per-table nested bucket response
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
- Replace bucketByKey (disabled by NoUniqueLookupPreset) with
buckets(condition: { key: $key }) { nodes { ... } } collection query
- Delete stale schema snapshots that referenced removed global mutations
(requestUploadUrl, requestBulkUploadUrls) — jest will regenerate them
- Replace condition (not available) with where: { key: { equalTo: ... } }
connection filter for bucket lookup in upload integration test
- Regenerate schema-snapshot and graphile-test introspection snapshots
(global mutations removed, per-table upload fields added)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the global
requestUploadUrl/requestBulkUploadUrls/deleteFilemutations with per-table PostGraphile hooks, keyed by@storageBucketsand@storageFilessmart tags.graphile-presigned-url-plugin:
plugin.ts: Rewritten —requestUploadUrlandrequestBulkUploadUrlsadded as fields on any type tagged@storageBuckets; delete middleware wrapsdelete*mutations on@storageFilestables with S3 cleanupstorage-module-cache.ts: NewloadAllStorageModules()+resolveStorageConfigFromCodec()helpers — matches codec's schema+table name against cached storage modules (no ownerId/probing needed)download-url-field.ts: Uses codec-based resolution for entity-scoped filess3-signer.ts: AddeddeleteS3Object()upload-client:
bucket.requestUploadUrl(...)fieldbucketQueryFieldoption for custom bucket collection field nameTests:
buckets(condition: { key: $key }) { nodes { ... } }(notbucketByKey— disabled byNoUniqueLookupPreset)Design: Each storage module (app-level, org-scoped, room-scoped, etc.) gets its own typed GraphQL API. No scope resolution needed — PostGraphile already knows which table each type comes from. The plugin detects smart tags at schema build time and augments the appropriate types.
Depends on: constructive-db PR #1037 (adds
@storageBucketssmart tag via newappend_smart_tagshelper)Review & Testing Checklist for Human
requestUploadUrlfield appears on bucket types in GraphiQL (on the Bucket type, not as a root mutation)requestUploadUrlfield → PUT to presigned URLdeleteAppFilemutation and check S3 object cleanupdownloadUrlstill works for entity-scoped files (not just app-level)requestUploadUrl,requestBulkUploadUrls) are gone from the schemaNotes
upload-clientnow takes abucketQueryFieldoption (defaults to'buckets') for apps with custom bucket table namesLink to Devin session: https://app.devin.ai/sessions/ffa3ed8652fc412f976accbdc229c88d
Requested by: @pyramation