feat: adds X-HTTP-Method-Override header (#6487)
Fixes: https://github.com/payloadcms/payload/issues/6486 Adds `X-HTTP-Method-Override` header to allow for sending query params in the body of a POST request. This is useful when the query param string hits the upper limit.
This commit is contained in:
committed by
GitHub
parent
78db50a497
commit
7bb2e3be76
@@ -618,3 +618,45 @@ export const Orders: CollectionConfig = {
|
||||
**req** will have the **payload** object and can be used inside your endpoint handlers for making
|
||||
calls like req.payload.find() that will make use of access control and hooks.
|
||||
</Banner>
|
||||
|
||||
## Method Override for GET Requests
|
||||
|
||||
Payload supports a method override feature that allows you to send GET requests using the HTTP POST method. This can be particularly useful in scenarios when the query string in a regular GET request is too long.
|
||||
|
||||
### How to Use
|
||||
|
||||
To use this feature, include the `X-HTTP-Method-Override` header set to `GET` in your POST request. The parameters should be sent in the body of the request with the `Content-Type` set to `application/x-www-form-urlencoded`.
|
||||
|
||||
### Example
|
||||
|
||||
Here is an example of how to use the method override to perform a GET request:
|
||||
|
||||
#### Using Method Override (POST)
|
||||
|
||||
```ts
|
||||
const res = await fetch(`${api}/${collectionSlug}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-HTTP-Method-Override': 'GET',
|
||||
},
|
||||
body: qs.stringify({
|
||||
depth: 1,
|
||||
locale: 'en',
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
#### Equivalent Regular GET Request
|
||||
|
||||
```ts
|
||||
const res = await fetch(`${api}/${collectionSlug}?depth=1&locale=en`, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -407,6 +407,11 @@ export const POST =
|
||||
let res: Response
|
||||
let collection: Collection
|
||||
|
||||
const overrideHttpMethod = request.headers.get('X-HTTP-Method-Override')
|
||||
if (overrideHttpMethod === 'GET') {
|
||||
return await GET(config)(request, { params: { slug } })
|
||||
}
|
||||
|
||||
try {
|
||||
req = await createPayloadRequest({
|
||||
config,
|
||||
|
||||
@@ -58,6 +58,17 @@ export const createPayloadRequest = async ({
|
||||
fallbackLocale = locales.fallbackLocale
|
||||
}
|
||||
|
||||
const overrideHttpMethod = request.headers.get('X-HTTP-Method-Override')
|
||||
const queryToParse = overrideHttpMethod === 'GET' ? await request.text() : urlProperties.search
|
||||
|
||||
const query = queryToParse
|
||||
? qs.parse(queryToParse, {
|
||||
arrayLimit: 1000,
|
||||
depth: 10,
|
||||
ignoreQueryPrefix: true,
|
||||
})
|
||||
: {}
|
||||
|
||||
const customRequest: CustomPayloadRequestProperties = {
|
||||
context: {},
|
||||
fallbackLocale,
|
||||
@@ -74,13 +85,7 @@ export const createPayloadRequest = async ({
|
||||
payloadUploadSizes: {},
|
||||
port: urlProperties.port,
|
||||
protocol: urlProperties.protocol,
|
||||
query: urlProperties.search
|
||||
? qs.parse(urlProperties.search, {
|
||||
arrayLimit: 1000,
|
||||
depth: 10,
|
||||
ignoreQueryPrefix: true,
|
||||
})
|
||||
: {},
|
||||
query,
|
||||
routeParams: params || {},
|
||||
search: urlProperties.search,
|
||||
searchParams: urlProperties.searchParams,
|
||||
|
||||
@@ -201,11 +201,15 @@ const RelationshipField: React.FC<RelationshipFieldProps> = (props) => {
|
||||
query.where.and.push(relationFilterOption)
|
||||
}
|
||||
|
||||
const response = await fetch(`${serverURL}${api}/${relation}?${qs.stringify(query)}`, {
|
||||
const response = await fetch(`${serverURL}${api}/${relation}`, {
|
||||
body: qs.stringify(query),
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-HTTP-Method-Override': 'GET',
|
||||
},
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
@@ -326,11 +330,15 @@ const RelationshipField: React.FC<RelationshipFieldProps> = (props) => {
|
||||
}
|
||||
|
||||
if (!errorLoading) {
|
||||
const response = await fetch(`${serverURL}${api}/${relation}?${qs.stringify(query)}`, {
|
||||
const response = await fetch(`${serverURL}${api}/${relation}`, {
|
||||
body: qs.stringify(query),
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-HTTP-Method-Override': 'GET',
|
||||
},
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
const collection = collections.find((coll) => coll.slug === relation)
|
||||
|
||||
@@ -569,6 +569,58 @@ describe('fields - relationship', () => {
|
||||
).toHaveCount(15)
|
||||
})
|
||||
})
|
||||
|
||||
describe('field relationship with many items', () => {
|
||||
beforeEach(async () => {
|
||||
const relations: string[] = []
|
||||
const batchSize = 10
|
||||
const totalRelations = 300
|
||||
const totalBatches = Math.ceil(totalRelations / batchSize)
|
||||
for (let i = 0; i < totalBatches; i++) {
|
||||
const batchPromises: Promise<RelationOne>[] = []
|
||||
const start = i * batchSize
|
||||
const end = Math.min(start + batchSize, totalRelations)
|
||||
|
||||
for (let j = start; j < end; j++) {
|
||||
batchPromises.push(
|
||||
payload.create({
|
||||
collection: relationOneSlug,
|
||||
data: {
|
||||
name: 'relation',
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const batchRelations = await Promise.all(batchPromises)
|
||||
relations.push(...batchRelations.map((doc) => doc.id))
|
||||
}
|
||||
|
||||
await payload.update({
|
||||
id: docWithExistingRelations.id,
|
||||
collection: slug,
|
||||
data: {
|
||||
relationshipHasMany: relations,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('should update with new relationship', async () => {
|
||||
await page.goto(url.edit(docWithExistingRelations.id))
|
||||
|
||||
const field = page.locator('#field-relationshipHasMany')
|
||||
const dropdownIndicator = field.locator('.dropdown-indicator')
|
||||
await dropdownIndicator.click({ delay: 100 })
|
||||
|
||||
const options = page.locator('.rs__option')
|
||||
await expect(options).toHaveCount(2)
|
||||
|
||||
await options.nth(0).click()
|
||||
await expect(field).toContainText(relationOneDoc.id)
|
||||
|
||||
await saveDocAndAssert(page)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
async function clearAllDocs(): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user