Accès d'une Googlesheet avec Javascript.

Lien Javascript et Googlesheet

Le problème exposé dans ce sujet a été résolu.

Dans une démarche d’étude du Javascript, je cherche à créer une application JS locale qui se connecterait à un chiffrier Googlesheet distant afin d’y écrire et lire des données. Je souhaiterais me servir d’une API Google et éviter si possible l’utilisation de Node.js. Je suis néophyte en Javascript. Je galère depuis une semaine en cherchant une démo de base fonctionnelle en 2024 ou un tuto récent, autant que possible supporté par leurs auteurs. Je n’arrive pas même à l’authentification. Google,ChatGpt et Gemini qui me sont souvent utiles me conduisent vers des impasses dans ce cas. Un Humain en chair et en os pourrais il me guider. Suggestion de tutoriel ou texte pertinent serait appréciée.

Merci de votre temps.

Serge

+0 -0

Salut,

Je n’ai pas de réponse mais plein de question pour éclaircir le problème.

Puisque tu connais la notion d’API, et que tu t’es servi de plusieurs outils de recherche, tu as dû tomber assez vite sur la documentation officielle de celle-ci. Quel a été le problème ? Quelle page ne fonctionne pas ou n’a pas été comprise ?

Tu sembles dire que tu n’arrives pas à t’authentifier. Quelle est l’erreur remontée ? Est-ce que l’utilisateur est refusé ou l’accès au document ?

Enfin, pourquoi "éviter NodeJS" ? Quel est le moteur de ton application ?

+1 -0

Merci de votre réponse , voici le code révisé suggéré par Gemini poug gapi et qui selon lui " Il est tout à fait possible d’utiliser l’API Google Sheets avec JavaScript sans passer par Node.js. En fait, c’est une approche très courante, notamment pour les applications web front-end" et que j’essaie de mettre en service. Si vous me suggérez un meilleur point de départ, j’y suis ouvert.

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Google Sheets API avec JavaScript</title>
    <script src="private.js"></script>
    <script src="script.js"></script>


</head>



<body onload="handleClientLoad()">
    <script src="https://apis.google.com/js/api.js"></script>
    <script>
        const CLIENT_ID = client_id;
        const API_KEY = apiKey;
        const SPREADSHEET_ID = spreadsheetId;
        const SCOPES = url;

        // Charger le client gapi
        function handleClientLoad() {
            gapi.load('client:auth2', initClient);
        }

        // Initialiser le client avec l'API Sheets et l'authentification
        function initClient() {
            gapi.client.init({
                apiKey: API_KEY,
                clientId: CLIENT_ID,
                discoveryDocs: ["https://sheets.googleapis.com/$discovery/rest?version=v4"],
                scope: SCOPES
            }).then(() => {
                gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
                updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
            }, (error) => {
                console.error('Erreur lors de l\'initialisation de l\'API', error);
            });
        }

        // Mise à jour de l'interface selon l'état de connexion
        function updateSigninStatus(isSignedIn) {
            if (isSignedIn) {
                lireFeuille();
            } else {
                gapi.auth2.getAuthInstance().signIn();
            }
        }

        // Lire des données depuis Google Sheets
        function lireFeuille() {
            const range = 'Feuille1!A1:C10'; // Plage de cellules à lire
            gapi.client.sheets.spreadsheets.values.get({
                spreadsheetId: SPREADSHEET_ID,
                range: range
            }).then((response) => {
                const valeurs = response.result.values;
                if (valeurs) {
                    valeurs.forEach((ligne) => {
                        console.log(ligne.join(", "));
                    });
                } else {
                    console.log("Aucune donnée trouvée.");
                }
            }, (error) => {
                console.error('Erreur lors de la lecture de la feuille', error);
            });
        }

        // Écrire des données dans Google Sheets
        function ecrireDansFeuille() {
            const range = 'Feuille1!A1';
            const valeurs = [
                ["Nouvelle donnée 1", "Nouvelle donnée 2"]
            ];
            const body = {
                values: valeurs
            };

            gapi.client.sheets.spreadsheets.values.update({
                spreadsheetId: SPREADSHEET_ID,
                range: range,
                valueInputOption: "RAW",
                resource: body
            }).then((response) => {
                console.log(`${response.result.updatedCells} cellule(s) mise(s) à jour.`);
            }, (error) => {
                console.error('Erreur lors de l\'écriture dans la feuille', error);
            });
        }

        // Déconnexion
        function handleSignOut() {
            gapi.auth2.getAuthInstance().signOut();
        }
    </script>
