I am trying to create a LWC version of [Doug Ayers](https://github.com/douglascayers) library for doing [promise-based service component for calling REST API from Lightning Aura Components](https://github.com/douglascayers/sfdx-lightning-api-component). Basically, the LWC communicates using [Penpal](https://github.com/Aaronius/penpal) via an `iframe` with a `Visualforce` page which uses `jsforce` to interact with the Salesforce REST API's. There are 3 elements to this: 1. LWC: Caller (caller.js) 2. LWC: Parent (api.js) 3. VF page: Child (LC_APIPage.page) The `caller.js` code: restRequest() { const lwcApi = this.template.querySelector('c-api'); lwcApi.restRequest({ 'url': '/services/data/v45.0/tooling/query/?q=SELECT+Id,+Name+FROM+ApexClass+ORDER+BY+Name', 'method': 'get', 'body': null }).then(function (results) { console.log(JSON.stringify(results)); }).catch(function (err) { console.error('LWC_API: error when calling restRequest', err); }); } The `caller.html` code: <template> <c-api></c-api> <lightning-button label='rest request' onclick={restRequest}></lightning-button> </template> The `catch` is getting an error: > TypeError With no details. Reviewing the `api.js` component, I think the issue is in method `makePenpalRequest` around line: p = child.restRequest(request); Because when I do `console.log(child.restRequest)` I get `undefined`, but `child` is a `Promise` as expected. The LWC `api.js` code is below: /* eslint-disable @lwc/lwc/no-async-operation */ import { LightningElement, wire, api } from 'lwc'; import { loadScript } from 'lightning/platformResourceLoader'; import PP from '@salesforce/resourceUrl/penpal' import getVisualforceDomainURL from '@salesforce/apex/LC_VisualforceDomainController.getVisualforceDomainURL'; export default class Api extends LightningElement { _penpal = {}; penpalInitialized = false; iframeSrc = ''; @wire(getVisualforceDomainURL) wiredDomainUrl({ error, data }) { if (data) { this.iframeSrc = data + '/apex/LC_APIPage'; console.log(this.iframeSrc); this.initializePenpal(); } else if (error) { console.error('LWC_API: error getting Visualforce Domain URL', error); this.iframeSrc = undefined; } } connectedCallback() { Promise.all([ loadScript(this, PP) ]).then(() => { }); } initializePenpal() { let initialized = this.penpalInitialized; var self = this; // Since the iframe source is calculated asynchronously, // we listen to the component's render events and each time // check if the iframe is ready, and if so, then we initialize // penpal to connect this component to the iframe. // Since we only want to do this once, we also set the initialized flag. if (!initialized) { const container = this.template.querySelector('div'); const iframeElmt = document.createElement('iframe'); iframeElmt.src = this.iframeSrc; container.appendChild(iframeElmt); if (iframeElmt != null) { this.penpalInitialized = true; // eslint-disable-next-line no-undef const connection = Penpal.connectToChild({ // The iframe to which a connection should be made iframe: iframeElmt }); this._penpal.connection = connection; Promise.all([connection]).then(function (child) { // Cache a reference to the child so that we can // use it in the restRequest/fetchRequest methods, // as well as be able to destroy it when this component unrenders. self._penpal.child = child[0]; }).catch(function (err) { console.error('LWC_API: Error establishing connection to iframe', err); this.penpalInitialized = false; }); } // else, iframe source is empty, keep waiting } } // Makes a Salesforce REST API request and returns a promise that resolves to the response. @api restRequest(request) { var self = this; var defaultRequest = { 'method': 'get' }; var defaultHeaders = { 'Content-Type': 'application/json' }; request = Object.assign({}, defaultRequest, request); request.headers = Object.assign({}, defaultHeaders, request.headers); return this.getPenpalChild().then(function (child) { return self.makePenpalRequest('rest', child, request); }); } @api fetchRequest(request) { return this.getPenpalChild().then(function (child) { return this.makePenpalRequest('fetch', child, request); }); } // ------------------------------------------------------------ /** * For internal use. * Returns a promise waiting for the parent-child postmate handshake to complete * then resolves with reference to the postmate child for making requests. */ getPenpalChild() { var child; var self = this; var timerId; var timeout = 10000; // ten seconds var pollFrequency = 500; // half a second var startTime = new Date().getTime(); var endTime = startTime + timeout; return new Promise(function (resolve, reject) { child = self._penpal.child; if (child) { resolve(child); } else { // all time values in milliseconds timerId = setInterval(function () { child = this._penpal.child; if (child) { // parent-child postmate handshake now complete clearInterval(timerId); resolve(child); } else { // check if we have exceeded our timeout let currentTime = new Date().getTime(); if (currentTime > endTime) { clearInterval(timerId); reject('LWC_API: Timeout trying to establish connection to iframe'); } // else, keep polling } }, pollFrequency); } }); } /** * For internal use. * Returns a promise waiting for the parent-child postmate request to complete * then resolves with response from the child iframe. */ makePenpalRequest(requestType, child, request) { var p; if (requestType === 'rest') { console.log('makePenpalRequest'); console.log(child); console.log(child.restRequest); p = child.restRequest(request); } else if (requestType === 'fetch') { p = child.fetchRequest(request); } else { p = Promise.resolve({ success: false, data: 'LWC_API: Invalid request type: ' + requestType }); } console.log('makePenpalRequest'); return p.then(function (response) { console.log(response); if (response.success) { return response.data; } throw new Error(response.data); }); } disconnectedCallback() { // unrender // this.superUnrender(); // When component unrenders then cleanup penpal // resources by destroying the connection and nulling out // the helper's cached reference to the connection and child. // This ensures that the helper.handleXyzRequest(..) methods // wait appropriately for the new parent-child handshake to complete // when this component is re-initialized and scripts are loaded. if (this._penpal && this._penpal.connection) { this._penpal.connection.destroy(); this._penpal = {}; } } } The `api.html` code: <template> <div lwc:dom="manual" class="slds-hide"></div> </template> **Question** 1. What is causing the error? 2. How do I fix it?