Skip to content

Conversation

@JarrodMFlesch
Copy link
Contributor

@JarrodMFlesch JarrodMFlesch commented Jan 5, 2026

Issue

URL field values can be set as full URLs, but should be stored as paths if they are on the local file system. Previously this was not being handled which is not portable, you should be able to change your serverURL without needing to update the DB documents.

This PR adds a beforeChange hook to the url fields that converts the data to the correct relative url for local files. If the url is external, then that gets returned as is.

Why

Flow before:

  • create --> no URL present --> afterRead returns URL --> update happens with that returned URL and it is saved

New Flow:

  • create --> beforeChange hook creates the URL --> saved in DB

The new flow also ensures URLs are saved as relative URLs when stored on the local filesystem. This makes files more portable, removing the need to update the DB i.e. if you change your domain for example.

URL fields are stored in the DB as:

  • relative when on the local filesystem i.e. /:apiRoute/:collectionSlug/file/:filename
  • absolute when external i.e. https://some-fake-external-website.com/images/1

URL field values are returned as:

  • absolute if serverURL is set on the config i.e. https://your-domain.com/:apiRoute/:collectionSlug/file/:filename
  • relative if serverURL is not set and the value is a relative path i.e. /:apiRoute/:collectionSlug/file/:filename
  • absolute if the url is external i.e. https://some-fake-external-website.com/images/1

The majority of the files added in this PR were moved from the imageResizer file.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 5, 2026

📦 esbuild Bundle Analysis for payload

This analysis was generated by esbuild-bundle-analyzer. 🤖

Meta File Out File Size (raw) Note
packages/next/meta_index.json esbuild/index.js 934.87 KB 🆕 Added
packages/payload/meta_index.json esbuild/index.js 1.24 MB 🆕 Added
packages/payload/meta_shared.json esbuild/exports/shared.js 164.31 KB 🆕 Added
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 281.24 KB 🆕 Added
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 1.16 MB 🆕 Added
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 15.65 KB 🆕 Added
Largest paths These visualization shows top 20 largest paths in the bundle.