</body>
</html>

A) En ce moment je bloque sur cette ligne: google.load('sheets', '4', function() { Qui provoque l’erreur Uncaught ReferenceError: google is not defined Alors que la ligne précédente .. <script src="https://apis.google.com/js/api.js"></script> Devrait selon moi satisfaire cette dépendance.

B) En supplément je cherche où je pourrais trouver l’information qui me mermettra d’implémenter ce qui m’est demandé par le commentaire qui suit dans le code,


   // Fonction d'authentification (à implémenter)

c) Enfin..

pourquoi "éviter NodeJS" ? Quel est le moteur de ton application ?

Je souhaite développer une application qui fonctionne dans un navigateur et qui n’impose pas d’installer autre chose sur l’oddi client. Selon ma compréhension les navigateurs ont la capacité de "driver" Javascript , avec certaines limitations, sans ajout.

Merci de votre temps.

Serge

+1 -0
Banni

Je suis un peu néophyte aussi mais il me semble que ton erreur "Uncaught ReferenceError: google is not defined", est due au fait que la méthode google.load que tu utilises est obsolète. Cette méthode faisait partie de l’ancienne bibliothèque Google Loader, qui n’est plus supportée.

Actuellement, pour utiliser les API Google dans une application JavaScript, il est plutôt recommandé d’utiliser la bibliothèque gapi (Google APIs Client Library for JavaScript). Je te laisse checker et revenir nous donner des news :)

Pour rebondir sur ce que Vilitors a dit, l’utilisation d’IA comme Gemini est très pratique pour des concepts fondamentaux (questions sur l’orienté objet, etc), mais il peut avoir certaines lacunes pour des questions précises sur une librairie ou framework (dans ce cas-ci le nom de l’objet gapi qui a changé).

Je te conseillerais donc de te fier à la documentation de Google directement qui est obligatoirement à jour: https://developers.google.com/sheets/api/quickstart/js .

Dans ce même site, il y a plusieurs cas d’utilisations avec des exemples de code en JavaScript (via l’onglet dans les blocs de code) . Par-exemple pour créer un spreadsheet https://developers.google.com/sheets/api/guides/create

+2 -0
Banni

D’accord avec Jeep. Il faut à minima confronté ce que te propose l’IA avec les bibliothèques récentes. Mais voici ce que me sort ChatGPT o1 avec ta requête. ça ne m’a pas l’air dégueu, je te laisse tester :

<!DOCTYPE html>
<html>
<head>
  <title>Google Sheets API</title>
  <script src="https://apis.google.com/js/api.js"></script>
  
  <script>
    // Vos identifiants (à remplacer par les vôtres)
    const CLIENT_ID = 'VOTRE_CLIENT_ID.apps.googleusercontent.com';
    const API_KEY = 'VOTRE_CLE_API';
    const SPREADSHEET_ID = 'VOTRE_SPREADSHEET_ID';

    // Scopes d'accès requis
    const SCOPES = "https://www.googleapis.com/auth/spreadsheets";

    function handleClientLoad() {
      gapi.load('client:auth2', initClient);
    }

    function initClient() {
      gapi.client.init({
        apiKey: API_KEY,
        clientId: CLIENT_ID,
        discoveryDocs: ["https://sheets.googleapis.com/$discovery/rest?version=v4"],
        scope: SCOPES
      }).then(function () {
        // Écouteur pour les changements d'état de connexion
        gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);

        // Vérifier si l'utilisateur est déjà connecté
        updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
      }, function(error) {
        console.error(JSON.stringify(error, null, 2));
      });
    }

    function handleAuthClick(event) {
      gapi.auth2.getAuthInstance().signIn();
    }

    function handleSignoutClick(event) {
      gapi.auth2.getAuthInstance().signOut();
    }

    function updateSigninStatus(isSignedIn) {
      if (isSignedIn) {
        listValues();
      }
    }

    function listValues() {
      gapi.client.sheets.spreadsheets.values.get({
        spreadsheetId: SPREADSHEET_ID,
        range: 'Feuil1!A1:B2',
      }).then(function(response) {
        var range = response.result;
        if (range.values.length > 0) {
          console.log('Données :');
          for (i = 0; i < range.values.length; i++) {
            var row = range.values[i];
            console.log(row);
          }
        } else {
          console.log('Aucune donnée trouvée.');
        }
      }, function(response) {
        console.error('Erreur : ' + response.result.error.message);
      });
    }
  </script>
