Somebody who shall remain nameless (@SantiagoPerez - oops) asked me if I could put together a tutorial on working with API’s, so here it is. It’s my first tutorial, so please be gentle
Whilst there is an app at the end of this, the app is not the main focus of the tutorial, so don’t expect to be impressed by that. The main focus is to give a gentle (?) introduction to Apps Script, and demonstrate how it can be used to interface with an API endpoint. I’ve tried to break down and explain the Apps Script bits as much as possible, so even if you’ve never used Apps Script before (@Robert_Petitto), hopefully you can follow along
Right, let’s get started…
Part 1 - The API
For the demo, I’ve picked Deck of Cards. This API, oddly enough, allows you to play around with a deck of cards. It has several methods, but I’ll just focus on 3 of them:
- Get a new deck of cards
- Shuffle the deck
- Draw some cards
Lets have a look at each of them in turn…
A Brand New Deck: https://deckofcardsapi.com/api/deck/new/
Which returns:
{
"success": true,
"deck_id": "3p40paa87x90",
"shuffled": false,
"remaining": 52
}
Okay, so we get a deck_id, with 52 cards, and it hasn’t been shuffled.
Next, Shuffle the Deck: https://deckofcardsapi.com/api/deck/<<deck_id>>/shuffle/
With this one, we have to provide the deck_id as a part of the request. It returns:
{
"success": true,
"deck_id": "3p40paa87x90",
"shuffled": true,
"remaining": 52
}
This time shuffled is true, and it also tells us how many cards are left in the deck (which won’t necessarily be 52)
And finally, draw some cards: https://deckofcardsapi.com/api/deck/<<deck_id>>/draw/?count=2
This time, we need to provide the deck_id, and tell it how many cards we want.
The response:
{
"success": true,
"cards": [
{
"image": "https://deckofcardsapi.com/static/img/KH.png",
"value": "KING",
"suit": "HEARTS",
"code": "KH"
},
{
"image": "https://deckofcardsapi.com/static/img/8C.png",
"value": "8",
"suit": "CLUBS",
"code": "8C"
}
],
"deck_id":"3p40paa87x90",
"remaining": 50
}
This is a little more complex, but basically what we have is an object that contains an array of cards, and each card has a series of key/value pairs that define its attributes. When it comes to decoding that, let’s say we want to get the URL of the image for the first card, we might do something like var image = json.cards[0].image
.
cards[0]
is referencing the first element in the cards array. But I’m getting ahead of myself. Let’s setup a sheet…
Part 2 - The Sheet
I decided to use two sheets, one for the Deck and one for the Cards. Why? No particular reason other than it seemed like a good idea at the time
The Deck Sheet:
Columns A/B/C will be used to store the responses from the API calls, and columns D & E will be used to trigger the API calls (more on that later).
The Cards Sheet:
Columns A-H will be used to store my card details. I’m going to draw 2 cards each time, so I have 2 of each for Code, Value, Suit & Image. And I’ve set them up so that Glide will treat them as array columns. Column I will be used to trigger an API call to fetch 2 new cards.
I’ve also created some Named Ranges in my sheet for some of the values I’ll need to reference in my script. This just makes life a little bit easier, and stuff is less likely to break if you start adding or removing columns/rows later on.
Right, time for the fun stuff
Part 3 - Getting down & dirty with the Apps Script
First thing we need to do is find the Script Editor. It can be found via the Tools menu option:
Okay, let’s write some code. I’m using screenshots here as it’s easier to reference line numbers in the code, but I’ll provide the full code at the end so it can be copied/pasted.
I’m going to be making lots of API calls, so the first thing I’ll do is write a helper function just for that purpose. This will save me having to write the same few lines of code over and over (and over) again.
Line 1 defines the function name, and tells it to expect one parameter (url) when it is called.
Line 2 uses the URLFetchApp method to go get whatever is at the end of that URL
Line 3 extracts the contents of the response, encoded as a string
Line 4 uses the JSON.parse() method to convert that string into a javascript object
Line 5 sends the result back to where ever the function was called from
Simple, eh?
Okay, let’s get a deck of cards:
Line 9 defines the URL we need to call.
Line 10 uses the helper function to call the API and get the data, and we store the response in a variable called deck
Line 11 is deliberately left blank
Line 12 gets a reference to the active spreadsheet…
Line 13 gets a reference to the ‘Deck’ sheet
Line 14 writes the data to the sheet… maybe I need to break that down a bit…
sheet.getRange(2,1,1,5)
- here I’m defining a range of cells that I want to write my data to. Essentially that translates to “gimme a two dimensional range of cells, starting at row 2, column 1, that is 1 row high and 5 columns wide”
setValues([[deck.deck_id, deck.remaining, deck.shuffled, '', '']]);
- and here I’m writing the data into that range.
getRange()
returns an array of arrays, so I need to feed the same thing back to it.
One thing to note in the above is that the last two values in the array that I’m writing to the sheet are both empty ('',''
). This is essentially reseting the trigger cells.
Clear as mud?
Time to shuffle the deck!!
Line 19 - here I make use of one of the named ranges that I created earlier to get the value of my deck_id
Line 20 - construct the URL, using the deck_id
Line 21 - intentionally blank
Line 22 - go get the data
Line 23 - get a reference to my Deck sheet
Line 24 - write the data back to the sheet
The only thing that’s really changed here is that the deck is now shuffled (shuffled.shuffled) hmm, tautology?
Now we can get some cards!
Lines 29-31 - get the deck_id, construct the URL, and fetch the data
Line 32 - looks innocent enough, but probably worth spending a few lines on. Let’s go back and look at the API response:
After line 31, that entire JSON response is stored in the variable I called, er, response. If I just want the array of cards from that object (which I do), then I need to dig into it a bit. And the way I do that is by using var cards = response.cards
. Once I have that, I can use the cards variable to easily reference the attributes of each card (and save some typing). And that’s what goes on at lines 35-41 - where I’m writing the card data into the sheet.
And now we need some triggers…
Part 4 - Let's get Trigger Happy
Triggers are like little watch dogs, but even better, in that they will do stuff when you’re not around. Sometimes bad stuff, but mostly good - hopefully. Well I guess that watch dogs also do stuff when you’re not around, but more often than not it’s stuff that you didn’t actually want them to do. ie. bad stuff.
Earlier on, I defined some named ranges. Here is where I’m going to use them. The idea is that I’m going to make a trigger and tell it to keep an eye on certain cells (my named ranges), and if something changes then BOOM!!, spring into action. Here goes…
Line 1 - the name of the function isn’t important, but I’m going to use this as an ON CHANGE
trigger, so I call it on_sheet_change
. Keeps things nice and tidy
Lines 3-4 - here I’m checking the value in my NewDeck
named range (which is just a single cell), and if it’s not empty, then I’ll go and get a new deck of cards by calling my get_new_deck
function
Line 5 - Return! Go back! Do not pass Go!
Lines 7-10 - same thing, except shuffle the deck
Lines 11-14 - once more, in case we need some new cards.
Activating the Trigger:
Look for the little clock icon in the Script Editor sidebar:
Click on the “Add Trigger” button (bottom right):
And add your trigger:
The two important things are the name of the function that should be called, and the event type - which needs to be “On change”
Save that, and we’re ready to rock and roll!!
Part 5 - The Code
Here is all the code that was used…
function get_api_response(url) {
var response = UrlFetchApp.fetch(url);
var json = response.getContentText();
var data = JSON.parse(json);
return data;
}
function get_new_deck() {
var url = 'https://deckofcardsapi.com/api/deck/new/shuffle';
var deck = get_api_response(url);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Deck');
sheet.getRange(2,1,1,5).setValues([[deck.deck_id, deck.remaining, deck.shuffled, '', '']]);
}
function shuffle_deck() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var deck_id = ss.getRangeByName('DeckID').getValue();
var url = 'https://deckofcardsapi.com/api/deck/' + deck_id + '/shuffle/';
var shuffled = get_api_response(url);
var sheet = ss.getSheetByName('Deck');
sheet.getRange(2,1,1,5).setValues([[shuffled.deck_id, shuffled.remaining, shuffled.shuffled, '', '']]);
}
function draw_cards() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var deck_id = ss.getRangeByName('DeckID').getValue();
var url = 'https://deckofcardsapi.com/api/deck/' + deck_id + '/draw/?count=2';
var response = get_api_response(url);
var cards = response.cards;
var sheet = ss.getSheetByName('Cards');
sheet.getRange(2,1,1,9).setValues([[
cards[0].code, cards[1].code,
cards[0].value, cards[1].value,
cards[0].suit, cards[1].suit,
cards[0].image, cards[1].image,
''
]]);
ss.getSheetByName('Deck').getRange(2,2).setValue(response.remaining);
}
function on_sheet_change() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
if (ss.getRangeByName('NewDeck').getValue() != '') {
get_new_deck();
return;
}
else if (ss.getRangeByName('Shuffle').getValue() != '') {
shuffle_deck();
return;
}
else if (ss.getRangeByName('Draw').getValue() != '') {
draw_cards();
return;
}
}
Part 6 - Acceptance Testing
So, the idea is that if we’ve done all this right we now have 3 “special” cells in our sheets, and if anything changes in any of those 3 cells then magic stuff will happen.
Here it is again with those three cells highlighted for easy reference
The Deck Sheet
The Cards Sheet
Let’s see if it actually works. Time for a short video interlude.
Part 7 - The App (of sorts)
Not much to see here, except a link and a few screenshots.
It kinda works, but it’s clunky and slow because of the sync delay between Glide and the sheets.
But making a super slick app wasn’t the point, was it - Santiago?
It’s copyable, so feel free to grab it and play around.
Part 8 - The End.
Finally, a couple of useful references if anyone is silly enough to want to learn more:
Phew!
That ended up being a bit longer than I originally intended. If you made it this far, you deserve a medal.
Anyway, I hope that this has helped to in some way de-mystify things a bit. If not, at least it’s kept me out of trouble for a couple of hours
Now, go back to building codeless apps!!