How to create a Google Chrome UX API batch query?

667 Views Asked by At

I've tried examples from the documentation page, but got errors (like invalid url, which was in fact valid) giving me no insight, what i'm doing wrong.

Could somebody give me an example for a batch query in the cURL flavour?

Tried: I was tried it on the following way:

  • Copied an example from documentation page into an online cURL tool,
  • added curl --request befor beginning POST,
  • added my API key,
  • added domains to places, where they imo should be.

But, as result i've got not such response, as stated in the documentation, but status 404 and the source code of the first website i queried - my request and response header you can see at the sreenshot:

enter image description here

2

There are 2 best solutions below

0
Rick Viscomi On BEST ANSWER

Here's an example of using batch requests with cURL:

curl -d '

--Batch

POST /v1/records:queryRecord?key=YOUR_API_KEY HTTP/1.1
Content-ID: item1
Content-Type: application/json

{"origin":"https://example.com", "metrics": ["first_contentful_paint"]}

--Batch

POST /v1/records:queryRecord?key=YOUR_API_KEY HTTP/1.1
Content-ID: item2
Content-Type: application/json

{"url":"https://example.com/", "metrics": ["first_contentful_paint"]}

--Batch--
' -H 'Content-Type: multipart/mixed; boundary="Batch"' -X POST -s https://chromeuxreport.googleapis.com/batch/ -v

The response looks like this:

--batch_R4dQQd20Vo_-lf91w1QTFvMinqZc7Zyj
Content-Type: application/http
Content-ID: response-item1

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Vary: Origin
Vary: X-Origin
Vary: Referer

{
  "record": {
    "key": {
      "origin": "https://example.com"
    },
    "metrics": {
      "first_contentful_paint": {
        "histogram": [
          {
            "start": 0,
            "end": 1000,
            "density": 0.53746873436718867
          },
          {
            "start": 1000,
            "end": 3000,
            "density": 0.39049524762381554
          },
          {
            "start": 3000,
            "density": 0.072036018009005318
          }
        ],
        "percentiles": {
          "p75": 1748
        }
      }
    }
  }
}

--batch_R4dQQd20Vo_-lf91w1QTFvMinqZc7Zyj
Content-Type: application/http
Content-ID: response-item2

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Vary: Origin
Vary: X-Origin
Vary: Referer

{
  "record": {
    "key": {
      "url": "https://example.com/"
    },
    "metrics": {
      "first_contentful_paint": {
        "histogram": [
          {
            "start": 0,
            "end": 1000,
            "density": 0.5311186712027276
          },
          {
            "start": 1000,
            "end": 3000,
            "density": 0.39493696217731078
          },
          {
            "start": 3000,
            "density": 0.073944366619972793
          }
        ],
        "percentiles": {
          "p75": 1768
        }
      }
    }
  }
}

--batch_R4dQQd20Vo_-lf91w1QTFvMinqZc7Zyj--

Edit: I've added a method to CrUXApiUtil to handle all of the batch logistics for you in JS applications.

CrUXApiUtil.batch = function (requests) {
  if (CrUXApiUtil.API_KEY == '[YOUR_API_KEY]') {
    throw 'Replace "YOUR_API_KEY" with your private CrUX API key. Get a key at https://goo.gle/crux-api-key.';
  }

  const BOUNDARY = 'BATCH_BOUNDARY';
  return fetch(CrUXApiUtil.API_BATCH_ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': `multipart/mixed; boundary="${BOUNDARY}"`
    },
    body: `--${BOUNDARY}\n` + requests.map((requestBody, i) => {
      return `
POST ${CrUXApiUtil.API_ENDPOINT_PATH} HTTP/1.1
Content-ID: crux-${i}
Content-Type: application/json

${JSON.stringify(requestBody)}`;
    }).join(`\n\n--${BOUNDARY}\n`) + `\n\n--${BOUNDARY}--`
  }).then(response => {
    const contentTypeHeader = response.headers.get('Content-Type');
    const boundaryPattern = /\bboundary=([\w-]+)\b/i;
    if (!boundaryPattern.test(contentTypeHeader)) {
      throw `Unable to parse boundary directive from Content-Type response header: "${contentTypeHeader}"`;
    }

    const boundary = contentTypeHeader.match(boundaryPattern)[1];
    return Promise.all([
      Promise.resolve(boundary),
      response.text()
    ]);
  }).then(([boundary, response]) => {
    const responseParts = response.split(`--${boundary}`);
    // Eject boundary bookends
    responseParts.shift();
    responseParts.pop();

    const responseJSONPattern = /\n({[\s\S]*)/m;
    return responseParts.map(part => {
      if (!responseJSONPattern.test(part)) {
        console.error(`Unable to parse CrUX API response from:\n${response}`);
        return null;
      }
      return JSON.parse(part.match(responseJSONPattern)[1]);
    });
  });
}

Example input:

CrUXApiUtil.batch([
  {"origin":"https://example.com", "metrics": ["first_contentful_paint"]},
  {"url":"https://example.com/", "metrics": ["first_contentful_paint"]}
]).then(console.log)

Example output:

[
  {
    "record": {
      "key": {
        "origin": "https://example.com"
      },
      "metrics": {
        "first_contentful_paint": {
          "histogram": [
            {
              "start": 0,
              "end": 1000,
              "density": 0.5374687343671887
            },
            {
              "start": 1000,
              "end": 3000,
              "density": 0.39049524762381554
            },
            {
              "start": 3000,
              "density": 0.07203601800900532
            }
          ],
          "percentiles": {
            "p75": 1748
          }
        }
      }
    }
  },
  {
    "record": {
      "key": {
        "url": "https://example.com/"
      },
      "metrics": {
        "first_contentful_paint": {
          "histogram": [
            {
              "start": 0,
              "end": 1000,
              "density": 0.5311186712027276
            },
            {
              "start": 1000,
              "end": 3000,
              "density": 0.3949369621773108
            },
            {
              "start": 3000,
              "density": 0.07394436661997279
            }
          ],
          "percentiles": {
            "p75": 1768
          }
        }
      }
    }
  }
]
1
Aleksey Kulikov On

crux-api helps to generate a batch request with an automatic support for retries (CrUX API quota is counted per record) and empty records. It's a tiny (500 bytes) isomorphic library with TS support.

import { createBatch } from 'crux-api/batch'
const batch = createBatch({ key: CRUX_API_KEY })

const records = await batch([
  { url: 'https://github.com/', formFactor: 'MOBILE', effectiveConnectionType: '4G' },
  { url: 'https://github.com/marketplace', formFactor: 'DESKTOP' },
  { url: 'https://www.github.com/explore', formFactor: 'TABLET' },
  // ... up to 1000 records.
])