</head>
<body>
  <button id="authorize_button" onclick="handleAuthClick()">Se connecter</button>
  <button id="signout_button" onclick="handleSignoutClick()">Se déconnecter</button>

  <script async defer src="https://apis.google.com/js/api.js" onload="this.onload=function(){};handleClientLoad()" onreadystatechange="if (this.readyState === 'complete') this.onload()">
  </script>
</body>
</html>

[EDIT par @viki53] : mise en forme du code

+0 -2

Je ne comprends pas si tu parles d’une extension navigateur (Add-On) ou si tu veux ouvrir ton fichier html depuis un dossier dans ton navigateur. Je crois comprendre l’option 2.

Node n’est pas forcément contradictoire avec ce que tu recherches, ça ne veux pas dire que le client devra installer des trucs, au contraire. Node permet de monter un serveur en Javascript et desservir justement ton html front, de cette façon le client n’a absolument rien à installer si ce n’est le navigateur (comme toute techno serveur web). Maintenant si tu ne vas pas mettre en place de serveur et que tu vas distribuer ton dossier tu n’as effectivement pas besoin de Node. Par contre, Node vient avec un outil nommé npm (pour Node Package Manager, et qui en fait une grande partie de sa force) qui permet de télécharger du code Javascript à inclure dans ton projet et qui peut servir pour le front-end aussi. Là encore le client n’a rien à installer car une fois télécharger ça fait partie de tes sources comme les fichiers que tu peux écrire toi même.

C’était une parenthèse car j’ai l’impression qu’on ne se comprend pas sur Node et ça peut venir d’une incompréhension. Ton application peut peut-être se passer complètement de node sans problème, je voulais juste que ce soit clair pour pas que tu te fermes des portes là ou un simple import pourrait t’épargner beaucoup de travail.

Concernant ton problème, je pense qu’il faut que tu parcours la documentation officiel que tu n’as je pense pas assez consulté. Les LLMs tels que Gemini peuvent donner des pistes, mais ce n’est pas fait pour être précis, il faut toujours aller voir la référence, la doc.
Et la documentation de Google présente ici les actions que l’api peut fournir, là l’interface Javascript, et là des exemples interactifs d’utilisation de cette API (pense à regarder l’onglet Javascript de l’éditeur)

N’aie crainte, l’authentification n’est pas un sujet trivial, pour des raisons de sécurité, c’est normal de se casser un peu les dents dessus.

+1 -0

Romantik, merci de tes explications sur Node, ça m’éclaire beaucoup sur l’architecture Javascript. Je consulte les références citées par Jeep et les tiennes et je reviens.

Je te conseillerais donc de te fier à la documentation de Google directement qui est obligatoirement à jour: https://developers.google.com/sheets/api/quickstart/js . Dans ce même site, il y a plusieurs cas d’utilisations avec des exemples de code en JavaScript (via l’onglet dans les blocs de code) . Par-exemple pour créer un spreadsheet https://developers.google.com/sheets/api/guides/create

Et la documentation de Google présente ici les actions que l’api peut fournir, là l’interface Javascript, et là des exemples interactifs d’utilisation de cette API (pense à regarder l’onglet Javascript de l’éditeur)

+0 -0

Autre point important : si tu veux accéder à un tableur Google Sheets avec une simple API key, alors il faut que ce document soit public, c’est-à-dire un tableur que tout le monde peut lire et modifier sans autorisation particulière (pour autant que l’on connaisse son identifiant).

