Search code examples
javascriptobjectrequeststringify

How can I convert a Request object into a stringifiable object in JavaScript?


Using Object methods like entries and keys produce empty arrays. JSON.stringify yields an empty object in the browser and a circular reference error in Node.js.

const request = new Request(
  'https://example.com/send',
  {
    method: 'POST',
    body: 'Hello world',
    headers: {'x-planet-origin': 'Mars'},
  },
);

const keys = Object.keys(request);
const values = Object.values(request);
const entries = Object.entries(request);
const string = JSON.stringify(request);

console.log({keys, values, entries, string, request});


Solution

  • You can use these functions to convert the stringifiable values in a Request object to values in a plain object. The first function alone will produce the described object, and the others will assist in producing a version with sorted properties.

    function requestAsObject (request) {
      if (!request instanceof Request)
        throw Object.assign(
          new Error(),
          {name: 'TypeError', message: 'Argument must be a Request object'}
        );
      request = request.clone();
    
      function stringifiableObject (obj) {
        const filtered = {};
        for (const key in obj)
          if (['boolean', 'number', 'string'].includes(typeof obj[key]) || obj[key] === null)
            filtered[key] = obj[key];
        return filtered;
      }
    
      return {
        ...stringifiableObject(request),
        headers: Object.fromEntries(request.headers),
        signal: stringifiableObject(request.signal),
        // bodyText: await request.text(), // requires function to be async
      };
    }
    
    function requestAsArrayEntries (request) {
      if (!request instanceof Request)
        throw Object.assign(
          new Error(),
          {name: 'TypeError', message: 'Argument must be a Request object'}
        );
      request = request.clone();
    
      function entriesFromObject (obj) {
        const entries = [];
        for (const key in obj)
          if (['boolean', 'number', 'string'].includes(typeof obj[key]) || obj[key] === null)
            entries.push([key, obj[key]]);
        return entries.sort();
      }
    
      return [
        ...entriesFromObject(request),
        ['headers', [...request.headers].sort()],
        ['signal', entriesFromObject(request.signal)],
        // ['bodyText', await request.text()], // requires function to be async
      ].sort();
    }
    
    function objectFromNestedEntries (arrayOfEntries) {
      if (!Array.isArray(arrayOfEntries)) return arrayOfEntries;
      const obj = {};
      for (const [key, value] of arrayOfEntries) {
        obj[key] = objectFromNestedEntries(value);
      }
      return obj;
    }
    
    const request = new Request('https://example.com/send', {
      method: 'POST',
      body: 'Hello world',
      headers: {'x-planet-origin': 'Mars'},
    });
    
    const object = requestAsObject(request);
    const arrayEntries = requestAsArrayEntries(request);
    const sortedObject = objectFromNestedEntries(arrayEntries);
    
    console.log({
      object,
      arrayEntries,
      sortedObject,
      objectAsString: JSON.stringify(object),
      sortedObjectAsString: JSON.stringify(sortedObject),
    });

    Thanks, Emeeus, for helping me think in the right direction.