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.
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.
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');
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)
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);
})
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();
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);
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();
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);
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();
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);
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);
8. Restore a deleted message.
function restoreMessage(messageId) {
await gmailClient.users.messages.untrash({userId: 'me', id: messageId});
}
await restoreMessage(messageId);
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);
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);
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);
12. Delete drafts.
function deleteDraft(draftId) {
await gmailClient.users.drafts.delete({userId: 'me', id: draftId});
}
await deleteDraft(draftId);
13. Send drafts.
function sendDraft(draftId) {
const requestBody = {
id: draftId
}
await gmailClient.users.drafts.send({userId: 'me', requestBody});
}
await sendDraft(draftId);
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);
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)