Si tu veux garder ton tableur privé, alors il faudra authentifier tes requêtes avec OAuth ou un service account, auquel cas il faudra stocker les credentials quelque part. Tu parles de développer une "application JS locale", qu’est-ce que ça signifie exactement ? Si cela veut dire que tu ne vas pas partager ou déployer ta page web sur Internet, tu peux inclure les credentials directement dans la page web sans trop de souci. En revanche, si tu partages ta page web, il faudrait éviter qu’elle contienne ces credentials, sans quoi n’importe qui pourra les utiliser pour modifier librement ton tableur (et c’est là que Node.js devient utile).


Il y a quelques temps, j’ai moi-même utilisé un tableur en guise de simple base de données. Pour ce faire, j’ai créé un projet sur GCP dans lequel j’ai activé l’API de Google Sheets. J’y ai ensuite créé un service account avec qui j’ai partagé le tableur.

Voici le code qui s’occupe d’insérer une nouvelle ligne (ici avec Node.js, j’imagine qu’avec gapi ça doit être assez semblable), j’espère que ça peut t’aider :

import { google } from "googleapis"

const auth = new google.auth.GoogleAuth({
    credentials: {
        private_key: Buffer.from(process.env.PRIVATE_KEY, "base64").toString(),
        client_email: process.env.CLIENT_EMAIL,
    },
    scopes: ["https://www.googleapis.com/auth/spreadsheets"],
})

const sheets = google.sheets({ version: "v4", auth })
await sheets.spreadsheets.values.append({
    spreadsheetId: process.env.SHEET_ID,
    range: "Sheet1",
    valueInputOption: "USER_ENTERED",
    resource: {
        majorDimension: "ROWS",
        values: [[new Date(), "foo", "bar", 123]],
    },
})

Merci Olybri, la synthèse sur la sécurité autour de Googlesheet m’est très éclairante. Aussi de mes lectures je crois comprendre que des changements chez Google liés a la sécurité font que tout code lié a cet aspect et plus agé qu’environ un an est questionnable. Plusieurs soulignent l’importance de Node, les API Google ne suffisent elles pas ? Probablement un concept qui m’échappe.

+0 -0

Plusieurs soulignent l’importance de Node, les API Google ne suffisent elles pas ? Probablement un concept qui m’échappe.

dubser

Peu importe que ton code soit exécuté côté client ou serveur, tu communiqueras avec l’API de Google d’une façon ou d’une autre. La seule chose qui change, c’est la bibliothèque cliente que tu vas utiliser : pour le frontend tu as gapi, pour Node.js tu as le paquet googleapis.

Comme je l’ai dit, si tu souhaites simplement développer une page locale que tu ne vas pas partager, tu peux effectivement te passer d’un backend type Node.js et tout faire côté client.

En revanche, si tu souhaites publier ta page, il vaut peut-être mieux déployer un backend qui s’occupe de la communication avec l’API. Cela fait que les credentials restent stockés côté serveur et ne seront jamais accessibles côté client. J’ai mentionné Node.js mais n’importe quel environnement backend fera l’affaire (PHP, Python, etc.).

Ce scénario est comparable au développement d’une application traditionnelle : en principe, on ne va jamais inclure les identifiants de la base de données directement dans l’application cliente. Au lieu de cela, on met en place un backend, qui est une sorte d’intermédiaire entre le client et la base de données. Mais à nouveau, ce n’est pas indispensable pour que ça fonctionne.

+2 -0

Ouf… Enfin un premier succès, après avoir ramé plusieurs jours, dans la lecture d’un fichier Googlesheet par une application Javascript. Grâce a vos références, j’ai trouvé ce démo associé à un exemple fonctionnel à cette date. https://developers.google.com/sheets/api/quickstart/js?hl=fr Je joint le code que j’ai testé et qui authentifie un utilisateur et par la suite lit les données d’une feuille de calcul, qui est un exemple fourni par Google. Cette démo est proche de mon besoin et je pourrai l’adapter. C’est un excellent point de départ pour moi. Merci à tous ceux Olybri,Romantik,Vilitors,Jeep qui ont contribué à ma progression.


