Spice up your SaaS project with a Gmail Add-on using TypeScript and clasp

Marc Logemann
SaaS Startup Factory
8 min readAug 6, 2018

Leverage the power of cross-platform Gmail extensions

Since late 2017 Google provides the new Gmail Add-on system to the broad public. A lot of SaaS projects, not only document management or CRM systems, benefit from a tight integration into email. Since Gmail Add-ons are cross platform and even cross devices (mobile and desktop), they are a good candidate to extend the reach of your SaaS business. They simply let you integrate your app into Gmail and extend Gmail to handle quick actions.

But what is a Gmail Add-on?

Quoted from Google: Gmail add-ons are developed using Apps Script, a scripting language based on JavaScript that serves as a connective platform between Google products like Docs, Sheets, Drive, and Gmail. Every Gmail add-on has a corresponding Apps Script project where you define your add-on’s appearance and behavior.

The good thing about Gmail Add-ons are the way they are distributed. Google provides an Add-on-Repository which looks like a store, where you can easily select which Add-on you want to install. Then its only a matter of a few clicks to have them integrated into your Gmail account.

So the natural way of starting a HelloWorld project is opening their Online Editor at https://script.google.com/, create a project and hack away. This really works well up until the point that your HelloWorld project is running and can be seen in the Gmail interface. Then you start coding some real features and start realizing a few things:

  • I dont want to code in a quite weak online editor
  • I need my GIT repository
  • I need a proper deployment pipeline
  • (and perhaps) i really would like to use a typed language instead of Apps Script aka Javascript + X.

And to the rescue, there is a deployment framework called clasp which does all of the things mentioned.

Clasp. Code locally. Deploy easily.

Clasp can be found at https://github.com/google/clasp. Its a Node.js based framework which should be installed globally. Like many other node packages, installation is easy as it gets and you are up and running in a few minutes. Lets make this happen really fast.

To install just type:
(assumed you have npm installed before, if not, check out their site at https://www.npmjs.com/)

sudo npm i @google/clasp -g

After that just create a folder in the filesystem and create the project like this:

$ mkdir attachmentcounter
$ cd attachmentcounter
$ clasp create "AttachmentCounter"

Now you have a manifest file called appscript.json in your newly created folder and a clasp config file called .clasp.json. Of course you should start putting that folder under GIT control. I wont show the procedure here but its easy enough to do. Then lets modify the appscript.json file so that the Add-on knows what code to run.

{
"timeZone": "America/New_York",
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
],
"dependencies": {
"enabledAdvancedServices": [{
"userSymbol": "Gmail",
"serviceId": "gmail",
"version": "v1"
}]
},
"gmail": {
"version": "TRUSTED_TESTER_V2",
"name": "Attachment Counter",
"logoUrl": "http://okaydocs.com.s3-website.eu-central-1.amazonaws.com/images/okaydocs_addonlogo2.png",
"contextualTriggers": [{
"unconditional": {
},
"onTriggerFunction": "getContextualAddOn"
}]
},
"exceptionLogging": "STACKDRIVER"
}

So basically we put in oauth scopes that we need in order to access Gmail features of the users inbox. When you use the Add-on for the first time in Gmail, you need to auth against Google with the scopes defined here. Then we enable some advanced Gmail services and define the startup function with “onTriggerFunction”. In our case the function getContextualAddon() is called when the user click on an email in Gmail. You might also want to change the logoUrl. Right now its an image from one of our upcoming SaaS projects. If you want to know more about the appscript.json file, consult the docs at https://developers.google.com/apps-script/concepts/manifests.

Let’s do some TypeScript coding

Your impatience grows so that we really should start coding a little bit right? But in order to have the TypeScript definitions for our libraries, we need to get them via npm. We use the now to be created package.json file to declare that dependency, so that colleagues dont need to pull it by themselves. We create the package.json and add the library with the type definitions:

$ npm init
(now hit many times return to answer with defaults)
$ npm add @types/google-apps-script

If you see a node_modules/@types folder in your project root, then everything worked as expected. You know have your types in the correct place.

We create the main typescript file that will hold our startup function. In fact this example should stay easy, so there is no other TypeScript file in this project. But you could easily create as many files as you like and reference functions / methods in other files without importing or including them. So create AttachmentCounter.ts in a src folder of your project root as follows:

/**
* Returns the contextual add-on data that should be rendered for
* the current e-mail thread. This function satisfies the
* requirements of
* an 'onTriggerFunction' and is specified in the add-on's manifest.
*
*
@param {Object} event Event containing the message ID and other
* context.
*
@returns {Card[]}
*/
import GmailMessage = GoogleAppsScript.Gmail.GmailMessage;
import GmailAttachment = GoogleAppsScript.Gmail.GmailAttachment;
import CardBuilder = GoogleAppsScript.Card.CardBuilder;