Meta file: packages/next/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████████████▌ }}}$ 82.4%, 766.76 KB
dist/views/Version ${{\color{Goldenrod}{ █▎ }}}$ 5.4%, 50.31 KB
dist/views/Dashboard ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 16.48 KB
dist/views/Document ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 15.42 KB
dist/views/List ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 11.27 KB
dist/views/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 9.03 KB
dist/views/API ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.03 KB
dist/views/Versions ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 5.98 KB
dist/elements/Nav ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 5.53 KB
dist/views/Account ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 5.46 KB
dist/elements/DocumentHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 4.81 KB
dist/views/Login ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 4.41 KB
dist/views/ForgotPassword ${{\color{Goldenrod}{ }}}$ 0.3%, 3.09 KB
dist/layouts/Root ${{\color{Goldenrod}{ }}}$ 0.3%, 2.91 KB
dist/views/CreateFirstUser ${{\color{Goldenrod}{ }}}$ 0.3%, 2.81 KB
dist/templates/Default ${{\color{Goldenrod}{ }}}$ 0.3%, 2.63 KB
dist/views/BrowseByFolder ${{\color{Goldenrod}{ }}}$ 0.3%, 2.56 KB
dist/views/CollectionFolders ${{\color{Goldenrod}{ }}}$ 0.3%, 2.44 KB
dist/views/ResetPassword ${{\color{Goldenrod}{ }}}$ 0.3%, 2.40 KB
dist/views/Logout ${{\color{Goldenrod}{ }}}$ 0.2%, 1.94 KB
(other) ${{\color{Goldenrod}{ ████▍ }}}$ 17.6%, 163.43 KB

Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ █████████████████▏ }}}$ 68.5%, 842.04 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 43.33 KB
dist/collections/operations ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 36.43 KB
dist/auth/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 15.21 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 12.33 KB
dist/queues/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 12.05 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 12.04 KB
dist/globals/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 11.98 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.21 KB
dist/bin/generateImportMap ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.53 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.90 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.74 KB
dist/index.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.65 KB
dist/database/migrations ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.50 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.27 KB
dist/collections/endpoints ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.00 KB
dist/config/sanitize.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 5.57 KB
dist/auth/strategies ${{\color{Goldenrod}{ }}}$ 0.4%, 5.50 KB
dist/auth/endpoints ${{\color{Goldenrod}{ }}}$ 0.4%, 5.42 KB
dist/utilities/telemetry ${{\color{Goldenrod}{ }}}$ 0.4%, 5.31 KB
(other) ${{\color{Goldenrod}{ ███████▉ }}}$ 31.5%, 387.65 KB

Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████████████████▋ }}}$ 78.9%, 126.93 KB
dist/fields/validations.js ${{\color{Goldenrod}{ █▌ }}}$ 6.4%, 10.21 KB
dist/fields/baseFields ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 2.79 KB
dist/utilities/deepCopyObject.js ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 2.51 KB
dist/auth/cookies.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 1.55 KB
dist/utilities/flattenTopLevelFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 1.42 KB
dist/fields/config ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 1.28 KB
dist/utilities/flattenAllFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 943 B
dist/folders/utils ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 916 B
dist/utilities/getVersionsConfig.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 895 B
dist/utilities/unflatten.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 779 B
dist/utilities/sanitizeUserDataForEmail.js ${{\color{Goldenrod}{ }}}$ 0.4%, 713 B
dist/utilities/getFieldPermissions.js ${{\color{Goldenrod}{ }}}$ 0.4%, 651 B
dist/collections/config ${{\color{Goldenrod}{ }}}$ 0.4%, 570 B
dist/bin/generateImportMap ${{\color{Goldenrod}{ }}}$ 0.3%, 561 B
dist/auth/sessions.js ${{\color{Goldenrod}{ }}}$ 0.3%, 525 B
dist/utilities/getSafeRedirect.js ${{\color{Goldenrod}{ }}}$ 0.3%, 423 B
dist/utilities/deepMerge.js ${{\color{Goldenrod}{ }}}$ 0.3%, 413 B
dist/utilities/formatLabels.js ${{\color{Goldenrod}{ }}}$ 0.2%, 380 B
dist/utilities/appendUploadSelectFields.js ${{\color{Goldenrod}{ }}}$ 0.2%, 360 B
(other) ${{\color{Goldenrod}{ █████▎ }}}$ 21.1%, 33.88 KB

Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/features/blocks ${{\color{Goldenrod}{ ███▏ }}}$ 12.6%, 35.02 KB
dist/lexical/plugins ${{\color{Goldenrod}{ ██▉ }}}$ 11.5%, 32.00 KB
dist/lexical/ui ${{\color{Goldenrod}{ ██▏ }}}$ 8.8%, 24.36 KB
dist/features/experimental_table ${{\color{Goldenrod}{ ██▏ }}}$ 8.5%, 23.70 KB
dist/packages/@lexical ${{\color{Goldenrod}{ █▋ }}}$ 6.8%, 18.99 KB
dist/features/link ${{\color{Goldenrod}{ █▋ }}}$ 6.5%, 18.11 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▌ }}}$ 6.4%, 17.75 KB
dist/features/upload ${{\color{Goldenrod}{ █▎ }}}$ 5.0%, 13.77 KB
dist/features/textState ${{\color{Goldenrod}{ █ }}}$ 4.0%, 11.08 KB
dist/features/relationship ${{\color{Goldenrod}{ ▊ }}}$ 3.2%, 9.03 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 8.22 KB
dist/features/debug ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 7.39 KB
dist/utilities/fieldsDrawer ${{\color{Goldenrod}{ ▋ }}}$ 2.6%, 7.12 KB
dist/features/converters ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 7.05 KB
dist/lexical/config ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 5.08 KB
dist/features/lists ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 5.00 KB
dist/features/format ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 3.46 KB
dist/lexical/LexicalEditor.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.17 KB
dist/lexical/theme ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.62 KB
dist/features/indent ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.50 KB
(other) ${{\color{Goldenrod}{ █████████████████████▊ }}}$ 87.4%, 242.96 KB

Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████▍ }}}$ 49.6%, 572.94 KB
dist/elements/FolderView ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 29.29 KB
dist/elements/BulkUpload ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 27.20 KB
dist/elements/WhereBuilder ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 16.91 KB
dist/views/Edit ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 16.39 KB
dist/fields/Relationship ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 15.78 KB
dist/elements/Table ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.48 KB
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 15.26 KB
dist/fields/Upload ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 14.15 KB
dist/fields/Blocks ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 13.69 KB
dist/elements/QueryPresets ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 10.33 KB
dist/elements/PublishButton ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 8.82 KB
dist/providers/Folders ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.47 KB
dist/elements/LivePreview ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.38 KB
dist/elements/ListHeader ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.90 KB
dist/elements/HTMLDiff ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.81 KB
dist/fields/Array ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 7.73 KB
dist/views/CollectionFolder ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.38 KB
dist/elements/ReactSelect ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.31 KB
dist/views/List ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.99 KB
(other) ${{\color{Goldenrod}{ ████████████▌ }}}$ 50.4%, 581.83 KB

Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ █████▏ }}}$ 20.8%, 3.12 KB
../../node_modules ${{\color{Goldenrod}{ ████▍ }}}$ 17.7%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██▌ }}}$ 10.2%, 1.52 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ ██▏ }}}$ 8.8%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▍ }}}$ 5.7%, 862 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █▎ }}}$ 5.4%, 812 B
dist/utilities/api.js ${{\color{Goldenrod}{ █▎ }}}$ 5.0%, 756 B
dist/utilities/getGlobalData.js ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 528 B
dist/elements/Translation ${{\color{Goldenrod}{ ▊ }}}$ 3.3%, 493 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 440 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▌ }}}$ 2.3%, 339 B
dist/utilities/getVisibleEntities.js ${{\color{Goldenrod}{ ▌ }}}$ 2.2%, 329 B
dist/utilities/getNavGroups.js ${{\color{Goldenrod}{ ▌ }}}$ 2.0%, 301 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 232 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 180 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 159 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 152 B
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 147 B
dist/utilities/abortAndIgnore.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 146 B
dist/utilities/hasSavePermission.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 136 B
(other) ${{\color{Goldenrod}{ ███████████████████▊ }}}$ 79.2%, 11.87 KB
Details

