DEV Community

Cover image for How To Integrate Gmail API In Your Node.js Application
Goodness Chukwudi
Goodness Chukwudi

Posted on

How To Integrate Gmail API In Your Node.js Application

The Gmail API provides a powerful and flexible solution for integrating Gmail's robust features into your app. With it, you can enable users to read, send, and organise their emails, manage drafts, labels and much more. This guide will walk you through the steps to seamlessly integrate the Gmail API, transforming your application into a centralised hub for email management.

Whether you’re developing a productivity tool, a customer relationship management (CRM) system, or any application that benefits from streamlined email interactions, this guide will equip you with the necessary knowledge and tools. We'll cover everything from setting up API access and handling authentication to implementing core email functionalities.

A more robust implementation of a Gmail client using Gmail APIs can be found here github.com/Goodness-Chukwudi/gmail-client.

Setting Up Your Gmail Project On Google Cloud Console

1. Create a project. To create and set up your project on Google cloud console, head over to console.cloud.google.com and sign in with your gmail account. From the project dropdown at the top left corner, create a new project or select an existing project if you have any.

Create a project

After creating a project, select APIs and services from the navigation menu at the top left corner.

2. Enable Gmail APIs. Click on + ENABLE APIS AND SERVICES at the top of the page. This opens the API library page. Search for Gmail API select it and click on Enable to activate Gmail API.

3. Generate credentials for your app. To create the credentials you will use to connect from your app, click on Credentials in the sidebar under APIs and services and click on + CREATE CREDENTIALS at the top of the screen. Select OAuth Client ID, select Web application as application type and click Create. Copy the Client ID and Client secret; You will need it to connect to your Google project from your Node.js app.

Generate credentials

4. OAuth screen Setup. You will be using OAuth to give your app access to a user’s mailbox. Select OAuth consent screen under Credentials and fill the name of the app, support email and other details. Don’t bother setting up the scopes, you will do that from your application. Once this is done, you will head to your system to write some code

Code Setup and API Integrations

This tutorial is meant for people with prior knowledge of Node.js already. Consequently, it guide does not cover installation and setup of a Node.js app.

1. Project setup and dependency installation. After setting up your Node.js project, on your terminal run npm i googleapis to install googleapis from npm. Add the credentials you created to your .env file: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GMAIL_REDIRECT_URL(during OAuth authentication, google will call this endpoint with the authentication code if successful). The redirect url needs to be a live url on your server's domain, but you can use ngrok to expose your localhost to the internet to enable us to get the redirect during OAuth authentication. Head over to ngrok.com and follow the instructions there to set up ngrok locally. Once you have set up ngrok, the redirect url should be in this format <your-ngrok-ur/a-route-of-your-choice>. Eg. https://my-url/call_back_path.

2. OAuth authentication. Create a file, say gmail_service.js for your Gmail API calls code. Import googleapis and initialise OAuth2 and Gmail client from googleapis. We will use nodemailer’s MailComposer to compose a MIME message for our emails. So go ahead and install nodemailer from npm: npm i nodemailer. You will be needing it later.

import { google } from "googleapis";
const MailComposer = require("nodemailer/lib/mail-composer");

const oauth2Client = new google.auth.OAuth2(
  process.env.GOOGLE_CLIENT_ID,
  process.env.GOOGLE_CLIENT_SECRET,
  process.env.GMAIL_REDIRECT_URL
);

google.options({auth: oauth2Client})
const gmailClient = google.gmail('v1');
Enter fullscreen mode Exit fullscreen mode

Setting the auth on the Gmail client with the OAuth client authenticates our app's request to our Google project.

3. Generate the consent page url using the OAuth2 client and open it on a browser to give access to the app. In a real world scenario, this url is returned to the front end part of your app. This enables the user to grant your app access to their email.

const scopes = [
    "https://www.googleapis.com/auth/gmail.labels",
    "https://www.googleapis.com/auth/gmail.modify",
  ];
  const url = oauth2Client.generateAuthUrl({
    access_type: "offline",
    scope: scopes,
    prompt: "consent"
});
console.log(url)
Enter fullscreen mode Exit fullscreen mode