function getContextualAddOn(event) {
let message: GmailMessage = getCurrentMessage(event);
let files: Array<GmailAttachment> = message.getAttachments();
let card = createCard(files);
return [card.build()];
}


function createCard(attachments: Array<GmailAttachment>) {
let card: CardBuilder = CardService.newCardBuilder();
let title: string = attachments.length + " attachments found!";
card.setHeader(CardService.newCardHeader()
.setTitle(title)
.setImageUrl("http://okaydocs.com.s3-website.eu-central-1.amazonaws.com/images/okaydocs_addonlogo.png"));
return card;
}

/**
* Retrieves the current message given an action event object.
*
@param {Event} event Action event object
*
@return {Message}
*/
function getCurrentMessage(event) {
let accessToken = event.messageMetadata.accessToken;
let messageId = event.messageMetadata.messageId;
GmailApp.setCurrentMessageAccessToken(accessToken);
return GmailApp.getMessageById(messageId);
}

When coding this, you really should use a TypeScript aware IDE like VSCode or Webstorm or whatever you like. You get plenty of help like auto-imports and stuff. If your IDE works well, you can, for example, do autocomplete for card which is of type CardBuilder. If you want to know more why TypeScript is the better language, see https://hackernoon.com/typescript-vs-javascript-b568bc4a4e58, but dont start a flame war in the comments section. Its just my personal oppinion and my background is like 17 years java, so you might get why i like types.

But lets get back to the code. Our start function is getContextualAddOn(event) which we have defined in the appscript.json file. We extract the currently clicked email message from the event object and just create a card based on the attachments of the message. In fact it only displays the number of attachments the email have. This Add-on is super simple and doesnt provide user actions or other widgets Google provides.

Deploy the code with clasp

Now lets use clasp to push our changes to the Google infrastructure which is Google Drive based in the case of Gmail Add-ons. The good thing is, you really dont need to know how this code is packaged and stored thanks to clasp. Lets go back to the shell and be sure you are in project root and execute:

$ clasp login

Clasp redirect you to the the Google Login website. From there you need to authorize yourself with your Gmail/Google Account. From now on clasp is authorized to interact with the your Google (Drive) account to do all the code uploading and related actions on your behalf.

Now we want to put the locally developed code into the google cloud. For that simply execute the next command. Of course the tree below the command is printed out by the command itself to give you visual feedback of the process.

$ clasp push
└─ appsscript.json
└─ src/AttachmentCounter.ts
Pushed 2 files.

To double check that everything is in place in the Google cloud, just execute:

$ clasp open

This will open the project in the browser and you will see the Google Online Editor for Apps Script. But the code you will see there has nothing to do with typescript, its in fact Apps Script (aka Javascript). So what clasp has done during push was (beside a lot of other stuff) transpiling the TypeScript code into Apps Script. You shouldnt code the script from there because that was why we chose clasp in the first place. But from the editor you need to get the ID of the Gmail Add-on in order to install it into your Gmail account.

Install your Add-on for testing purposes

To get the ID from the editor click Publishdeploy from manifest. From there you can grab the ID of the project by clicking Get Id. Since we are still in development/testing phase, we of course dont want to publish to the Google Add-on Repository yet. But luckily you can install the beta version as developer Add-on.

Now open the settings section in your Gmail webapp by clicking on the cog icon and select settings as shown below.

How to get to the settings screen in Gmail

From there go to the “Add-ons” tab.

Settings Add-on section

Now simply insert the ID you grabbed on the Apps Script editor into the “Developer add-ons” input field and select install.

See the Add-on in action

If everything went ok, you will see the Add-on by opening an email in your inbox and looking at the right side of the Gmail screen.

Add-on at the right side of the Gmail Screen

By clicking on the icon, you get the content of your Add-on which in our case looks pretty simple. For first time usage, you will get a security warning because you need to grant access that the plugin can read your currently open email. Remember the “oauthScopes” you defined in the manifest.

Gmail Add-on in action

From there you could do a lot more stuff with Add-on like adding Buttons or Links for triggering actions, showing information to the user which you got via a REST call or whatever. The sky (or the Google SDK) is the limit.

If you feel your plugin is ok and you want to deploy the first version to the store, dont forget to use the “version” command of clasp to version your Add-on before publishing. The complete process of publishing is not part of this blog. Have fun with Google Add-ons. And to be honest, the fun started when TypeScript was available as language option.

(special thanks to Grant Timmerman from Google for a super fast fix on a TypeScript transpilation issue with clasp when using IntelliJ IDEA)

If you want to know more about well architected applications on cloud platforms or things you need to have a successful SaaS business, feel free to head over to https://okaycloud.de for more information.

--

--

Marc Logemann
SaaS Startup Factory

Entrepreneur & CTO - (AWS) Software Architect, likes Typescript, Java and Flutter, located in the Cloud, Berlin and Osnabrück.