Integrating Instagram Basic Display API on a Node.js GraphQL API
Instagram is a photo and video-sharing social networking service launched in 2010. It is currently one of the most used social media applications with more than one billion users. <!--more--> As of October 2019, Facebook, the parent organization of Instagram, released the Instagram basic display API. The API allows users to get profile information, media (photos and videos) of their Instagram accounts. The restriction is that you cannot access the data of another Instagram user.
Goals
In this article, we will integrate the Instagram basic display API on a Node.js GraphQL API.
Prerequisites
To follow along in this article, it is necessary to have:
Overview
- Creating a Facebook developer account
- Creating an app
- Adding an Instagram test user
- Setting up the project
- Installing the necessary dependencies
- Getting the authorization code
- Getting the short lived access token
- Getting the long lived acccess token
- Getting user profile data
- Getting user media data
Creating a Facebook developer account
To create a Facebook developer account, follow the following steps:
- Visit the Facebook developer page and on the right side, click Login.
- In the resulting page, key in your credentials and click Log In.
- After signing in, you will be directed to your dashboard page.
Creating an app
To consume the Instagram API, we will need to have created an app. To do that, we will follow the following steps:
-
From your dashboard page, in the apps section, click the Create App button.
-
In the resulting pop-up, select Consumer and then click Continue.
-
Enter your App Display Name and then click Create App.
-
In the resulting page, in the products section, on the Instagram Basic Display product click Set Up.
-
On the following page, read through the information provided and then click Create App down below. Confirm the name of the app in the resulting pop-up.
-
In the resulting page, we will need to add some valid URLs in the entries provided. For this article, we will use httpstat.us. It's a service for generating HTTP Status Codes based on the nature of the request.
For example, a https://httpstat.us/200 will return a
200
status code.In the form, we will enter the link to yield a
200
status code as follows -
In the App Review for Instagram Basic Display section, hit Add to Submission for instagram_graph_user_profile_and_instagram_graph_user_media to be able to access Instagram's user profile and media.
-
Hit the
Save Changes
button below.
Adding an Instagram test user
To use the Instagram basic display API in development, we will have to add a test user. To do so, we will follow the following steps:
-
In the left sidebar, click on Roles and then Roles.
-
Scroll to the bottom, and hit the Add Instagram Testers button.
-
In the resulting pop-up, enter the username of the Instagram account you are going to use throughout the article. Make sure it's an Instagram account you can log in to because you will be required to accept the request sent.
-
On hitting Submit, the account will appear in the section with a
Pending
text attached to it. The owner of the Instagram account is supposed to accept it first to be complete. -
Log in to that particular Instagram account you have entered its username.
-
In the settings section, find Apps and Websites. In the resulting section, click on the TESTER INVITES tab. Your recently created app should be listed there as follows:
Click on the Accept button.
-
Hurray!!, your Instagram app is now configured, it's time to set up our project.
Setting up the project
To set up the project, we will follow the following steps:
-
Create a folder instagram-display-api-graphql.
-
Open VS Code to that specific folder.
-
Open the VS Code terminal by pressing ctrl+shift+`.
-
In the resulting terminal, type in :
npm init --y
to initialize the application with default settings.
-
Structurize the application as follows:
Installing the necessary dependencies
To start, we will install all the dependencies that we will need throughout the application.
The dependencies are as follows:
- express: Minimalist web application framework for Node.js.
- apollo-server-express: For providing an Express.js based integration for GraphQL server.
- axios: For sending requests and receiving responses from Instagram API.
- cors: For controlling client access.
- dotenv: For accessing the environmental variables from the
.env
file. - graphql: For providing a query language for our API.
- request: For sending requests that require form data to the Instagram API and receiving appropriate responses.
- nodemon: For automatically restarting our Node.js server whenever we make a change.
We install them as follows:
npm i express apollo-server-express axios cors dotenv graphql request nodemon
Getting the authorization code
For us to use the API, the first step is to get the authorization code. It provides an authentication mechanism to the Instagram API.
To get an authorization code, we follow the following steps:
-
Start with getting our application's credentials. To do this, go to your dashboard page. On the left sidebar to the bottom, click on Instagram Basic Display and Basic Display.
-
In the resulting page, scroll down to find the Instagram App ID, and Instagram App Secret, copy them and paste them appropriately to the
.env
file in the root of the project. Your.env
file should be similar to: -
After setting your credentials, its time we set up the server in the
src/index.js
file by adding the following:const express = require("express"); const cors = require("cors"); require("dotenv").config(); const PORT = process.env.PORT || 4000; // initialize express const app = express(); // express configs app.use(cors()); // cors set up app.use(express.json()); // json format app.use(express.urlencoded({ extended: false })); // data parsing // routes // getting-authorization-code app.get("/get-auth-code", (req, res, next) => { return res.send( `<a href='https://api.instagram.com/oauth/authorize?client_id=${process.env.INSTAGRAM_APP_ID}&redirect_uri=${process.env.REDIRECT_URI}&scope=user_media,user_profile&response_type=code'> Connect to Instagram </a>` ); }); // start server on the PORT. app.listen(PORT, () => console.log(`Server started on port: ${PORT}`));
From above, we are:
- Requiring the packages we need.
- Setting the port for our application to run to.
- Initializing express.
- Adding some configuration to the express app.
- Setting up the route for getting an authorization code.
- Starting the server by listening to the specified port.
-
Edit the
scripts
section ofpackage.json
as follows: -
In your recently opened VsCode terminal, run the following command:
npm run dev
This will start the server on PORT 4000.
-
From your web browser, open http:localhost:4000/get-auth-code. In there, click the link Connect to Instagram to connect to your Instagram account.
-
In the resulting pop-up, click Allow, after which you will be redirected to a different page. On this new page, in the URL section, we have a code parameter which is as below:
Copy the entire code up to where we have
#
. Don't include the#
. Paste the code to your.env
file. Your.env
file should now be similar to:
Getting the short-lived access token
After getting the authorization code, we can now get the short-lived access token. It is defined as short-lived because it is only valid for one hour. An access token is usually sent along with every request you make to the API for authentication.
To get the short-lived access token, we will follow the following steps:
- Introduce
Apollo server
to our app. To do this, we will edit oursrc/index.js
as follows:-
Require it as a package at the top:
const { ApolloServer } = require("apollo-server-express");
-
Require the type definitions and the resolvers:
const typeDefs = require("./schema"); const resolvers = require("./resolvers");
-
After the express configurations, we configure
Apollo server
as follows:const server = new ApolloServer({ typeDefs, resolvers }); server.start().then(() => { return server.applyMiddleware({ app }); });
The above configuration takes in the typeDefs which is the schema and the resolvers. The server is started and then the express instance, app is applied as a middleware.
-
- In the
schema/index.js
file, we set up the schema:-
Start by requiring
gql
fromapollo-server-express
. It will add some highlights to the queries we will write:const { gql } = require("apollo-server-express");
-
We define the schema of the response sent by the API when getting a short lived access token:
const AccessTokenResponse = gql` type AccessTokenResponse { access_token: String! user_id: Float! } `;
The response will consist of an
access_token
which is aString
and auser_id
which is aFloat
. -
We then define the overall
Query
for the schema. It will hold all the methods and their respective response schemas:const Query = gql` type Query { getShortLivedAccessToken: AccessTokenResponse } `;
From above, the method
getShortLivedAccessToken
will return a response with the schemaAccessTokenResponse
. -
We then export the
Query
, andAccessTokenResponse
:module.exports = [AccessTokenResponse, Query];
We are exporting it as an array because, throughout the article, we will define other schemas.
-
- After the definition in the schema, we now work out the resolvers. To do this, proceed to
resolvers/Instagram.js
:-
Start by requiring the necessary packages:
const { UserInputError } = require("apollo-server-express"); const { get } = require("axios").default; const { post } = require("request"); const { promisify } = require("util"); require("dotenv").config();
-
UserInputError: Will be used for sending an error to the user.
-
get: For sending GET request to the Instagram API.
-
post: For sending POST request to Instagram API with
Form Data
. -
promisify: For turning a callback to a promise-based function.
-
dotenv: For loading environmental variables.
-
Promisify the
post
function from request:const postAsync = promisify(post);
-
Initialize the function to get the
access token
:async function getShortLivedAccessToken() { // sending the request. let { body, statusCode } = await postAsync({ url: `https://api.instagram.com/oauth/access_token `, formData: { client_id: process.env.INSTAGRAM_APP_ID, client_secret: process.env.INSTAGRAM_APP_SECRET, redirect_uri: "https://httpstat.us/200", code: process.env.AUTHORIZATION_CODE, grant_type: "authorization_code", }, headers: { "content-type": "multipart/form-data", host: "api.instagram.com", }, }); // getting the response. let response = JSON.parse(body); // checking the status code for error. if (statusCode !== 200) { let error_message = response.error_message; // if error exists, sending the error. return new UserInputError(error_message); } // if no error exists, returning the response. return response; }
-
From above, we are:
-
Sending the request to the API.
-
Getting the response from the API.
-
Checking the status code of the response to detect an error.
-
If an error exists, sending the error.
-
If an error does not exist, sending the response.
-
Export the function from the file:
module.exports = { getShortLivedAccessToken, };
-
After declaring the function in the
resolvers/Instagram.js
file, We also need to make it known in theresolvers/index.js
file. From this file, it's where we will export the generalQuery
object we declared in theschema/index.js
file. Therefore, for all the functions we will declare in theresolvers/Instagram.js
file, we have to make each of them known here. For now, we will add the following:// get the defined function(s) const { getShortLivedAccessToken } = require("./instagram"); // general query object const Query = { Query: { getShortLivedAccessToken: () => getShortLivedAccessToken(), }, }; // export the Query object module.exports = Query;
-
After adding the getShortLivedAccessToken to the
resolvers/index.js
, we are ready to test the functionality. To do so, we will follow the following steps: -
Start the development server if not yet started by running:
npm run dev
-
From your browser, visit:
http://localhost:4000/graphql
. -
In the space provided on the left side, write the following query:
query GetShortToken{ getShortLivedAccessToken{ access_token user_id } }
-
The above query calls the getShortLivedAccessToken
method and then extracts the access_token
and user_id
from the response of the method.
-
Hit the play button aligned at the center between the left pane and the right pane.
-
Observe the results. If you get an error of invalid
client secret
andcode
or that yourauthorization code
has expired, revisit the previous step and restart the server manually by pressingctrl + c
to stop it and thennpm run dev
to start it. Else if you got no error, your response on the right side should be similar to:
- Copy the access token value from the response to your
.env
file. Your.env
file now should be similar to:
Getting the long-lived access token
Since short-lived access tokens have a limited time-space, it is vital to generate an access token with a longer time-space. They are called long-lived access tokens. They are valid for 60 days.
To get a long-lived access token, we will follow the following steps:
-
In the
schema/index.js
, we add its schema definition as follows:const LongLivedAccessToken = gql` type LongLivedAccessToken { access_token: String! token_type: String! expires_in: Float! } `;
The above schema defines that the response of getting a long-lived access token will comprise of: an access token, a token type, and an expires in value.
-
Add the method of getting a long-lived access token to the query object:
const Query = gql` type Query { getShortLivedAccessToken: AccessTokenResponse getLongLivedAccessToken: LongLivedAccessToken } `;
-
Add the schema definition to the exported array:
module.exports = [AccessTokenResponse, LongLivedAccessToken, Query];
-
After exporting the schema, we proceed to the
resolvers/Instagram.js
and define a function to get the long-lived access token from the API:// getting a long lived access token async function getLongLivedAccessToken() { let response; try { // send a request to the API response = await get("https://graph.instagram.com/access_token", { params: { grant_type: "ig_exchange_token", client_secret: process.env.INSTAGRAM_APP_SECRET, access_token: process.env.SHORT_LIVED_AT, }, headers: { host: "graph.instagram.com", }, }); } catch (error) { // If an error occurs, return it. return new UserInputError(error); } // If no error, get the response and return it. response = response["data"]; return response; }
From above, we are:
-
Sending a GET request to the API.
-
Listening for any error that might occur and returning it.
-
If no error occurs, we are sending back the response.
-
Add the function to the exported object:
module.exports = {
getShortLivedAccessToken,
getLongLivedAccessToken,
};
-
In the
resolvers/index.js
file, import the function:const { getShortLivedAccessToken, getLongLivedAccessToken, } = require("./instagram");
-
Add it to the Query object:
const Query = { Query: { getShortLivedAccessToken: () => getShortLivedAccessToken(), getLongLivedAccessToken: () => getLongLivedAccessToken(), }, };
-
-
After that, we can test the functionality now. To do that:
-
Ensure that your development server is up and running.
-
In your browser, In the same tab we were previously, click the
+
to open another pane. -
In the resulting pane, on the left side, add the following query:
query GetLongLivedToken {
getLongLivedAccessToken{
access_token
token_type
expires_in
}
}
The above query calls the getLongLivedAccessToken method and extracts the _access_token, token_type and expires_in.
- Click the play button in the middle and observe the results.
- If your short-lived access token is expired, go to the previous step. Else, your response should be similar to:
- Copy your access token from the response to your
.env
file. Your.env
file should be similar to:
Getting user profile data
On an Instagram account, we can be able to get the profile data of that specific account. The profile data here involves the account type, id, media count, and username.
To implement the above functionality, we will follow the following steps:
-
Add its schema definition in
schema/index.js
:const ProfileData = gql` type ProfileData { account_type: String! id: String! media_count: Int! username: String! } `;
From above, we are setting our response to contain the account_type, id, media_count, and username since it is what we want to get.
-
Add the method of getting profile data to the Query object:
const Query = gql` type Query { getShortLivedAccessToken: AccessTokenResponse getLongLivedAccessToken: LongLivedAccessToken getProfileData: ProfileData } `;
-
Add the schema defined above to the exported array:
module.exports = [ AccessTokenResponse, LongLivedAccessToken, ProfileData, Query, ];
-
After defining the schema, we set up the function for getting the profile data from the API in the
resolvers/Instagram.js
file as follows:// getting profile data async function getProfileData() { let response; // send request to the API try { response = await get("https://graph.instagram.com/me", { params: { fields: "id,username,media_count,account_type", access_token: process.env.LONG_LIVED_AT, }, headers: { host: "graph.instagram.com", }, }); } catch (error) { // catch and return the error return new UserInputError(error); } // get the data and return it. response = response["data"]; return response; }
From above, we are:
-
Sending request to the API.
-
Listening to any error that occurs. If it does, we return the error.
-
Else if, no error, we return the data sent.
-
Add the above function to the exported object:
module.exports = { getShortLivedAccessToken, getLongLivedAccessToken, getProfileData, };
-
In the
resolvers/index.js
, import the function:
const {
getShortLivedAccessToken,
getLongLivedAccessToken,
getProfileData,
} = require("./instagram");
- Add the function to the Query object:
const Query = {
Query: {
getShortLivedAccessToken: () => getShortLivedAccessToken(),
getLongLivedAccessToken: () => getLongLivedAccessToken(),
getProfileData: () => getProfileData(),
},
};
-
Test the functionality:
-
Ensure that the development server is up and running.
-
In the same browser tab as previously, click the
+
to open a separate pane. On the left side of the pane, add the following query:
query getProfileData {
getProfileData{
account_type
id
media_count
username
}
}
From above, we are calling the getProfileData function and extracting the account_type,id,media_count,username.
- Click the play button in the center, If everything was okay, you should receive a response similar to:
In case of an error, revisit the steps.
Getting user media data
Media data is the data that the user has posted in his or her Instagram account. The media_count value from the previous process is the count of the media data that the user has posted.
The media here can be a photo, a video, or a carousel album. To set up the functionality of getting a user's media, we will follow the following steps:
- Start by setting up the schema for the data we will receive:
const MediaData = gql`
scalar Date
type MediaData {
caption: String
id: String
media_type: String
media_url: String
permalink: String
thumbnail_url: String
timestamp: Date
username: String
}
`;
From above, we are setting that the media data will comprise of caption, id,media_type, media_url, permalink, thumbnail_url, timestamp, username.
- Add the method of getting a user's media data to the Query object:
const Query = gql`
type Query {
getShortLivedAccessToken: AccessTokenResponse
getLongLivedAccessToken: LongLivedAccessToken
getProfileData: ProfileData
getUserMediaData: [MediaData]
}
`;
From above, we are adding the getUserMediaData method, which will return an array of data with type MediaData.
- Add the MediaData type to the exported array:
module.exports = [
AccessTokenResponse,
LongLivedAccessToken,
ProfileData,
MediaData,
Query,
];
- In the
resolvers/Instagram.js
, we set up a function for getting media data as follows:
// getting media data
async function getUserMediaData() {
let response;
// sending request to API
try {
response = await get("https://graph.instagram.com/me/media", {
params: {
fields:
"id,caption,media_url,media_type,permalink,thumbnail_url,timestamp,username",
access_token: process.env.LONG_LIVED_AT,
},
headers: {
host: "graph.instagram.com",
},
});
} catch (error) {
// Catching an error, and returning it.
return new UserInputError(error);
}
// If no error, returning the response.
response = response["data"];
return response.data;
}
From above, we are:
-
Sending request to the API.
-
Listening to any error, catching it, and returning it.
-
If there is no error, returning the response from the API.
-
Add the function to the exported object:
module.exports = {
getShortLivedAccessToken,
getLongLivedAccessToken,
getProfileData,
getUserMediaData,
};
-
In the
resolvers/index.js
: -
Import the function:
const { getShortLivedAccessToken, getLongLivedAccessToken, getProfileData, getUserMediaData, } = require("./instagram");
-
Add the function to the Query object:
const Query = { Query: { getShortLivedAccessToken: () => getShortLivedAccessToken(), getLongLivedAccessToken: () => getLongLivedAccessToken(), getProfileData: () => getProfileData(), getUserMediaData: () => getUserMediaData(), }, };
-
To test the functionality:
-
Ensure that the development server is up and running.
-
In your browser, in the previous tab, hit the
+
to open a separate tab. In the new tab, on the left pane, add the following query:query getMediaData{ getUserMediaData{ caption id media_type media_url permalink thumbnail_url timestamp username } }
The following query calls the getUserMediaData function and extracts the caption, id, media_type, media_url, permalink, thumbnail_url, timestamp_, username_ from the response.
- Hit the play button in the center. Observe the response on the right side. If you encounter any error, revisit the steps. Else if you have some media posted to that Instagram account, your response should be similar to:
If you don't have some media data posted, your response will be an empty array.
Summary
In this article, we have managed to integrate the Instagram basic display API on a Node.js GraphQL API by following the below steps:
- Creating a Facebook developer account
- Creating an app
- Adding an Instagram test user
- Setting up the project
- Installing the necessary dependencies
- Getting the authorization code
- Getting the short lived access token
- Getting the long lived acccess token
- Getting user media data
- Getting user profile data
References
To gain more insights on the tools and technologies involved in this article, it is advisable to go through the following resources:
Conclusion
With the Instagram basic display API, you can be able to integrate an Instagram account into various applications to provide various automation schemes.
In the finalized code, which can be accessed from this GitHub repository, there is added functionality concerning the following use cases:
- Getting information on a single media.
- Getting information on an album.
- Refreshing a long-lived access token.
Be sure to check it out.
Happy hacking!!
Peer Review Contributions by: Adrian Murage