The scopes specifies the type of access and access level the app will request from the user. Refer to developers.google.com/gmail/api/auth/scopes for more on scopes.

4. Extract refresh token from auth code. You need to specify a route that matches the redirect url you specified in your application. This route must be a GET endpoint. Once the OAuth authentication is complete, Google will call your redirect url with the auth code in the request query. Extract the credentials from the returned code using the OAuth client. The extracted token is an object containing the refresh token among other fields. This refresh token should be saved in a secured place, preferably on your DB. This refresh token is sent alongside and validates every request to the API for this user.

import { Router } from "express";

const router = Router();
router.get("[path]", async (req, res) => {
    const code = req.query.code as string;
    const {tokens} = await oauth2Client.getToken(code)
    oauth2Client.setCredentials(tokens);
    console.log("refresh_token =========>  ", tokens.refresh_token);
})

Enter fullscreen mode Exit fullscreen mode

Accessing Gmail APIs

Now that your Gmail client is set up and authenticated, you can now call Gmail’s API to manage an email's inbox, send email and much more. Go to developers.google.com/gmail/api/guides to see all available APIs and their usages.

1. Retrieving messages. The list method retrieves messages in the provided mailbox. me refers to the email of the user attached to the refresh token of a request. You can replace me with the actual email address.

function retrieveMessages() {
  const messagesResponse = await gmailClient.users.messages.list({userId: 'me'});
  const messages = messagesResponse.data.messages;
  console.log("messages =========>  ", messages);
}

await retrieveMessages();
Enter fullscreen mode Exit fullscreen mode

2. Get one message.

function getOneMessage(messageId) {
  const messageResponse = await gmailClient.users.messages.get({userId: 'me', id: messageId});
  const message = messageResponse.data;

  let messageBody = "";
  const body = message.payload?.parts ? message.payload.parts[1].body : message.payload?.body;

  if (body?.data) {
    const buffer = Buffer.from(body.data, "base64");
    messageBody = buffer.toString("utf-8");
  } 

  //For messages with attachments
  if (body?.attachmentId) {
    const textPart = message.payload?.parts[0]?.parts[1]?.body?.data;
    if (textPart) {
      const buffer = Buffer.from(textPart, "base64");
      messageBody = buffer.toString("utf-8");
    }
  }

  console.log("message =========>  ", messageBody);
}

await getOneMessage(messageId);
Enter fullscreen mode Exit fullscreen mode

3. List drafts. The list method lists the drafts in the provided mailbox.

function listDrafts() {
  const draftsResponse = await gmailClient.users.drafts.list({userId: 'me'});
  const drafts = draftsResponse.data.drafts;
  console.log("drafts =========>  ", drafts);
}

await listDrafts();
Enter fullscreen mode Exit fullscreen mode

4. Get one draft

function getOneDraft(draftId) {
  const draftResponse = await gmailClient.users.drafts.get({userId: 'me', id: draftId});
  const draft = draftResponse.data;
  const payload = draft.message?.payload;

  let messageBody = "";
  const body = payload?.parts ? payload.parts[1].body : payload?.body;

  if (body?.data) {
    const buffer = Buffer.from(body.data, "base64");
    messageBody = buffer.toString("utf-8");
  } 

  //For drafts with attachments
  if (body?.attachmentId) {
    //@ts-ignore
    const textPart = payload?.parts[0]?.parts[1]?.body?.data;
    if (textPart) {
      const buffer = Buffer.from(textPart, "base64");
      messageBody = buffer.toString("utf-8");
    }
  }
  console.log("draft =========>  ", messageBody); 
}

await getOneDraft(draftId);
Enter fullscreen mode Exit fullscreen mode

5. List labels.

function listLabels() {
  const labelsResponse = await gmailClient.users.labels.list({userId: 'me'});
  const labels = labelsResponse.data.labels;
  console.log("labels =========>  ", labels);
}

