How-to library · Developer docs

How-to library

Task-shaped pages: a title that starts with a verb, the situation in a few lines, the steps, the working code in both engines, and why this way rather than another. Found from a search, done, and left.

Send a long properties selector in a GET body

When a selector is too long for a URL, send it as the raw body of the GET, braces and all, with the header X-Body-Has-Parameters: 1. Only properties travels this way: filters, limit and orderBy stay in the query string, and a browser's fetch cannot send a GET body at all.

Context

Context. A selector that reaches into several relations and areas grows long, and every character is URL-encoded on the way. Past a server's or a proxy's limit, a URL is refused with a 414 or cut off. The REST API reads the properties selector from the body of a GET instead, when the request says so. It reads nothing else from there.

Before you start

A client that can send a body with GET: curl, PHP's Guzzle, Python's requests. Node's and the browser's fetch refuse to, so a server written for Node uses another HTTP library or keeps the selector in the URL.

Steps

  1. Write the selector as you would in the URL, with its outer braces: {id,name,fullPath,activeRevision{id,layout{id,name}}}.
  2. Send it as the raw body of the GET, with Content-Type: text/plain and the header X-Body-Has-Parameters: 1.
  3. Keep everything else in the query string: filters, limit, page, offset, orderBy.
  4. Check the answer is shaped. If it comes back as the full default record, the header did not arrive; if it is a 500 "Could not decode … as GraphQL query", the body was not the bare selector.

Code

Shell
curl -X GET 'https://www.example.com/cdna-api/pages/page/1885' \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'X-Body-Has-Parameters: 1' \
  --header 'Content-Type: text/plain' \
  --data-binary '{id,name,fullPath,activeRevision{id,layout{id,name}}}'

What this documentation site answered to the three ways of sending it:

Body and headerAnswer
The selector, with the header{"result":"success","data":{"id":1885,"name":"API sandbox","fullPath":"/api-sandbox","activeRevision":{"id":3447,"layout":{"id":30,"name":"Hub Page Sections"}}}}, the same as the selector in the query string
The selector, without the headerThe full default record: the header is what tells the API to read the body as the selector
properties={id,name} (without its outer braces), with the header500, "Could not decode properties={id,name} as GraphQL query": the body must be the bare selector, braces included

The same request from Python, with the filters left in the URL:

python
import os, requests

selector = '{id,name,fullPath,activeRevision{id,layout{id,name},areasContent}}'
response = requests.get(
    os.environ['COREDNA_URL'] + '/cdna-api/pages/pages',
    params={'parent.id': 72, 'limit': 50, 'orderBy': 'position:ASC'},   # still in the query string
    data=selector,                                                     # the body: the selector only
    headers={
        'Authorization': 'Bearer ' + os.environ['COREDNA_TOKEN'],
        'X-Body-Has-Parameters': '1',
        'Content-Type': 'text/plain',
        'Accept': 'application/json',
    },
    timeout=30,
)
response.raise_for_status()
pages = response.json()['data']

Why this and not that

Not a POST to carry the body: on a list route a POST is a create, and on a record route it is refused, so the method has to stay GET. Not a JSON body: only the bare selector is read, so filters put in a JSON body do nothing. Not shortening the selector with aliases: an alias renames a field in the answer, it does not make the request shorter. And first ask whether the selector needs to be that long: the default answer is large, but a selector naming forty fields is usually two screens' worth of data in one call.

Go deeper

Was this page helpful?

Your answer goes to the docs team.