Wait for fetch to resolve

Question:
Hi community, I fetch data via fetch() and a Google API and display it in a repeater. Everything works fine. However, the Google bot only sees an empty repeater.

I am probably using async or .then incorrectly, so that the rendering is not yet complete when it is delivered as html to the Google bot.

The fetchdata function runs in the backend code and returns the result of the API request. Who can perhaps help me?

Product:
Wix Editor

What are you trying to achieve:
I want the content of a repeater to be “seen” by Google to improve SEO.

What have you already tried:
I have written the following code:

$w.onReady(async function () {
	(...)
	await InfoBox()
	(...)
})

function InfoBox() {
	$w('#dynamicDataset').onReady(() => {
		var itemObj = $w('#dynamicDataset').getCurrentItem()
		if (itemObj.googleId != undefined) {
			fetchData(itemObj.googleId).then(function(response) {
				if (response.formattedAddress != undefined) {
					(...)
					const neuerDatenpunkt = {
						"_id": MyID,
						"Beschreibung": "Address:",
						"Inhalt": response.formattedAddress
					};
					MyData.push(neuerDatenpunkt);
				}
			})
		}
	)}
}

How do you know whether or not google is “seeing” the content?

Is it a repeater that you are using?

It seems you are only obtaining 1 item, why do you need a repeater? Wouldn’t regular text elements suffice?

Also, what does your repeater code look like to see how you are setting the values?

Hi @Smith_Breaux

Wix uses server-side rendering, the components of your page are rendered and returned to the browser as HTML. JavaScript code is not necessarily executed by a search bot. This means that if you use JS, you cannot improve SEO without controlling what Google sees of your site.

In my example, my entire repeater is empty for Google and therefore cannot be taken into account in search queries / rankings. You can see this e.g. in the Google Search Console or you can set a user agent on the web browser to Googlebot.

Regards!

Hi @codequeen,

I have shortened the code, 10-20 entries are displayed in the repeater, partly via the API, partly from the CMS.

I first write the entries one after the other in the array “MyData” and pass this to the repeater at the end. The code looks like this:

$w("#repeater").data = MyData;	
$w("#repeater").forEachItem(($item, itemData, index) => {     
    $item("#description").text = itemData.description;
	$item("#content").text = itemData.content;
})

Everything works as it should, but I’m not sure how to handle async/await so that the code is executed completely before everything goes to the Googlebot - the bot only sees an empty repeater.

I don’t have much experience with programming either, so I’m just muddling through :wink:

Spektral

Dein gesamter Ansatz scheint falsch zu sein!

Du musst dir im klaren darüber sein, dass du zweigleisig fährst, was in den meisten Fällen schief läuft, wie du siehst auch in deinem Fall. Du benutzt einerseits das DATASET und versuchst dabei auch noch per code zu injezieren, was natürlich so nicht functionieren wird, zumindest nicht wenn man es falsch macht.

Anstatt den Mist hier…
$w.onReady(async function () {
(…)
await InfoBox()
(…)
})

…benutze…

$w.onReady(async()=> {
    $w('#dynamicDataset').onReady(() => {
        ...code...
    });

      
    $w("#repeater").onItemReady(($item, itemData, index) => {     
        ...code...
    });
});

