Search code examples
google-apichrome-ux-report

How to create a Google Chrome UX API batch query?


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


Solution

  • 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
              }
            }
          }
        }
      }
    ]