Read GoogleSheet in VanillaTS project

Sorry in advance if this post has nothing to do here but I confess I’m a bit lost (I’ve created a new post on the codesandBox community as well).
I’m using a Vanilla Typescript template to develop an application using speckle’s APIViewer. So I have an index.html file, a main index.ts file
I’ve set everything in my google settings to use the GoogleSheet API.
I’ve created a sheet and now I’m trying to read the data in my sheet…
I’m looking for examples…I confess I’m lost…could someone please give me some hints?

I finaly use : const response = await fetch(url);

Hey @pierrenavarra , kind of out of the normal scope for us, so I hope you don’t mind I’ve moved your question to the general discussion.

I have done a fair deal of reading Google Sheets from scripts in my time (a killer feature of Sheets tbh)

I’ll also assume that

means you sorted your own problem?

If not, I can see what I can do to help.

Hi @jonathon

Thank you for taking the time to reply.
Since my message, I’ve been able to read data in a GoogleSheet, but when I write, I get a 401 error and I can’t explain it…
Here’s my js code and screenshots of my OAuth setup at Google Cloud

// index.js

// Configuration de l'API MapTiler (optionnel si besoin dans d'autres parties)
maptilerClient.config.apiKey = "0IOxISLhuYyV9WWBRWYR";

// Fonction pour récupérer les données de Google Sheets
window.fetchData = async function() {
  const apiKey = "myKey";
  const spreadsheetId = "1CpczW9Y4ggB1HnUWBn88vG3Gnt-tFiPnLXEZl5s1UXU";
  const range = "COORDINATES!A2:E6";
  const url = `${spreadsheetId}/values/${range}?key=${apiKey}`;

  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);

    const jsonData = await response.json();
    const googleSheetData = [];

    jsonData.values.forEach((row) => {
      const elementId = parseInt(row[0]);
      const latitude = parseFloat(row[1]);
      const longitude = parseFloat(row[2]);
      const name = row[3];
      const mapUrl = row[4];

      googleSheetData.push({ elementId, latitude, longitude, name, mapUrl });

    // Récupérer l'elementId saisi par l'utilisateur
    const elementIdInput = parseInt(document.getElementById('elementIdInput').value);

    // Filtrer les données par elementId
    const filteredData = googleSheetData.filter(item => item.elementId === elementIdInput);

    // Afficher les données filtrées dans le DOM

    // Remplir les champs latitude et longitude si les données sont trouvées
    if (filteredData.length > 0) {
      document.getElementById('latitudeInput').value = filteredData[0].latitude;
      document.getElementById('longitudeInput').value = filteredData[0].longitude;

  } catch (error) {
    console.error("Error fetching data:", error);
    document.getElementById('fetchedData').innerHTML = "<p>Error fetching data from Google Sheets. Please try again later.</p>";

// Fonction pour afficher les données récupérées dans le DOM
function displayFetchedData(data) {
  const fetchedDataElement = document.getElementById('fetchedData');
  fetchedDataElement.innerHTML = '';

  if (data.length === 0) {
    fetchedDataElement.innerHTML = "<p>No data found for the given Element ID.</p>";

  data.forEach((item) => {
    const dataItem = `
      <div class="data-item">
        <p>Element ID: ${item.elementId}</p>
        <p>Name: ${}</p>
        <p>Latitude: ${item.latitude}</p>
        <p>Longitude: ${item.longitude}</p>
        <p>Map URL: <a href="${item.mapUrl}" target="_blank">${item.mapUrl}</a></p>
    fetchedDataElement.innerHTML += dataItem;

// Fonction pour initialiser l'authentification OAuth
async function authenticate() {
  const CLIENT_ID = myClientID';
  const REDIRECT_URI = ''; // Utilisez l'URI de redirection exact
  const SCOPES = '';

  const tokenClient = google.accounts.oauth2.initTokenClient({
    client_id: CLIENT_ID,
    scope: SCOPES,
    redirect_uri: REDIRECT_URI,
    callback: (response) => {
      if (response.error !== undefined) {
        throw response;
      console.log('Access token acquired:', response.access_token);
      localStorage.setItem('oauth_token', response.access_token);

  tokenClient.requestAccessToken({ prompt: 'consent' });

// Fonction pour mettre à jour les coordonnées dans Google Sheets
window.updateCoordinates = async function() {
  const CLIENT_ID = 'MyClientID';
  const REDIRECT_URI = 'http://localhost:5501/index.html'; // Remplacez par votre URI de redirection autorisé
  const SCOPES = '';
  const REFRESH_TOKEN = 'votre-refresh-token'; // Remplacez par votre refresh token

  const accessToken = localStorage.getItem('oauth_token');
  if (!accessToken) {
    await authenticate();

  const apiKey = "AIzaSyDf8su5VGkJyPrblaoNpXkYGIgPkmB5eQw";
  const spreadsheetId = "1CpczW9Y4ggB1HnUWBn88vG3Gnt-tFiPnLXEZl5s1UXU";
  const elementIdInput = parseInt(document.getElementById('elementIdInput').value);
  const latitudeInput = parseFloat(document.getElementById('latitudeInput').value);
  const longitudeInput = parseFloat(document.getElementById('longitudeInput').value);

  if (isNaN(elementIdInput) || isNaN(latitudeInput) || isNaN(longitudeInput)) {
    document.getElementById('fetchedData').innerHTML = "<p>Invalid input. Please provide valid Element ID, latitude, and longitude.</p>";

  const range = "COORDINATES"; // Nom de la feuille où les données doivent être mises à jour

  try {
    // Récupérer les données actuelles de la feuille
    const fetchUrl = `${spreadsheetId}/values/${range}?key=${apiKey}`;
    const fetchResponse = await fetch(fetchUrl);
    if (!fetchResponse.ok) {
      throw new Error(`HTTP error! Status: ${fetchResponse.status}`);
    const sheetData = await fetchResponse.json();

    // Trouver la ligne correspondant à l'elementId
    const rowIndex = sheetData.values.findIndex(row => parseInt(row[0]) === elementIdInput);
    if (rowIndex === -1) {
      document.getElementById('fetchedData').innerHTML = "<p>No matching Element ID found in the Google Sheet.</p>";

    // Mettre à jour les valeurs dans la ligne trouvée
    sheetData.values[rowIndex][1] = latitudeInput.toString();
    sheetData.values[rowIndex][2] = longitudeInput.toString();

    // Envoyer les données mises à jour vers Google Sheets
    const updateUrl = `${spreadsheetId}/values/${range}!A${rowIndex + 2}:E${rowIndex + 2}?valueInputOption=USER_ENTERED&key=${apiKey}`;
    const updateBody = {
      range: `${range}!A${rowIndex + 2}:E${rowIndex + 2}`,
      majorDimension: "ROWS",
      values: [sheetData.values[rowIndex]]

    const updateResponse = await fetch(updateUrl, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json'
      body: JSON.stringify(updateBody)

    if (!updateResponse.ok) {
      throw new Error(`HTTP error! Status: ${updateResponse.status}`);

    document.getElementById('fetchedData').innerHTML = "<p>Coordinates updated successfully in Google Sheets.</p>";

  } catch (error) {
    console.error("Error updating coordinates:", error);
    document.getElementById('fetchedData').innerHTML = "<p>Error updating coordinates in Google Sheets. Please try again later.</p>";


From memory, you must pass the authentication as a header on the PUT event, even with an authenticated client. I have found that using the publish client libraries (there is one for JS if not TS) under the gapi namespace is quite beneficial here.

const updateResponse = await fetch(updateUrl, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${accessToken}` // Include the access token here
      body: JSON.stringify(updateBody)

Added Authorization: 'Bearer ${accessToken}' in the headers for the PUT request. You can retrieve this from localStorage

Check if the access token is properly obtained and stored.

1 Like

Hi @jonathon
thank you for your reply
I’ll keep this and keep you posted

1 Like