DEV Community

Matt Crowder
Matt Crowder

Posted on

Creating a node script that can write to google docs

My wife and I use a shared google doc for our grocery list, and I often find myself forgetting to add groceries to our list which then results in us forgetting to buy things at the grocery store.

So I setup an automation when I tell google to add "something", IFTTT knows that when I say that, it sends a web request to my heroku server which then uses the google javascript api to update my google doc. I am happy with this setup and want to share with you all how I did it!

So this is Part 1 of a multi-part blog series on how to accomplish what I mentioned above! Stay tuned for the next parts!

Go to https://docs.new in your browser, and that will create a new google doc.
Title it Groceries (or whatever you want).

Note the id of the doc. It's between d/ and /edit in the url.

Mine is 1OvhQtG2WYagqI-zBk_IXTRWhLijCu2_hB-ExdyCYI54

Creating oauth tokens with google nodejs apis

Run the following commands.

We'll be using yarn from here on out.

mkdir grocery-adder && cd grocery-adder
yarn init -y
git init
touch .gitignore
yarn add googleapis
mkdir src && touch src/index.js

Add the following to your .gitignore file:

.idea
node_modules

Keep in mind that I will have credentials.json and token.json ignored in my final example so I don't compromise my google account to you all :)

For the rest of the oauth setup, I'm really just summarizing the following tutorial: https://developers.google.com/gmail/api/quickstart/nodejs

  1. Go to https://console.developers.google.com/apis/library/docs.googleapis.com
  2. Click Enable
  3. Go to https://developers.google.com/gmail/api/quickstart/nodejs
  4. Click Enable the Gmail API
  5. Save credentials.json to grocery-adder
  6. Paste the following code into src/index.js
const fs = require("fs");
const readline = require("readline");
const { google } = require("googleapis");

// If modifying these scopes, delete token.json.
const SCOPES = ["https://www.googleapis.com/auth/documents"];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = "token.json";

// Load client secrets from a local file.
fs.readFile("credentials.json", (err, content) => {
  if (err) return console.log("Error loading client secret file:", err);
  // Authorize a client with credentials, then call the Gmail API.
  authorize(JSON.parse(content), () => console.log("authorized!"));
});

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) {
  const { client_secret, client_id, redirect_uris } = credentials.installed;
  const oAuth2Client = new google.auth.OAuth2(
    client_id,
    client_secret,
    redirect_uris[0]
  );

  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, (err, token) => {
    if (err) return getNewToken(oAuth2Client, callback);
    oAuth2Client.setCredentials(JSON.parse(token));
    callback(oAuth2Client);
  });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback for the authorized client.
 */
function getNewToken(oAuth2Client, callback) {
  const authUrl = oAuth2Client.generateAuthUrl({
    access_type: "offline",
    scope: SCOPES
  });
  console.log("Authorize this app by visiting this url:", authUrl);
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  rl.question("Enter the code from that page here: ", code => {
    rl.close();
    oAuth2Client.getToken(code, (err, token) => {
      if (err) return console.error("Error retrieving access token", err);
      oAuth2Client.setCredentials(token);
      // Store the token to disk for later program executions
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), error => {
        if (error) return console.error(error);
        console.log("Token stored to", TOKEN_PATH);
      });
      callback(oAuth2Client);
    });
  });
}

  1. Run node src/index.js
  2. Follow the prompts on the terminal
  3. You'll get a warning saying This app isn't verified, that's ok! You are the app!!
  4. Great now you have token.json stored at the root of grocery-adder
  5. Run node src/index.js and you'll see that authorized! is printed on the terminal
  6. Now we can delete a lot of that setup code and write to your google doc!

Your file should look like the following:

const { google } = require("googleapis");
const token = require("../token.json");
const credentials = require("../credentials.json");

function authorize() {
  const { client_secret, client_id, redirect_uris } = credentials.installed;
  const oAuth2Client = new google.auth.OAuth2(
    client_id,
    client_secret,
    redirect_uris[0]
  );
  oAuth2Client.setCredentials(token);
  return oAuth2Client;
}

async function main(YOUR_DOCUMENT_ID) {
    const auth = await authorize();
    const docs = google.docs({
      version: "v1",
      auth
    });
    await docs.documents.batchUpdate({
      auth,
      documentId: YOUR_DOCUMENT_ID,
      requestBody: {
        requests: [
          {
            insertText: {
              location: {
                index: 1
              },
              text: "hello!\n"
            }
          }
        ]
      }
    });
}

main("YOUR_DOCUMENT_ID");

If you see text: "hello!\n", you'll see what will be getting written to our doc!

Replace "YOUR_DOCUMENT_ID" with your document id and run it!

Now, if you go to your new google doc, you'll see hello! printed there! If you keep running this, hello! will continue getting added.

The code to generate the token can be found here

The code to write to the google doc can be found here

Top comments (1)

Collapse
 
juandresyn profile image
Juandres Yepes Narvaez

This is awesome!

You saved me a lot of time with this!