In dem dataset-onReady-Part, dort bereitest du zunächst das Datenpacket vor für den REPEATER welches du gerne haben möchtest. Das heißt…

  1. …du definierst → var itemObj = $w(‘#dynamicDataset’).getCurrentItem();
    …oder wie ich es nennen würde → curItem ← was für CurrentItem steht, das ist nämlich auch genau das was in diesem Objekt nachher drin sein wird, die Daten deiner DYNAMISCHEN-SEITE → sprich → ein SINGLE-ITEM aus deiner Datenbak welches zur Zeit selectiert wird → das currentItem → abgekürzt → curItem.

Versuche deine Variablendefinitionen stets ordentlich und sauber zu halten, dann verstehst du deinen eigenen code erstens viel besser und liest diesen auch schneller.

So nun hast du dein → curItem <— bezogen, nachdem dein dyn-datasetREADY ist.

$w.onReady(async()=> {
    $w('#dynamicDataset').onReady(() => {
       let curItem = $w('#dynamicDataset').getCurrentItem(); console.log('Current-Item: ', curItem);
    });
});
  1. Jetzt brauchst du noch deine “gefetchten” Daten (fetch) die du ja hiermit bekommst…
function getGoogleData(itemObj, MyID) {
    fetchData(itemObj.googleId).then(function(response) {
        if (response.formattedAddress != undefined) {
            (...)
            const neuerDatenpunkt = {
                "_id": MyID,
                "Beschreibung": "Address:",
                "Inhalt": response.formattedAddress
            };
            //MyData.push(neuerDatenpunkt);
            return neuerDatenpunkt;
        }
    });
}

…oh ja habe das etwas umstrukturiert. :joy:

Jetzt ist es eine RETURN-FUNKTION, ich denke das muss ich dir nicht erklären.

  1. Sooooo, setzen wir das PUZZLE mal weiter zusammen und zwar in einem Zeitraffer (mehrere Einzelschritte werden hierbei übersprungen)…
import { fetchData } from 'backend/..........'

$w.onReady(()=> {
    let myID = '.............';
    //----------------------------------------------------------------------------------------------------------------
    $w('#dynamicDataset').onReady(async() => {
        let curItem = $w('#dynamicDataset').getCurrentItem();
        let neuerDatenpunkt = await getGoogleData(curItem, myID);
        //-------------------------------------------------------------------------------------------------------------

        //...continue here...with --> OBJECT-PREPARATION for INJECTION...
        let preparedRepData = await prepareRepData(curItem, neuerDatenpunkt); console.log('Prepared-Rep-Data: ', preparedRepData);
        //...feeding your repeater with the prepared data...
        $w("#repeater").data = preparedRepData;
    });

    $w("#repeater").onItemReady(($i, iData, i) => {     
        $i("#description").text = iData.description;
        $i("#content").text = iData.content;
    })
});



function getGoogleData(curItem, MyID) {
    fetchData(curItem.googleId).then(function(response) {
        if (response.formattedAddress != undefined) {
            //(...)
            const neuerDatenpunkt = {
                "_id": MyID,
                "Beschreibung": "Address:",
                "Inhalt": response.formattedAddress
            };
            return neuerDatenpunkt;
        }
    });
}


function prepareRepData(curItem, neuerDatenpunkt) {
    console.log('Current-Item: ', curItem);
    console.log('Neuer-Datenpunkt: ', neuerDatenpunkt);
    // prepare your repeater-data here like you want...
    let repData = {};
    repData._id = neuerDatenpunkt['property'];
    repData.datenpunkt = neuerDatenpunkt['property'];
    repData.title = curItem.title;
    repData.xyz = curItem['property'];
    repData.xyz = curItem['property'];
    repData.xyz = curItem['property']['subProperty'];
    repData.xyz = curItem['property']['subProperty'];
    repData.xyz = curItem['property']['subProperty'];
    //-------------------------
    return repData;
}

Und so weiter.

Jetzt hast du die → ZWEIGLEISIGKEIT wieder rausgenommen und fährst pur mit deinem CODE weiter —> gute Fahrt !!!

Grüße nach Deutschland!
2024-03-10 14_51_16-Wait for fetch to resolve - Discussion _ Ask a question - Wix Studio Community f

Und benutze immer die → KONSOLE ← wenn du nicht mehr weiter kommst.

English @russian-dima !!! I can’t understand anything you just said!!! :roll_eyes::rofl::woman_facepalming:t4:

1 Like

:rofl:

Well…in english-version in shorten form…

1) Do not take a two-pronged approach → this is what he is doing → wanting to use DATASET and own generated date in a mix, causing a → Two-track approach.

2) Define your VARIABLES always the right way…
…instead of →

let itemObj = $w('#dynamicDataset').getCurrentItem();
...using the right definition title for variable--->
let curData = $w('#dynamicDataset').getCurrentItem();

This will make your code more intuitive and better (faster) readable.

3) Starting code-sequence should be…

$w.onReady(async()=> {
    $w('#dynamicDataset').onReady(() => {
        ...code...
    });

      
    $w("#repeater").onItemReady(($item, itemData, index) => {     
        ...code...
    });
});

4) Optimized fetch-function into return-function → will be needed later for better code-structure…

