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.

Read a page with its sections and images in one request

One GET with properties returns a page's name, address, layout and every layout area, with the components and image paths inside them. Read it by path, walk the collection by component layout, and take each layout area's value from the key its input type uses.

Context

Context. A headless front end needs everything one page shows: its title, its address, which layout it uses, and the content of every layout area, including the components in a collection and the images inside them. REST answers all of that in one request, because properties can ask for the live revision's areasContent. Nothing needs a second call per component or per image.

Before you start

An OAuth access token or an API key for a backend user who may read pages (Authentication). The request runs on your server, since the site sends no cross-origin headers.

Steps

  1. Address the page by its path or its id: GET /cdna-api/pages/page/developer-docs/api-reference/pages and GET /cdna-api/pages/page/836 read the same page.
  2. Ask for the fields and the live revision's areas: properties={id,name,fullPath,activeRevision{id,layout{id,name},areasContent}}.
  3. Choose how to render by the layout's id or name, and each component by its componentLayout.
  4. Read each layout area's value from the key its input type uses: text for text, rich text and select areas; active for a switch; imageId and imagePath for an image; collection for a component collection, whose elements carry their own areas the same way; component for a single component.
  5. Build image sizes from imagePath with the resize parameters (Resize an image from JavaScript), rather than storing copies.
  6. Cache the answer in your application, keyed by the URL: the platform caches no API response.

Code

HTTP
GET /cdna-api/pages/page/api-sandbox?properties={id,name,fullPath,activeRevision{id,layout{id,name},areasContent}}
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json

The answer on this documentation site's sandbox page, cut to two of its sections. Every layout area describes itself with its input type, and its value sits under one key:

JSON
{
  "result": "success",
  "data": {
    "id": 1885,
    "name": "API sandbox",
    "fullPath": "/api-sandbox",
    "activeRevision": {
      "id": 3482,
      "layout": {"id": 30, "name": "Hub Page Sections"},
      "areasContent": {
        "navSection": {"id": 90806, "type": "Text", "category": "text", "editor": "text", "text": "API sandbox"},
        "pageSections": {
          "id": 90798,
          "type": "Component Collection",
          "category": "collection",
          "collection": [
            {
              "componentId": 21105,
              "componentName": "Section A",
              "componentLayout": 40,
              "componentGlobal": false,
              "body": {"id": 90800, "type": "Richtext", "category": "text", "editor": "richtext", "text": "<p>Section A</p>"}
            },
            {
              "componentId": 21108,
              "componentName": "Sandbox figure",
              "componentLayout": 58,
              "componentGlobal": false,
              "image": {"id": 90803, "type": "Image", "category": "image", "imageId": 1185, "imagePath": "/files/images/api-sandbox/api-sandbox-figure.png"},
              "alt": {"id": 90804, "type": "Text", "category": "text", "editor": "text", "text": "A blue and green gradient"},
              "caption": {"id": 90805, "type": "Text", "category": "text", "editor": "text", "text": "Uploaded through the API"}
            }
          ]
        }
      }
    }
  }
}

A small reader that turns the layout areas into plain values, so the rest of the front end never sees the envelope:

JavaScript
// areas.js: turn areasContent into plain values
export function value(area) {
  if (!area || typeof area !== 'object') return area ?? null;
  switch (area.category) {
    case 'collection':
      return (area.collection ?? []).map(component);
    case 'component':
      return area.component ? component(area.component) : null;
    case 'image':
      return area.imageId ? { id: area.imageId, path: area.imagePath } : null;
    default:
      if ('active' in area) return area.active;          // a switch
      return area.text ?? null;                           // text, rich text, select, date
  }
}

function component(c) {
  const areas = {};
  for (const [name, area] of Object.entries(c)) {
    if (!name.startsWith('component')) areas[name] = value(area);
  }
  return { layout: c.componentLayout, name: c.componentName, global: c.componentGlobal, areas };
}

// usage
const { data } = await response.json();
const areas = Object.fromEntries(
  Object.entries(data.activeRevision.areasContent).map(([name, area]) => [name, value(area)])
);
// areas.pageSections[1].areas.image.path === '/files/images/api-sandbox/api-sandbox-figure.png'

Why this and not that

Not one request per component: everything the page shows is in the one answer, and a page of twelve sections would otherwise be thirteen round trips. Not the default answer without properties: it carries admin links, revision ids and more than forty fields you do not render, and still not the layout areas. Not areaText(name) for every layout area: it is the shortest way to read one value, but it cannot reach inside a collection. And not the areasContent answer as the body of a write: it is the read shape, and writing it back takes the mapping in Writing areas.

Go deeper

Was this page helpful?

Your answer goes to the docs team.