APIs
表情包
Introduction
Up until now, building interactivity into the front-end of our web applications has required that we pass some props (in the form of variables, objects or functions) and manage state between and amongst our React components. More importantly, our applications are truly independent and portable — they don’t require an internet connection and they don’t interact with other applications or sources of data.
We’ve used the ReactJS library itself to add more functionality and features to our application, but these are static libraries. You import or npm install
them and they’re available for you to use anywhere, again, without an internet connection.
Is this boring? Not necessarily, but the web is a dynamic place and there are a lot of awesome sources of data out there that you can use to make your applications more interactive, interesting and powerful. One way to do this is to incorporate an API into your app.
What is an API?
First of all, API stands for Application Programming Interface. In general, most websites and web apps hide their source code (the secret recipe) from the world. That said, many sites still want to engage with their community and provide standardized ways for them to interact with their service. Most of the time, this standard way of interacting is via an API, and it’s one of the main catchall terms used to describe how one web app talks to another web app.
Why is it useful?
APIs are extremely useful, partially because similarly to how we leverage libraries such as P5.js or d3.js to create cool sounds or visualizations on the web, we can also leverage APIs provided by other websites, individuals or collectives to do stuff — show a random cat fact, or get a list of all tweets tagged as#catfacts
, then add them to our state
and display them in a realtime list.
The advantage for you as an app developer is that you get a bunch of new, cool features in your application for free, while the API provider gets to promote their platform.
What does an API Look Like?
This depends on the API provider, but most will provide documentation and examples describing how to implement their API in your web application. For example, we’re going to be making a simple React app that returns a random sticker using the Giphy API’s Random Sticker API Endpoint (an endpoint is the URL you pass parameters to).
Most APIs will return to you a JSON object — basically a JavaScript-like object (see the end of the article for an explanation of JSON), or others, like the Giphy API we’ll use in this article will return a JavaScript object. There are not a lot of differences between the two (really only two small ones) but it’s worth talking now about JSON so that when you see it later, you’ll have some idea of what it means and why it’s useful.
Requests and Responses
In general, APIs expect you to send a request
, formatted in a particular way, to a URL that they publish (often called an endpoint
) that accepts requests
. Once they receive an authorized request
in the format they expect, they’ll send you back a response
, normally formatted similarly to a JavaScript object, that you can use in your application (either for logic, or to display). This request — response
pattern is repeated throughout the internet, and is a helpful way to thinking about what you’ll be doing.
Let’s Build a Sticker Search Engine
Go here to see what we’re going to build: https://stickerpicker.herokuapp.com/
For this example, I thought I would try and solve a problem that I have. In China, there is a popular messaging app called WeChat. One fun feature it has is the ability to send animated stickers to people in lieu of text or audio. It’s quite popular (about 90% of the internet-using population of China uses it for most of their daily online communications).
In WeChat chat threads, once you see a sticker you like, you can long-press and choose to save it to your collection. If you want to add a sticker you made yourself, it can be a little bit difficult, and involves a lot of steps. That’s what I’d like us to be able to solve.
How?
For this app, I want us to be able to use the GIPHY API to search for stickers that match a term
we enter in an input field, furthermore, I want to be able to only show those stickers that are small in size (WeChat limits the file size of stickers to 1MB)
The best part is that this application will be able to be used on my mobile, so I can easily choose to forward the stickers it finds directly to my friends. I am a simple man who likes simple apps — a user can simply enter a search term in an input box, press a button and receive a list of matching stickers. That’s it.
Step 1: Clone the git repository from our prior lab:
$ git clone https://github.com/clg236/stickerpicker.git
$ cd stickerpicker
$ npm install
$ npm start
Our Layout
Since this is a React app, let’s start by identifying our component relationship and what each part will do. We can start by building off ofthe prior lab’s project, with a few edits of course.
What do we need to change?
- Instead of adding whatever the user types in the input component to our App’s state and displaying a new entry in our App, we want to take the input and use it as the search term input to the Giphy API.
- We’ll need to rename the button text
- We’ll need to do some additional styling in CSS
Handling the Search Box
First piece of functionality we are going to change is how to handle passing the search term from the InputComponent
to the App
component as well as updating the the state we’ll need in order to pass some meaningful terms to the Giphy API.
Recall from the last lab, we have an App
component that renders an InputComp
and passes it a prop called submit
.
Here’s the Input Component:
Let’s break this InputComp down.
- Lines 2–6: This is our constructor, it’s called once every time the component is added to our
App
(which is one time in this case, because it’s our input field). It creates astate
in our app with a single string variable calledcurrentWord
- Lines 9–13: This is our
updateWord
function, it takes in an argument called (event
) and then calles the setState function to set ourcurrentWord
variable in the state to whatever is passed in fromevent.target.value
(which is normally whatever is typed in a field). - Lines 15–17: This function, when called executes another function that’s passed down in the form of a prop called
submit
, and passes that function the value of ourcurrentWord
variable in the state. - Lines 19–26: This part of the component renders two HTML elements. An input, that when changed calls the
updateWord
function on line 9, and a button, that when clicked calls thesubmitWord
function on line 15. - Our
App
component renders ourInput
component with a prop ofsubmit
, passing theInputComp
its ownfindGifs
function as an argument.
What should we change?
only one thing!
Remember how we mentioned to you that the pattern we were using last week was very flexible, well the only one thing that we need to change here, is to add a bit of code to stop our page from reloading. This is called preventDefault, which is a method that will prevent the default action of refreshing our page when we hit the submit button from occurring:
In order to use this new function, we need to make a change to our submitWord
function to take in an event, and then to call the preventDefault()
function on that event:
submitWord = (event) => {
this.props.submit(this.state.currentWord);
event.preventDefault();
}
That’s it for the input component. Now, let’s make a change in our App component to handle the search term that people will enter.
Working with the API
Now that we have our search input working (at least it’s updating state and passing data back to our App
’s addWord
method, let’s make the changes we need to make to begin passing (our request) and receiving (Giphy’s response) data from our API.
To use the Giphy API, we’re going to need a way to send and receive data within our React app. There are a ton of ways to handle making HTTP requests, all of them use a library or built-in functions. Here are few popular ones:
- The Browser’s Fetch API: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
- Axios: https://github.com/axios/axios
- SuperAgent: https://github.com/visionmedia/superagent
- JQuery AJAX: https://api.jquery.com/jQuery.ajax/
I’m choosing SuperAgent for this example, mainly because after trying all of them, I found it to be the easiest to use and understand. To install SuperAgent in your React project, simply run the following within your project’s directory:
$ cd yourprojectdirectory
$ npm install --save superagent
Now, to use Super Agent we need to add it to our React project. In your App component, simply import it!
import React, {Component} from 'react';
import './App.css';
import Headline from './Headline.js';
import InputComp from './InputComp.js';
import superAgent from 'superagent'; //here's how we can import
Now, you’ll be able to access functions inside of this package by simply writing: superAgent.functionnamehere
for example, we’ll use the get
function by calling superAgent.get()
to fetch data from the Giphy url and do something with the response.
Review the GIPHY API
Before we can write the code that will help us populate our sticker search, we’ll need to review the Giphy API to see what’s possible and appropriate.
It looks like we’ll need 3 things in order to make this work:
- an API key (which means we’ll need to ‘create an app’ on their portal)
- an endpoint (looks like their sticker search endpoint at
/v1/stickers/search
looks perfect. - the terms we want to search for (comes from our Input component)
Create an Account
If you don’t already have one, create an account at https://giphy.com/join/:step?
Create an App
After logging in, create an App at https://developers.giphy.com/dashboard/
Give your app a name and description, this is useful if you want to apply to Giphy to make your application into a production application (which means you can make more requests per second, etc…)
Next, copy down your API Key (you’ll need it in order to make requests to the Giphy API)
Now that we have our API key, we can move on to implementing the search function in our sticker API
Implementing Search
Before we can implement the search, we need to understand the structure of the data that we’ll be receiving back from the API
Giphy provides a nice API explorer for us to see which of their APIs might be suitable for our app, as well as to see what the data structure that results from our queries:
This is an excellent way to understand behaviors and data before wasting too much time console.log
’ing.
The Request URL
is especially useful, as it tells us exactly how to format our API request.
https://api.giphy.com/v1/stickers/search?api_key=&q=bunnies&limit=25&offset=0&rating=G&lang=en
Refactoring Our Input
In the prior lab, we had an input component that updated state and then submitted state up to our App component when we pressed the Submit button. In this new app, we want to so something similar, we want to keep track of what the user is typing, then when they press the Give Gif button, we want to pass whatever is typed into the Input Component up to our App.
Let’s take a look at the addWord function in the prior lab:
addWord = (newWord) => {
let newWords = this.state.words.concat(newWord); // overwrite the current words in our state
// with the updated one
this.setState({
words: newWords
})
}
This function is taking in an argument from our Input Component (newWord
) and then concatenating it into our existing words
array in our state. We should change this to reflect the new functionality. Which is:
- We want to receive our search term
- Then pass the search term along with our API key to the Giphy URL endpoint.
- Then receive data back from the Giphy API
- Then add the URL for a Gif to our state.
Let’s make a few changes to our function to accommodate these new features. See if you can do these before moving on:
Challenge 1:
- Rename the function to findGifs and change the argument to term
- Create a const string variable called
url
that holds our URL and uses theterm
argument you’re passing instead of a regular search term (hint: check out the Giphy API Explorer to see how to format the URL. - Now use Super Agent to request data from the API and log the result to the console. This part is tough, but check out their documentation here: https://visionmedia.github.io/superagent/
hint: for step 3, simply try to console.log the result from the Super Agent’s get function, you don’t need to worry about .catch.
Run your app and type something in, then check your console. Do you see any response?
solution is below
.
.
.
.
.
.
.
.
.
.
.
.
.
are you sure you want to peak?
.
.
.
.
.
.
did you try copy-paste from the SuperAgent Request Basics?
.
.
.
.
.
okay!
.
Solution:
Here’s the new findGifs function.
findGifs = (term) => {
const url = `//api.giphy.com/v1/stickers/search q=${term}&api_key=SWq0akBClfv0noc1kvCvN8bCBGAKqAZG&limit=10`; //use superagent
superAgent.get(url).then(res => {
console.log(res);
});
}
After importing SuperAgent, we can use their request
object to make an API call within our handleInputSubmit
method, since that's receiving the term
from our Input
Component.
We’re setting a const named url
with all three things we said we needed to make an API call: (1) the endpoint, (2) our API key and (3) our search term.
Finally, we’re making a get
request using SuperAgent to the URL we specified (the Giphy sticker API URL endpoint). You can find out more about how to make requests with SuperAgent in their documentation, but for now, we're going to keep it as simple as possible.
Let’s go ahead and search for something in our Input component and open the console. You should get something back from the Giphy API!
From the console we can see that the request comes back as an Object, and it looks like the meat of the Gif is inside the body
> data
child.
Challenge 2:
- Make a change so that only one of the Gif URLs is returned instead of the entire Object.
- Click on some of the links in your console to make sure they return a valid sticker.
This should about 5 minutes!
Got it? Here’s my version:
findGifs = (term) => {
const url = `//api.giphy.com/v1/stickers/search q=${term}&api_key=SWq0akBClfv0noc1kvCvN8bCBGAKqAZG&limit=10`;//use superagent
superAgent.get(url).then(res => {
console.log(res.body.data[0]);
});
}
Let’s refactor our code to pass the Giphy sticker data into our App components state via this.setState
.
What’s in our app’s state?
constructor(props) {
super(props);
this.state = {
words: ['paradise', 'bicycle', 'jianbing', 'timezone', 'hotdog', 'dog'],
}
}
Obviously this won’t work. Instead, we’re going to want to store whatever comes back from our API call to Giphy in our state, then we can access the Gif image URLs in our map function later.
Challenge 3:
- Create a new state for your App component to hold the gif objects that are returned from the API.
.
.
.
.
.
.
.
.
.
.
.
Solution:
constructor(props) {
super(props);
this.state = {
gifs: [],
}
}
Yup. That’s it. We just need an empty array. Our findGifs
function will fill this up every time it’s called!
Update our findGifs function
Don’t forget that we now want to populate our state with the data returned from our API call.
this.setState({ gifs: res.body.data})
Update our Render Component
The last and final step is to update our render method to display the gif URLs in our state. We can actually keep a lot of our existing map function intact! We just need to make a few changes to dig deeper into the javascript objects in our state that contain the URLs. Let’s think about what needs to change:
- We’ll need to display images instead of just text. We can us the
<IMG>
tag for this - We need to dig down into the object to access the correct URLs for the Gifs that we want (we want the smallest ones in order to make sure we can send them over WeChat, which only accepts 1MB or less)
We’ll need to update the map function to return a <div>
full of gifs instead of the <p>
tags we had previously:
<div className="display">
{this.state.gifs.map((gif) => {
return (
<div className="box">
<img src={gif.images.downsized.url} width="200" height="200" /
</div>
);
})
}
</div>
Advanced Challenge:
- Edit the sticker engine to only return GIFs that are equal to or smaller than 1MB in size (which just so happens to be the limit that WeChat places on sticker sizes…hmmmmmm). Hint, recall the console.log from when we returned a single Gif object from the API for hints.
That’s it! This was a long article, but I hope it was worthwhile and useful, especially for those who would like to integrate APIs into their projects. There are a lot out there:
What is JSON
JSON stands for JavaScript Object Notation. It’s a data format that you should be familiar with already as we’ve been writing its syntax all along, with the only difference being that in JSON, values are enclosed in quotes:
{
"name": "Dog",
"breed": "Labrador Retriever",
"color": "Yellow",
"age": 6
}
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition — December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers.
JSON is built on two structures:
- A collection of name/value pairs.
- An ordered list of values (e.g., an object, an array, boolean, number, string, null).
How is JSON Different from a JavaScript Object
JSON very similar to a JavaScript objects. But there are differences, according to the spec:
“JSON is a text format that facilitates structured data interchange between all programming languages.”
So it’s universal, this has nothing to do with JavaScript other than the name and the fact it was derived from the way JavaScript objects are written.
In terms of the syntax itself, there are a few major differences. First, all names (keys) are represented as strings (i.e. they need to be inside quotes). So the following is not valid JSON:
// not valid JSON, but valid as JS object
{
name: "Bob Ross"
}
If we wanted to make this valid JSON, a simple change:
// not valid JSON, but valid as JS object
{
"name": "Bob Ross",
"occupation": "celebrity painter"
}
According to the specification, a JSON value can only be text, so you can’t pass it a JavaScript function. The following are all valid value types in a JSON object (file):
- Object
- Array
- Number
- String
true
false
null
Last thing, just like JavaScript Objects, a JSON object can also have nested key/value pairs, just be sure to include the “”
syntax:
// This is VALID
{
"species": "Dog",
"breed": "Labrador Retriever",
"age": 6,
"traits": {
"eyeColor": "brown",
"coatColor": "yellow",
"weight": "137lbs"
}
}
How to Handle JSON Returned from APIs
Many APIs, not all, will return (require) a JSON object to (from) your application after you’ve successfully (or unsuccessfully) interacted with it. Your task then is to transform the data you are sending or receiving into one that will be recognized by the API or your application.
JavaScript provides two built-in methods to do this. JSON.parse()
and JSON.stringify()
. Can you guess which is useful for outbound API calls and which one is useful for inbound API data?