function getGoogleData(itemObj, MyID) {
    fetchData(itemObj.googleId).then(function(response) {
        if (response.formattedAddress != undefined) {
            (...)
            const neuerDatenpunkt = {
                "_id": MyID,
                "Beschreibung": "Address:",
                "Inhalt": response.formattedAddress
            };
            //MyData.push(neuerDatenpunkt);
            return neuerDatenpunkt;
        }
    });
}

5) Puttin all together in fast time-lapsing-mode …

import { fetchData } from 'backend/..........'

$w.onReady(()=> {
    let myID = '.............';
    //----------------------------------------------------------------------------------------------------------------
    $w('#dynamicDataset').onReady(async() => {
        let curItem = $w('#dynamicDataset').getCurrentItem();
        let neuerDatenpunkt = await getGoogleData(curItem, myID);
        //-------------------------------------------------------------------------------------------------------------

        //...continue here...with --> OBJECT-PREPARATION for INJECTION...
        let preparedRepData = await prepareRepData(curItem, neuerDatenpunkt); console.log('Prepared-Rep-Data: ', preparedRepData);
        //...feeding your repeater with the prepared data...
        $w("#repeater").data = preparedRepData;
    });

    $w("#repeater").onItemReady(($i, iData, i) => {     
        $i("#description").text = iData.description;
        $i("#content").text = iData.content;
    })
});



function getGoogleData(curItem, MyID) {
    fetchData(curItem.googleId).then(function(response) {
        if (response.formattedAddress != undefined) {
            //(...)
            const neuerDatenpunkt = {
                "_id": MyID,
                "Beschreibung": "Address:",
                "Inhalt": response.formattedAddress
            };
            return neuerDatenpunkt;
        }
    });
}


function prepareRepData(curItem, neuerDatenpunkt) {
    console.log('Current-Item: ', curItem);
    console.log('Neuer-Datenpunkt: ', neuerDatenpunkt);
    // prepare your repeater-data here like you want...
    let repData = {};
    repData._id = neuerDatenpunkt['property'];
    repData.datenpunkt = neuerDatenpunkt['property'];
    repData.title = curItem.title;
    repData.xyz = curItem['property'];
    repData.xyz = curItem['property'];
    repData.xyz = curItem['property']['subProperty'];
    repData.xyz = curItem['property']['subProperty'];
    repData.xyz = curItem['property']['subProperty'];
    //-------------------------
    return repData;
}

What has been done?

  1. given the code a better readability and structure.
  2. removing the → Two-track approach <---- (mixed-mode between DATASET and CODE) → what in most cases causes problem when someone tries to combine.

Of course there are other options how to solve it → THIS IS JUST ONE OF THOSE.

Switching from double-track driving to single-track driving → this is how i am calling it.
3) Giving the possibility to create and prepare your own DATA-OBJECT for REPEATER-INJECTION.

Now he has clear defined single functions running in the right order (regarding ASYNC-AWAIT).

  1. Oh, and last thing → if you get stuck in your code → use the CONSOLE!!!

Yeah that in short summary. :joy:

YOU NEED IT IN RUSSIAN ASWELL ? :rofl:

2 Likes

@russian-dima and @codequeen,

codequeen may still have an advantage. I understand German (I’m German), but not all the code :grin:

Firstly, thank you very much for your support!

I’ve been trying to implement this for a while, but I can’t get any further because it’s beyond my understanding of coding.

Here is a more detailed part of my (old) code first (even though it’s probably very “beginner-like”, it works, except that the Googlebot doesn’t see the repeater content):

FRONTEND-CODE:

import {fetchData} from 'backend/FetchHandler.jsw' //für Google-Daten