await listLabels();
Enter fullscreen mode Exit fullscreen mode

6. Delete a message. A message is deleted by adding a TRASH label to it. Go to developers.google.com/gmail/api/guides/labels for more on message labels.

function deleteMessage(messageId) {
  await gmailClient.users.messages.trash({userId: 'me', id: messageId});
}

await deleteMessage(messageId);
Enter fullscreen mode Exit fullscreen mode

7. Batch delete messages. To delete multiple messages at a go, add TRASH to the label of each message.

function deleteMessages(messageIds) {
  const requestBody = {ids: messageIds, addLabelIds: ["TRASH"]};
  await gmailClient.users.messages.batchModify({userId: 'me', requestBody});
}

await deleteMessages(messageIds);
Enter fullscreen mode Exit fullscreen mode

8. Restore a deleted message.

function restoreMessage(messageId) {
  await gmailClient.users.messages.untrash({userId: 'me', id: messageId});
}

await restoreMessage(messageId);
Enter fullscreen mode Exit fullscreen mode

9. Send mail. The email API accepts only MIME email messages that's compliant with RFC 2822 and encoded as a base64 string.

function encodeEmail(email) {
  const mail = new MailComposer({
      from: "me",
      to: email.recipient,
      cc: email.cc,
      html: email.body,
      subject: email.subject,
      textEncoding: "base64"
    });

    mail.compile().build((err, message) => {
      if (err) console.log(err);
      const encodedEmail = Buffer
        .from(message)
        .toString('base64')
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=+$/, '');

      return encodedEmail;
    });
}

function sendMessage(emailObject) {
  const requestBody = {
      raw: await encodeEmail(emailObject)
  }
  await gmailClient.users.messages.send({userId: "me", requestBody});
}

await sendMessage(emailObject);
Enter fullscreen mode Exit fullscreen mode

10. Create drafts. Drafts represent an unsent message with the DRAFT label applied. The input will be the same with send email API inputs.

function createDraft(emailObject) {
  const requestBody = {
    raw: await encodeEmail()
  }

  await gmailClient.users.drafts.create({userId: 'me', requestBody});
}

await createDraft(emailObject);
Enter fullscreen mode Exit fullscreen mode

11. Update drafts. Messages do not actually get updated. The update request instead, destroys the message attached to the draft you want to update and replaces it with a new message containing the new MIME message you sent.

function updateDraft(emailObject, draftId) {
  const requestBody = {
    raw: await encodeEmail()
  }

  await gmailClient.users.drafts.update({userId: 'me', id: draftId, requestBody});
}

await updateDraft(emailObject, draftId);
Enter fullscreen mode Exit fullscreen mode

12. Delete drafts.

function deleteDraft(draftId) {
  await gmailClient.users.drafts.delete({userId: 'me', id: draftId});
}

await deleteDraft(draftId);
Enter fullscreen mode Exit fullscreen mode

13. Send drafts.

function sendDraft(draftId) {
  const requestBody = {
    id: draftId
  }
  await gmailClient.users.drafts.send({userId: 'me', requestBody});
}

await sendDraft(draftId);
Enter fullscreen mode Exit fullscreen mode

14. Revoke access. Though users can revoke access given to your application from their Gmail app, you can also give them a way to revoke the access from within your application using the revokeToken API.

function revokeAppAccess(refreshToken) {
  await oauth2Client.revokeToken(refreshToken);
}

await revokeAppAccess(refreshToken);
Enter fullscreen mode Exit fullscreen mode

Conclusion

A full list and implementation guide for all the features available on the Gmail API is available on developers.google.com/gmail/api/guides.

What’s Next?

A more robust implementation of a Gmail client app, with user authentication management using Node.js and Express.js and MongoDB is available on github.com/Goodness-Chukwudi/gmail-client. Please leave a star on the repo if you find it helpful. Also feel free to raise a PR if you want to contribute more features or improve existing ones. Suggestions are welcomed as well. Reach me on ibechechukwudi@gmail.com

Top comments (0)