How to Replicate a Request in Chrome DevTools

If you want to alter a request of an API in Chrome to modify it, you can start with an existing request.

4 min read
How to Replicate a Request in Chrome DevTools
Photo by Jozef Fehér from Pexels.

Context

This weekend, I tried to delete an account on a website, but I couldn't. The website has an obscure bug that prevents the submission of the deletion form... 😕

Since I didn't want to contact their support (they really aren't great) and I really want to delete my account, I started looking at their source code to see if I could find a way around it. 😏

After many unsuccessful attempts (probably too many), I finally managed to delete my account after writing a Fetch request. 🤗

In this post, I want to share a little trick that you might want to use if you're playing with some APIs: the Copy as function.

Explanation

If you open Chrome DevTools (F12), navigate to the Network tab and click on the Fetch/XHR filter, you'll see all requests that use the Fetch API (you may need to refresh the page).

Screenshot of the Network tab inside Chrome DevTools.

But, did you know that by right-clicking on one of the requests, you can copy it to reproduce it? 😲

Screenshot of the Copy of options in DevTools.

Chromium browsers offer us no less than five ways to a request:

  1. Copy as PowerShell
  2. Copy as fetch
  3. Copy as Node fetch
  4. Copy as cURL (cmd)
  5. Copy as cURL (bash)

Let's take a look at the code they generated.

🤭
I won't show the PowerShell output, as I'm not a fan of this language. Sorry! 🙈

fetch

await fetch("https://www.benjaminrancourt.ca/ghost/api/canary/admin/site/", {
  "headers": {
    "accept": "application/json, text/javascript, */*; q=0.01",
    "accept-language": "en,fr-CA;q=0.9,fr;q=0.8,la;q=0.7",
    "app-pragma": "no-cache",
    "cache-control": "no-cache",
    "content-type": "application/json; charset=UTF-8",
    "pragma": "no-cache",
    "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "x-ghost-version": "4.36",
    "x-requested-with": "XMLHttpRequest"
  },
  "referrer": "https://www.benjaminrancourt.ca/ghost/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": null,
  "method": "GET",
  "mode": "cors",
  "credentials": "include"
});
The fetch command that Chrome has generated.

If you take this command and paste it inside the Chrome console, you should get a response, because this Ghost API endpoint is public.

But depending on the API you want to contact, not all these headers and parameters might not be required. In this case, I can remove most of them. The final command looks like the one below.

await fetch("https://www.benjaminrancourt.ca/ghost/api/canary/admin/site/");
The same request, with only the strictly required fields.

Simpler, right? 😏

Node Fetch

await fetch("https://www.benjaminrancourt.ca/ghost/api/canary/admin/site/", {
  "headers": {
    "accept": "application/json, text/javascript, */*; q=0.01",
    "accept-language": "en,fr-CA;q=0.9,fr;q=0.8,la;q=0.7",
    "app-pragma": "no-cache",
    "cache-control": "no-cache",
    "content-type": "application/json; charset=UTF-8",
    "pragma": "no-cache",
    "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "x-ghost-version": "4.36",
    "x-requested-with": "XMLHttpRequest",
    "cookie": "ghost-admin-api-session=MASKED",
    "Referer": "https://www.benjaminrancourt.ca/ghost/",
    "Referrer-Policy": "strict-origin-when-cross-origin"
  },
  "body": null,
  "method": "GET"
});
The Node fetch command that Chrome has generated.

If you carefully compare between the fetch and Node fetch, you might notice that there is not much difference. But one of them is the presence of the cookie header in Node fetch.  If this header is missing from your Node.js script, chances are you can't contact the API.

cURL (cmd)

If you're on Windows and using the default terminal, you'll probably want to use this cCURL command.

curl "https://www.benjaminrancourt.ca/ghost/api/canary/admin/site/" ^
  -H "authority: www.benjaminrancourt.ca" ^
  -H "pragma: no-cache" ^
  -H "cache-control: no-cache" ^
  -H "sec-ch-ua: ^\^" Not A;Brand^\^";v=^\^"99^\^", ^\^"Chromium^\^";v=^\^"98^\^", ^\^"Google Chrome^\^";v=^\^"98^\^"" ^
  -H "dnt: 1" ^
  -H "sec-ch-ua-mobile: ?0" ^
  -H "app-pragma: no-cache" ^
  -H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36" ^
  -H "content-type: application/json; charset=UTF-8" ^
  -H "accept: application/json, text/javascript, */*; q=0.01" ^
  -H "x-requested-with: XMLHttpRequest" ^
  -H "x-ghost-version: 4.36" ^
  -H "sec-ch-ua-platform: ^\^"Windows^\^"" ^
  -H "sec-fetch-site: same-origin" ^
  -H "sec-fetch-mode: cors" ^
  -H "sec-fetch-dest: empty" ^
  -H "referer: https://www.benjaminrancourt.ca/ghost/" ^
  -H "accept-language: en,fr-CA;q=0.9,fr;q=0.8,la;q=0.7" ^
  -H "cookie: ghost-admin-api-session=MASKED" ^
  --compressed
The cURL (cmd) command that Chrome has generated.

cURL (bash)

For Linux users, the separator used to continue the command is different in Bash:

curl 'https://www.benjaminrancourt.ca/ghost/api/canary/admin/site/' \
  -H 'authority: www.benjaminrancourt.ca' \
  -H 'pragma: no-cache' \
  -H 'cache-control: no-cache' \
  -H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"' \
  -H 'dnt: 1' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'app-pragma: no-cache' \
  -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36' \
  -H 'content-type: application/json; charset=UTF-8' \
  -H 'accept: application/json, text/javascript, */*; q=0.01' \
  -H 'x-requested-with: XMLHttpRequest' \
  -H 'x-ghost-version: 4.36' \
  -H 'sec-ch-ua-platform: "Windows"' \
  -H 'sec-fetch-site: same-origin' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-dest: empty' \
  -H 'referer: https://www.benjaminrancourt.ca/ghost/' \
  -H 'accept-language: en,fr-CA;q=0.9,fr;q=0.8,la;q=0.7' \
  -H 'cookie: ghost-admin-api-session=MASKED' \
  --compressed
The cURL (bash) command that Chrome has generated.

You now have four ways to play with the APIs you have access to! 😆