Next to the size is how much the size has increased or decreased compared with the base branch of this PR.

  • ‼️: Size increased by 20% or more. Special attention should be given to this.
  • ⚠️: Size increased in acceptable range (lower than 20%).
  • ✅: No change or even downsized.
  • 🗑️: The out file is deleted: not found in base branch.
  • 🆕: The out file is newly found: will be added to base branch.

@jhb-dev
Copy link
Contributor

jhb-dev commented Jan 5, 2026

Hi @JarrodMFlesch, it seems like this PR is somewhat related to the following issue I created a while back: #14562

@JarrodMFlesch
Copy link
Contributor Author

JarrodMFlesch commented Jan 5, 2026

@jhb-dev yes this should resolve that open issue since it uses a before change hook to set the correct url.

I brought up the issue regarding the /api/media/file/filename being stored earlier today as well. Personally I would like to see it just stored as undefined or null if the file is stored locally. But decided to keep it as is until 4.0 since it is technically a breaking change.

@paulpopus paulpopus requested a review from Copilot January 7, 2026 20:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR ensures that upload URL fields store relative paths in the database for local files while maintaining absolute URLs for external resources. This change makes the database portable across different deployment environments without requiring URL updates when the serverURL changes.

Key Changes:

  • Adds beforeChange hooks to URL fields that convert full URLs to relative paths for local files before database storage
  • Refactors image resizing logic by splitting the monolithic imageResizer.ts into smaller, focused modules
  • Updates tests to verify relative URLs are stored in the database while absolute URLs are returned via API

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/uploads/int.spec.ts Adds comprehensive tests verifying relative URL storage in database for create, update, and duplicate operations
test/uploads/e2e.spec.ts Updates assertions to use regex patterns instead of exact string matches for URL validation
test/uploads/config.ts Sets serverURL to enable full URL testing scenarios
packages/payload/src/uploads/types.ts Adds optional url field to FileSize type and exports FocalPoint type
packages/payload/src/uploads/getBaseFields.ts Adds beforeChange hooks to convert URLs to relative paths before database storage
packages/payload/src/uploads/generateFilePathOrURL.ts New utility to generate file paths or URLs with control over relative/absolute format
packages/payload/src/uploads/generateFileData.ts Updates focal point handling and fixes focal point coordinate assignment bug
packages/payload/src/uploads/image-resizing/*.ts Refactored image resizing functionality into modular components

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@DanRibbens DanRibbens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good other than the test config change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot edit image when serverURL is set Upload collection url and thumbnailURL fields are inconsistently stored in the database

4 participants