$w.onReady(function () {

	$w('#dynamicDataset').onReady(() => {
		var itemObj = $w('#dynamicDataset').getCurrentItem()

		if (itemObj.googleId != undefined) {

			fetchData(itemObj.googleId).then(function(response) {
				let Ergebnis_Open;
				let MyID = "0";
				let MyData = [];

				// Adresse von Google
				if (response.formattedAddress != undefined) {
					let num = parseInt(MyID);
					num = num +1;
					MyID = JSON.stringify(num);
					const neuerDatenpunkt = {
						"_id": MyID,
						"Beschreibung": "Address:",
						"Inhalt": response.formattedAddress
					};
					MyData.push(neuerDatenpunkt);
				} else {
					if (itemObj.address != undefined) {
						let num = parseInt(MyID);
						num = num + 1;
						MyID = JSON.stringify(num);
						const neuerDatenpunkt = {
							"_id": MyID,
							"Beschreibung": "Address:",
							"Inhalt":  itemObj.address.formatted
						};
						MyData.push(neuerDatenpunkt);
					}
				}

				// Konfession (immer katholisch bei Kirchen)
				if (itemObj.filterParksChurches != undefined) {
					if (itemObj.filterParksChurches[0] === "Churches") {
						let num = parseInt(MyID);
						num = num + 1;
						MyID = JSON.stringify(num);
						const neuerDatenpunkt = {
							"_id": MyID,
							"Beschreibung": "Denomination:",
							"Inhalt": "Catholic Church"
						};
						MyData.push(neuerDatenpunkt);
					}
				}

				// -------------  etc.

				$w("#repeater").data = MyData;	
				// Daten in den Repeater schreiben
				$w("#repeater").forEachItem(($item, itemData, index) => {     
					$item("#Beschreibung").text = itemData.Beschreibung;
					$item("#Inhalt").text = itemData.Inhalt;
				})
			})
		} else {
			let MyID = "0";
			let MyData = [];

			// Adresse vom CMS
			if (itemObj.address != undefined) {
				let num = parseInt(MyID);
				num = num + 1;
				MyID = JSON.stringify(num);
				const neuerDatenpunkt = {
					"_id": MyID,
					"Beschreibung": "Address:",
					"Inhalt":  itemObj.address.formatted
				};
				MyData.push(neuerDatenpunkt);
			}

			// Opening Hours vom CMS
			if (itemObj.openingHours != undefined) {
				let num = parseInt(MyID);
				num = num + 1;
				MyID = JSON.stringify(num);
				const neuerDatenpunkt = {
					"_id": MyID,
					"Beschreibung": "Opening Hours:",
					"Inhalt": itemObj.openingHours
				};
				MyData.push(neuerDatenpunkt);
			}

			// -------------  etc.
			
			$w("#repeater").data = MyData;	
			// Daten in den Repeater schreiben
			$w("#repeater").forEachItem(($item, itemData, index) => {     
				$item("#Beschreibung").text = itemData.Beschreibung;
				$item("#Inhalt").text = itemData.Inhalt;
			})
		}
	});
});

BACKEND-CODE:

import { fetch } from 'wix-fetch';

export function fetchData(GoogleID) {
	const key = "...............";
    const url = "https://places.googleapis.com/v1/places/" + GoogleID + "?fields=regularOpeningHours,rating,userRatingCount,formattedAddress,internationalPhoneNumber&key=" + key
    return fetch (url, {method: 'get'}).then( (httpResponse) => {
	    if (httpResponse.ok) {
	    	return httpResponse.json();
		}
	})
}

I’ve spent 1-2 hours trying to apply your example code to it, but can’t get it to work (mainly because I don’t understand everything).

“response.formattedAddress” is only one possible response from the Google API. You have hard coded that in your function “getGoogleData”. Sometimes I only access the CMS, not the API. Wouldn’t it be better not to “outsource” this in a function? “prepareRepData” only creates a data set once based on an entry, right? I want to write multiple entries (from the API and/or the CMS) to the repeater (see my code above).

Oder in Kurzform: Ich fürchte, ich blicke leider nicht durch :innocent:

Regards, Spektral

Well regarding your code → i get frightened. You really want to hard code, everything?

You should use more LOOPS and do it more dynamicaly → generating enclosed selfworking and calculating functions → instead of generating TONS OF CODE FOR EVERY SINGLE CODE-STEP.

For example…

let MyID = "0";
let MyData = [];

if (googleID) {
    fetchData(itemObj.googleId).then(function(response) {
        if (response.formattedAddress != undefined) {
            processAddress(response.formattedAddress);
        } else if (itemObj.address != undefined) {
            processAddress(itemObj.address.formatted);
        }

        if (itemObj.filterParksChurches != undefined && itemObj.filterParksChurches[0] === "Churches") {
            processDenomination("Catholic Church");
        }

        // Add more conditions as needed

        // Write data to repeater
        writeDataToRepeater();
    });
} else {
    if (itemObj.address != undefined) {
        processAddress(itemObj.address.formatted);
    }

    if (itemObj.openingHours != undefined) {
        processOpeningHours(itemObj.openingHours);
    }

    // Add more conditions as needed

    // Write data to repeater
    writeDataToRepeater();
}