<!-- Inspiré de  ..... -->
<!-- https://developers.google.com/sheets/api/quickstart/js?hl=fr -->
<!DOCTYPE html>
<html>
  <head>
    <title>Sheets API Quickstart</title>
    <meta charset="utf-8" />
    <script src="private.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <p>Sheets API Quickstart</p>

    <!--Add buttons to initiate auth sequence and sign out-->
    <button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
    <button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>

    <pre id="content" style="white-space: pre-wrap;"></pre>

    <script type="text/javascript">
      /* exported gapiLoaded */
      /* exported gisLoaded */
      /* exported handleAuthClick */
      /* exported handleSignoutClick */

      // TODO(developer): Set to client ID and API key from the Developer Console
      const CLIENT_ID = client_id;
      const API_KEY = apiKey;

      // Discovery doc URL for APIs used by the quickstart
      const DISCOVERY_DOC = 'https://sheets.googleapis.com/$discovery/rest?version=v4';

      // Authorization scopes required by the API; multiple scopes can be
      // included, separated by spaces.
      const SCOPES = 'https://www.googleapis.com/auth/spreadsheets.readonly';

      let tokenClient;
      let gapiInited = false;
      let gisInited = false;

      document.getElementById('authorize_button').style.visibility = 'hidden';
      document.getElementById('signout_button').style.visibility = 'hidden';

      /**
       * Callback after api.js is loaded.
       */
      function gapiLoaded() {
        gapi.load('client', initializeGapiClient);
      }

      /**
       * Callback after the API client is loaded. Loads the
       * discovery doc to initialize the API.
       */
      async function initializeGapiClient() {
        await gapi.client.init({
          apiKey: API_KEY,
          discoveryDocs: [DISCOVERY_DOC],
        });
        gapiInited = true;
        maybeEnableButtons();
      }

      /**
       * Callback after Google Identity Services are loaded.
       */
      function gisLoaded() {
        tokenClient = google.accounts.oauth2.initTokenClient({
          client_id: CLIENT_ID,
          scope: SCOPES,
          callback: '', // defined later
        });
        gisInited = true;
        maybeEnableButtons();
      }

      /**
       * Enables user interaction after all libraries are loaded.
       */
      function maybeEnableButtons() {
        if (gapiInited && gisInited) {
          document.getElementById('authorize_button').style.visibility = 'visible';
        }
      }

      /**
       *  Sign in the user upon button click.
       */
      function handleAuthClick() {
        tokenClient.callback = async (resp) => {
          if (resp.error !== undefined) {
            throw (resp);
          }
          document.getElementById('signout_button').style.visibility = 'visible';
          document.getElementById('authorize_button').innerText = 'Refresh';
          await listMajors();
        };

        if (gapi.client.getToken() === null) {
          // Prompt the user to select a Google Account and ask for consent to share their data
          // when establishing a new session.
          tokenClient.requestAccessToken({prompt: 'consent'});
        } else {
          // Skip display of account chooser and consent dialog for an existing session.
          tokenClient.requestAccessToken({prompt: ''});
        }
      }

      /**
       *  Sign out the user upon button click.
       */
      function handleSignoutClick() {
        const token = gapi.client.getToken();
        if (token !== null) {
          google.accounts.oauth2.revoke(token.access_token);
          gapi.client.setToken('');
          document.getElementById('content').innerText = '';
          document.getElementById('authorize_button').innerText = 'Authorize';
          document.getElementById('signout_button').style.visibility = 'hidden';
        }
      }

      /**
       * Print the names and majors of students in a sample spreadsheet:
       * https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
       */
      async function listMajors() {
        let response;
        try {
          // Fetch first 10 files
          response = await gapi.client.sheets.spreadsheets.values.get({
            spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
            range: 'Class Data!A2:E',
          });
        } catch (err) {
          document.getElementById('content').innerText = err.message;
          return;
        }
        const range = response.result;
        if (!range || !range.values || range.values.length == 0) {
          document.getElementById('content').innerText = 'No values found.';
          return;
        }
        // Flatten to string to display
        const output = range.values.reduce(
            (str, row) => `${str}${row[0]}, ${row[4]}\n`,
            'Name, Major:\n');
        document.getElementById('content').innerText = output;
      }
    </script>
    <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
    <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
  </body>
</html>
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte