Geek Culture
Published in

Geek Culture

How to Build an API Wrapper in Google Apps Script

No Google Apps Script SDK for your favorite API? No problem!

What’s an API Wrapper? Why You Might Want to Build One

Every developer at some point needs to make use of an external service to extend their code. APIs are built with this purpose in mind; they allow us to communicate with other services and applications over HTTP in a standardized way. Another option is to use NPM modules, so check out my previous article about this issue. Under the hood, Google Apps script makes use of various Google APIs, such as the Google Sheets API or the Google Drive API.

In this article, we will examine three different real-world APIs: Trello, ClickSend, and BigQuery. For reference, ClickSend and BigQuery require a credit card.

If you look at the docs for the Trello API, you will notice that their documentation has SDKs for a number of languages, namely Node.js, Java, Python, and PHP, but not Google Apps Script.

There is never an SDK for Google Apps Script

In fact, I’ve never seen a Google Apps Script SDK made available, as, surprise surprise, it’s not the most popular choice for professional development. It’s time to start changing that, so we are going to learn to build our own!

If you wish to go straight to the solution, here’s the GAS API Wrapper repo.

The Anatomy of a RESTful API

This section is just a refresher. If you have experience with APIs, feel free to skip it. Yes, I will be talking about RESTful APIs here, not SOAP (never will) or GraphQL (some other time).

A RESTful API is a service that returns a response to a request over HTTP with a response code (i.e., letting us know if the request was successful or not) and some data. Typically, this is in JSON format, but it can be XML or plain text as well, when a request is sent.

When querying an API, you are transmitting information in the following ways:

  1. You are querying specific endpoints. Every API has a base URL. Using ClickSend as an example, their base URL is https://rest.clicksend.com/v3. However, sending an SMS requires appending the following endpoint: /sms/send
  2. The URLs can contain query strings.
  3. Every request is sent with a specific method, typically POST, GET, PUT, or DELETE, that represent the four basic CRUD operations, respectively.
  4. Requests also contain headers with meta- information about the request (e.g., the content type or authorization tokens).
  5. POST and PUT requests typically contain the payload, which can contain more data than the query string.

I mentioned how some APIs require authorizations when requests are linked to specific accounts. Our wrapper will be able to handle the three most popular authorization types: Basic, Bearer, and token.

Let’s Look at the Documentation

Let’s start with Trello. We will do some CRUD operations on cards.

https://developer.atlassian.com/cloud/trello/guides/rest-api/api-introduction/

If you’ve never used Trello, it’s a collaboration tool that organizes projects into boards with lists and cards.

Trello’s architecture is rather simple: cards are contained in lists, which are contained in boards. In our case, we will need to be able to do the following:

  • Get all boards.
  • Get a specific board.
  • Get a specific board’s lists.
  • Get a specific list.
  • Get a list’s cards.
  • Get a specific card’s name and data.
  • Create a new card.
  • Update a card.
  • Delete a card.

Let’s get to it.

Time to Send Some Requests

First, let’s create a board with a couple of lists and several cards in each. This is what my demo Trello account looks like:

A Trello board with 3 lists with 2 cards each in my demo account

It is a board with three links lists containing two cards each.

Now, let’s decompose the request that must be made to get a list of boards on Trello:

  • The endpoint: https://api.trello.com/1
  • The path: /members/me/boards
  • The authentication requires a key and a token in the query string: ?key={key}&token={token}
  • Method: GET
  • Payload: none

The only thing that’s missing is a key and token. Go to the settings section of your account (create one if you don’t already have one) and copy them from there. Then, save them to a global object in your source code. I prefer storing this kind of data in a separate file named .env.gs, as I can then add it to .gitignore and not expose them on GitHub. It’s good practice to keep your credentials separate from the rest of your code. All we need is this:

I like to store my credentials in the ENV onbject in a separate .env.js file

Now, let’s build a function that will send our requests and accept the necessary information passed as parameters:

At this stage, we’re simplifying URL handling. We’re not breaking it down to base, endpoint, and query string for simplicity, but we will at a later stage.