function processAddress(address) {
    let num = parseInt(MyID);
    num = num + 1;
    MyID = JSON.stringify(num);
    const neuerDatenpunkt = {
        "_id": MyID,
        "Beschreibung": "Address:",
        "Inhalt": address
    };
    MyData.push(neuerDatenpunkt);
}

function processDenomination(denomination) {
    let num = parseInt(MyID);
    num = num + 1;
    MyID = JSON.stringify(num);
    const neuerDatenpunkt = {
        "_id": MyID,
        "Beschreibung": "Denomination:",
        "Inhalt": denomination
    };
    MyData.push(neuerDatenpunkt);
}

function processOpeningHours(openingHours) {
    let num = parseInt(MyID);
    num = num + 1;
    MyID = JSON.stringify(num);
    const neuerDatenpunkt = {
        "_id": MyID,
        "Beschreibung": "Opening Hours:",
        "Inhalt": openingHours
    };
    MyData.push(neuerDatenpunkt);
}

// Add more processing functions as needed

function writeDataToRepeater() {
    // Write data to repeater
    $w("#repeater").data = MyData;
    // Daten in den Repeater schreiben
    $w("#repeater").forEachItem(($item, itemData, index) => {
        $item("#Beschreibung").text = itemData.Beschreibung;
        $item("#Inhalt").text = itemData.Inhalt;
    });
}

But well, i think maybe to much into detail.

Let us do it STEP-BY-STEP and improve your code. Your aimes will be…
1) making your code better readable and structured.
2) making your code more dynamic instead of hard-coded.

So try to implement (change) this first…

import {fetchData} from 'backend/FetchHandler.jsw';

$w.onReady(()=> {
    //Dataset-onReady-part...
	$w('#dynamicDataset').onReady(async() => {
        //Step-I: getting the currentItemData (in your case --> itemObj)...
		let itemObj = $w('#dynamicDataset').getCurrentItem(); console.log('Current-Item: ', itemObj);
        //Step-II: --> defining google-id...
        let googleID = itemObj.googleId; console.log('Google-ID: ', googleID);
        //Step-III: ---> running a function to get the right --> DATENPUNKT --> returning-function...
        let myData = await myFunction();
        feedRepeater(myData);
	});

    //Repeater-onReady-part...
    $w("#repeater").onItemReady(($item, itemData, index) => {     
        $item("#Beschreibung").text = itemData.Beschreibung;
        $item("#Inhalt").text = itemData.Inhalt;
    });
});




async function myFunction(googleID, itemObj) {
    if (googleID) {
        fetchData(itemObj.googleId).then(function(response) {
            let Ergebnis_Open;
            let MyID = "0";
            let MyData = [];
            // Adresse von Google
            if (response.formattedAddress != undefined) {
                let num = parseInt(MyID);
                num = num +1;
                MyID = JSON.stringify(num);
                const neuerDatenpunkt = {
                    "_id": MyID,
                    "Beschreibung": "Address:",
                    "Inhalt": response.formattedAddress
                };
                MyData.push(neuerDatenpunkt);
                return MyData;
            } 
            else {
                if (itemObj.address != undefined) {
                    let num = parseInt(MyID);
                    num = num + 1;
                    MyID = JSON.stringify(num);
                    const neuerDatenpunkt = {
                        "_id": MyID,
                        "Beschreibung": "Address:",
                        "Inhalt":  itemObj.address.formatted
                    };
                    MyData.push(neuerDatenpunkt);
                    return MyData;
                }
            }
            // Konfession (immer katholisch bei Kirchen)
            if (itemObj.filterParksChurches != undefined) {
                if (itemObj.filterParksChurches[0] === "Churches") {
                    let num = parseInt(MyID);
                    num = num + 1;
                    MyID = JSON.stringify(num);
                    const neuerDatenpunkt = {
                        "_id": MyID,
                        "Beschreibung": "Denomination:",
                        "Inhalt": "Catholic Church"
                    };
                    MyData.push(neuerDatenpunkt);
                    return MyData;
                }
            }
            // -------------  etc.
        });
    } 
    else {
        let MyID = "0";
        let MyData = [];
        // Adresse vom CMS
        if (itemObj.address != undefined) {
            let num = parseInt(MyID);
            num = num + 1;
            MyID = JSON.stringify(num);
            const neuerDatenpunkt = {
                "_id": MyID,
                "Beschreibung": "Address:",
                "Inhalt":  itemObj.address.formatted
            };
            MyData.push(neuerDatenpunkt);
            return MyData;
        }
        // Opening Hours vom CMS
        if (itemObj.openingHours != undefined) {
            let num = parseInt(MyID);
            num = num + 1;
            MyID = JSON.stringify(num);
            const neuerDatenpunkt = {
                "_id": MyID,
                "Beschreibung": "Opening Hours:",
                "Inhalt": itemObj.openingHours
            };
            MyData.push(neuerDatenpunkt);
            return MyData;
        }
        // -------------  etc.     
    }
}


function feedRepeater(data) {$w("#repeater").data = data;}

When you have done the optimizations and it works, you can do next optimization steps.

I am pretty sure something is missing in your provided code… :thinking:

  1. You generate an ARRAY called MyData
    let MyData = [];

  2. You fill the ARRAY with —> DATENPUNKTEN —>
    MyData = ['datenpunkt1, datenpunk2, datenpunkt3, ........];

Right ???

  1. And then once you have collected all you push → MyData ← to your REPEATER ? :thinking: → directly ??? —>
$w("#repeater").data = MyData;	
// Daten in den Repeater schreiben
$w("#repeater").forEachItem(($item, itemData, index) => {     
    $item("#Beschreibung").text = itemData.Beschreibung;
    $item("#Inhalt").text = itemData.Inhalt;
})

Out of my view → this can’t work → (you even do not bind an absolutely neccessary ID to the data).

Please show a pic what you get inside of your repeater.
Does your repeater work ? How ?

This is why i had generated the → DATA-PREPARATION-FUNCTION → to prepare the REPEATER-DATA first.

function prepareRepData(curItem, neuerDatenpunkt) {
    console.log('Current-Item: ', curItem);
    console.log('Neuer-Datenpunkt: ', neuerDatenpunkt);
    // prepare your repeater-data here like you want...
    let repData = {};
    repData._id = neuerDatenpunkt['property'];
    repData.datenpunkt = neuerDatenpunkt['property'];
    repData.title = curItem.title;
    repData.xyz = curItem['property'];
    repData.xyz = curItem['property'];
    repData.xyz = curItem['property']['subProperty'];
    repData.xyz = curItem['property']['subProperty'];
    repData.xyz = curItem['property']['subProperty'];
    //-------------------------
    return repData;
}

Your REPEATER-DATA has to be like the following pattern…

[
  {
    "_id": "1",
    "firstName": "John",
    "lastName": "Doe",
    "image": "http://someImageUrl/john.jpg"
  },
  {
    "_id": "2",
    "firstName": "Jane",
    "lastName": "Doe",
    "image": "http://someImageUrl/jane.jpg"
  }
]

As you can see → ‘_id’ ← is a MUST !!!

Please read the following…

Furthermore → you can’t push an ARRAY without including objects (as of my experience), also stated in the shown DESCRIPTION!

MY BAD !!! I think i am overloaded!

You did all good regarding the rep-data-format… (you created an object with including ID → all good, my bad) !

const neuerDatenpunkt = {
    "_id": MyID,
    "Beschreibung": "Address:",
    "Inhalt":  itemObj.address.formatted
};

Ok → du bist vorerst auf dich allein gestellt.

Try to remodify your code. Sometimes it is better to recode from scratch (my own experience telling me this).

Hi @russion-dima

I have checked it again, but there is nothing essential missing in the code and it works.
Each “neuerDatenpunkt” receives a ID:

const neuerDatenpunkt = {
    "_id": MyID,
    "Beschreibung": "Denomination:",
    "Inhalt": "Catholic Church"
};

A “console.log(MyData)” results in the following output:

Yes, i already corrected…

Ok, no problem, I’m very thankful for the help :hugs:
I will now consolidate the code next.

export function fetchData(GoogleID) {
	const key = "...............";
    const url = "https://places.googleapis.com/v1/places/" + GoogleID + "?fields=regularOpeningHours,rating,userRatingCount,formattedAddress,internationalPhoneNumber&key=" + key
    return fetch (url, {method: 'get'}).then( (httpResponse) => {
	    if (httpResponse.ok) {
	    	return httpResponse.json();
		}
	})
}

Do me a vafour → show me one example of your response object →
return httpResponse.json();

What do you get in console?
Does this response has different variations, regardings it’s result-structure?
Or do you get always a constant data-package?

Why you do not transfer more code to the BACKEND?

All the following code…

async function myFunction(googleID, itemObj) {
    if (googleID) {
        fetchData(itemObj.googleId).then(function(response) {
            let Ergebnis_Open;
            let MyID = "0";
            let MyData = [];
            // Adresse von Google
            if (response.formattedAddress != undefined) {
                let num = parseInt(MyID);
                num = num +1;
                MyID = JSON.stringify(num);
                const neuerDatenpunkt = {
                    "_id": MyID,
                    "Beschreibung": "Address:",
                    "Inhalt": response.formattedAddress
                };
                MyData.push(neuerDatenpunkt);
                return MyData;
            } 
            else {
                if (itemObj.address != undefined) {
                    let num = parseInt(MyID);
                    num = num + 1;
                    MyID = JSON.stringify(num);
                    const neuerDatenpunkt = {
                        "_id": MyID,
                        "Beschreibung": "Address:",
                        "Inhalt":  itemObj.address.formatted
                    };
                    MyData.push(neuerDatenpunkt);
                    return MyData;
                }
            }
            // Konfession (immer katholisch bei Kirchen)
            if (itemObj.filterParksChurches != undefined) {
                if (itemObj.filterParksChurches[0] === "Churches") {
                    let num = parseInt(MyID);
                    num = num + 1;
                    MyID = JSON.stringify(num);
                    const neuerDatenpunkt = {
                        "_id": MyID,
                        "Beschreibung": "Denomination:",
                        "Inhalt": "Catholic Church"
                    };
                    MyData.push(neuerDatenpunkt);
                    return MyData;
                }
            }
            // -------------  etc.
        });
    } 
    else {
        let MyID = "0";
        let MyData = [];
        // Adresse vom CMS
        if (itemObj.address != undefined) {
            let num = parseInt(MyID);
            num = num + 1;
            MyID = JSON.stringify(num);
            const neuerDatenpunkt = {
                "_id": MyID,
                "Beschreibung": "Address:",
                "Inhalt":  itemObj.address.formatted
            };
            MyData.push(neuerDatenpunkt);
            return MyData;
        }
        // Opening Hours vom CMS
        if (itemObj.openingHours != undefined) {
            let num = parseInt(MyID);
            num = num + 1;
            MyID = JSON.stringify(num);
            const neuerDatenpunkt = {
                "_id": MyID,
                "Beschreibung": "Opening Hours:",
                "Inhalt": itemObj.openingHours
            };
            MyData.push(neuerDatenpunkt);
            return MyData;
        }
        // -------------  etc.     
    }
}

…is not interacting with frontend-elements.

Push all that code to backend, you don’t need it on frontend.

Do all processes on BACKEND → and send only results to frontend.

@russian-dima

here is an example:

If Google has all the data available, the structure is the same as far as I can see. Individual data records are often missing because they are not available from Google, in which case I retrieve them from my own database if necessary.

I previously assumed that the code belongs in the frontend by default and is only written to the backend if necessary (e.g. for security reasons). In other words, everything should be in the backend if possible?

Just an idea for you…regarding the fetch function on your backend…

The shown function below, gives you the following options (features)…

  1. It can itarate into depth of objects
  2. It will search for required properties (of your choice → you can define the required properties) → see code.
  3. It will generate a separate RESULT-OBJECT including all required properties, no matter if the PROPERTY was found or not, it will be automatically filled up if not fund (with → ‘undefined’).

So this code will generate you ALWAYS a CONSTANT → RESULT-OBJECT → NO MATTER IF GOOLE-FETCH-RESULT will have missing properties, like you mentioned.

I hope you understand what i mean. I already assumed such a behaviour of your fetched RESULTS.

Implement this code on your backend aswell → in can help you to get a more compressed and more automated code.

function fillMissingProperties(obj, requiredProperties, defaultProperties) {
    let resultObj = {};

    // Iterate through required properties
    for (let prop of requiredProperties) {
        // Check if property is missing in obj
        if (!(prop in obj)) {
            // Fill the missing property with default value if available
            resultObj[prop] = defaultProperties[prop];
            if (resultObj[prop] === undefined) {
                resultObj[prop] = undefined;
            }
        } else {
            resultObj[prop] = obj[prop];
        }
    }

    // Recursively iterate through nested objects
    for (let prop in obj) {
        if (typeof obj[prop] === 'object') {
            const nestedObj = fillMissingProperties(obj[prop], requiredProperties, defaultProperties);
            for (let nestedProp in nestedObj) {
                resultObj[nestedProp] = nestedObj[nestedProp];
            }
        }
    }

    return resultObj;
}

// Example usage:
const obj = {
    person: {
        name: "John",
        age: 30,
        address: {
            city: "New York"
        }
    },
    car: {
        model: "Toyota"
    }
};

const requiredProps = ['name', 'age', 'lastname']; // Define required properties
const defaultProperties = { lastname: undefined }; // Define default properties

const newObj = fillMissingProperties(obj, requiredProps, defaultProperties);
console.log(newObj);

Let’s take this example-Object…

const obj = {
    person: {
        name: "John",
        age: 30,
        address: {
            city: "New York"
        }
    },
    car: {
        model: "Toyota"
    }
};

Your defined required properties are…
const requiredProps = ['name', 'age', 'lastname'];

So as you can see → lastname <— is missing inside of the object.
This missing PROPERTY including → undefined ← as value → will be automatically filled up inside the NEW-GENERATED RESULT-OBJECT, which will look like…

So your result-object will be…

{
    name: "John",
    age: 30,
    lastname: undefined,
    city: "New York",
    model: "Toyota"
}

The function makes sure that you always have the same structure and values inside of your object in a flattened-mode.

It will make your coding life a little bit easier :grin:

But wait the ‘_id’ property is missing !!! —> What to do ??? :thinking:

You already have an idea?

Good luck and happy coding! Expand your horizont and overthing your code again and again, until you got a real good result.

Thank you, I have now rewritten the code more dynamically/compactly as suggested. Before I optimise it further, it should run first :slight_smile:

I have the following problem: The output of MyData on the console in the function “createMyData” works. The output under “$W.onReady” is undefined (therefore the repeater is empty).

And console output 2 (in onready) appears before output 1 (in createMyData). I.e. is there probably still a problem with “await”?

import {fetchData} from 'backend/FetchHandler.jsw'

$w.onReady(function () {

	$w('#dynamicDataset').onReady(async() => {
		var curItem = $w('#dynamicDataset').getCurrentItem()
		let googleID = curItem.googleId
		let MyData = await createMyData(googleID, curItem)
		console.log("2: " + MyData)
		write_array_to_repeater(MyData)
	});

	async function createMyData(googleID, curItem) {
		if (googleID) {
			fetchData(googleID).then(function(response) {
				let MyID = "0";
				let MyData = [];

				// Adresse (von Google oder CMS)
				if (response.formattedAddress ?? false) {
					MyID = write_data_to_array(MyID, "Address:", response.formattedAddress, MyData)
				} else if (curItem.address ?? false) {
					MyID = write_data_to_array(MyID, "Address:", curItem.address, MyData)
				}
				
				// .............

				console.log("1: " + MyData)
				return MyData;
			})
		} else {
			let MyID = "0";
			let MyData = [];

			// Adresse
			if (curItem.address ?? false) {
				MyID = write_data_to_array(MyID, "Address:", curItem.address.formatted, MyData)
			}

			// ..................

			return MyData;
		}
	}

});

function write_data_to_array(MyID, description, content, MyData) {
	if (content ?? false) {
		let number = parseInt(MyID);
		number = number + 1;
		MyID = JSON.stringify(number);
		const neuerDatenpunkt = {
			"_id": MyID,
			"Beschreibung": description,
			"Inhalt": content
		};
		MyData.push(neuerDatenpunkt);
	}
	return MyID;
}

function write_array_to_repeater(MyData) {
	$w("#repeater").data = MyData;	
	// Daten in den Repeater schreiben
    $w("#repeater").onItemReady(($item, itemData, index) => {     
        $item("#Beschreibung").text = itemData.Beschreibung;
        $item("#Inhalt").text = itemData.Inhalt;
    });
}

Yes, then there is still a problem with await.