Here is what the function does:

  1. It initializes the options object.
  2. It adds them to the options, provided that we have headers and a payload.
  3. It sends a request with UrlFetchApp.fetch().
  4. It saves the response, received as a string, to a variable.
  5. It parses the string to object and returns it.

Now, we’re ready to query the Trello API with this function:

At this stage, it’s just a matter of feeding the right parameters into our doRequest() function. If we log out the output of this function, we get a JSON object representing an array of boards linked to our account, including its name and id (which we will later need).

Getting a list of lists for a specific board and a list of cards in a specific list requires adding the board id and the list id to the path, respectively. The corresponding GET functions now look like this:

Put This in a Proper Class for Easy Maintenance and Readability

Let’s take this code a step further with another level of abstraction:

The _doRequest() is now a private method, not returned by the IIFE. Thus, it is hidden from the user. Additionally, _baseUrl, _key, and _token are private properties that are set during the creation process. They can be read, but they cannot be updated or deleted once the class is instantiated.

The getBoards() method now simply passes the path and url parameters to the _doRequest() method, and it does all of the heavy lifting. The methods getList(), getCards(), newCard(), and renameCard() are not defined. I will leave that up to you as a practical exercise; all you need are the correct arguments for the _doRequest() method.

Using this class is now extremely simple:

Creating a General-Purpose Wrapper

Thus far, we’ve been able to build a wrapper that is specific to the Trello API. Taking this one step further, I built a library that can wrap any API, which is available on GitHub.

To get started, git clone it from the repo

git clone git@github.com:WildH0g/gas-api-wrapper.git

or just copy and paste the /gas/APIWrapper.js file into your project.

The library uses the builder pattern (you can find additional information on design patterns in my previous article).

Let’s look at some examples. First, let’s wrap Trello again:

The above factory function returns an instance of APIWrapperBuilder. It’s instantiated with the Trello base URL, and the following options:

  • type: This is the authentication type, which in this case is KeyToken, but Basic and Bearer are also supported.
  • addTo: This is where to add the the key and token to, and supports query or headers.
  • token: This is an object containing the token name and value.
  • secret: This is an object containing the secret name and value.

Then, we have a bunch of addMethod calls that define our instance’s methods. These let you define the name of the method and its options, including the path, method, headers, query parameters (that are then turned into a query string), and payload.

You will notice that some values are used inside the {{mustache}} notation. This allows us to pass certain arguments to the defined methods. You can insert these into query parameters, payloads, or paths.

Additionally, because it’s a builder pattern, once your wrapper definition is ready, you need to call the build() method at the end.

Let’s look at some examples now.

We call the trelloFactory() function followed by one of the defined methods. Parameters are passed as objects in key-value pairs. The key is the template name in the mustache, and the value is the value you want to replace it with.

Now, let’s take a look at some other examples. You may have read my previous article How to Use Service Accounts and OAuth2 in Google Apps Script. One of my examples is using the BigQuery API with a service account. I recommend that you check it out. In fact, under the hood, the BigQuery API uses Bearer authentication. This is how we can wrap it:

Now, let’s execute a query:

At this stage, it’s only a matter of generating a Bearer token from our service account with the OAuth2 library and then passing our SQL query string as a parameter to the runQuery() function. If you’re lost here, be sure to check out the aforementioned article.

Our final example is the ClickSend API, with which we will send an SMS message. ClickSend uses Basic authentication.

We have a single method here, called sendSMS, that takes four parameters: to, from, body, and source.

This is how we can call it:

And that’s it! Now you can wrap any RESTful API in your Google Apps Script projects!

Where to Go From Here

If you’re new to working with APIs, I encourage you to learn more about how they work and, if you know a language such as Node.js, try and build one.

If you’re going to wrap an API, feel free to build a proper SDK and share it on GitHub with the community.

Contribute! Reach out if you believe that you can help make this library better. Also check out the “Contributing” section in the README.

Buy me a coffee — it’s what fuels my code. :)

About the Author

Dmitry Kostyuk is a full-time GAS developer and the Founder of Wurkspaces.dev.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store