Meta Graph API, Resumable Upload — Using NodeJS
In this article, we’ll dive into the world of the Meta Graph API’s Resumable Upload API and explore how to implement it seamlessly using the power of Node.js.
Before starting, If you don’t know about the upload process and steps then I would recommend you refer to the Resumable Upload API documentation.
Prerequisites
First and foremost we need to prepare a few things to make our program successful,
- App ID — Find in your meta developer app
- Access Token — The app user’s User Access Token
- API Version — The Graph API version, find in your meta developer app
Create a Project
Create a directory, execute the first command, and set up the package.json file.
npm init
Install Dependencies
We are gonna use a few specific dependencies to meet our requirements,
- Multer — To upload media from the client side
- Got — To send an HTTP request, call graph API
- Dotenv — To access environment variables
- Express — We will use the framework
The dependencies are installed with specific versions to avoid conflicts,
npm i express@4.18.2
npm i dotenv@16.3.1
npm i multer@1.4.5-lts.1
npm i got@11.8.5
Setup Environment
Let’s get started and define the environment variables to use in our functions securely, update the details with yours, and set these variables in the .env file.
META_API_URI=https://graph.facebook.com/v14.0
META_APP_ID=xxxxxxx
META_ACCESS_TOKEN=xxxxxxx
PORT=3000
Core Construction
Let’s initiate the required dependencies and put our main structure of the API in the index.js file, You have to add the 3 functions as I have added comments in the below code from our further discussion in this article.
We have created an API, that can access through the uploadMedia path, but ignore it now, just follow the further discussion we will execute our API in the last step.
// DEPENDENCIES
require('dotenv').config();
const express = require('express');
const app = express();
app.use(express.json({ limit: "100mb" }));
app.use(express.urlencoded({ limit: "100mb", extended: true }));
const got = require("got");
const multer = require("multer");
// CREATE MEMORY STORAGE MULTER'S OBJECT
const upload = multer({ storage: multer.memoryStorage() });
// STEP 1: Upload API's Create Session Function
// explaind below, put the function here
// STEP 2: Upload API's Initiate Upload Function
// explaind below, put the function here
// STEP 3: Upload Media API's Main Function - Custom API
// explaind below, put the function here
// CONSTRUCTION OF OUR CUSTOM API REQUEST
app.use('/uploadMedia', uploadMedia);
// SERVER LISTEN
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.info(`Listening on port ${port}...`)
}).on("error", (err) => {
console.error(err.message);
});
Upload API’s Create Session Function
Let’s begin with our first step, It will Create a Session for a file and you need to use that returned session id in the next step.
In brief, this function will input the file’s information and return a session id, we will call this function in our main function.
/**
* RESUMABLE UPLOAD - CREATE SESSION
* https://developers.facebook.com/docs/graph-api/guides/upload#step-1--create-a-session
* https://developers.facebook.com/docs/graph-api/reference/whats-app-business-account/message_templates/
* @param {Object} body
* {Number} body.file_length - File's size in bytes
* {String} body.file_name - File's name,
* {String} body.file_type - The file's MIME type
* @return {Object} - Upload session ID.
*/
function createSession(body) {
try {
return got.post([process.env.META_API_URI, process.env.META_APP_ID, "uploads"].join("/"), {
headers: {
'Authorization': 'Bearer ' + process.env.META_ACCESS_TOKEN,
'Accept': '*/*'
},
json: body,
responseType: 'json',
throwHttpErrors: false
});
} catch (error) {
return error;
}
}
Upload API’s Initiate Upload Function
The second step is to initiate file upload for our generated session from the first step.
In brief, this function will initiate the file upload on the provided session and return the uploaded file handler.
The interruption case is not handled here, if you are uploading big files and connection interrupts then you can put your logic and send a GET request to the Graph API host address and append your session ID, as explained here.
/**
* RESUMABLE UPLOAD - INITIATE UPLOAD
* https://developers.facebook.com/docs/graph-api/guides/upload#step-2--initiate-upload
* https://developers.facebook.com/docs/graph-api/reference/whats-app-business-account/message_templates/
* @param {String} sessionId - Upload session ID returned in step 1
* @param {Binary Data} body - The file's buffer data
* @return {Object} - The uploaded file's file handle
*/
function initiateUpload(sessionId, body) {
try {
return got.post([process.env.META_API_URI, sessionId].join("/"), {
headers: {
'Authorization': 'OAuth ' + process.env.META_ACCESS_TOKEN,
'Accept': '*/*',
'file_offset': 0
},
body: body,
responseType: 'json',
throwHttpErrors: false
});
} catch (error) {
return error;
}
}
Upload Media API’s Main Function — Custom API
For the final method to perform our upload process together, I have handled different errors but you can change it your way, just focus on the main logic where we are performing our steps in the success part.
// UPLOAD MEDIA MAIN FUNCTION
function uploadMedia(req, res) {
// UPLOAD FILE FROM 'file' PARAMETER
upload.single('file')(req, res, async (error) => {
// PROBABLY THE ERROR FROM MULTER
if (error instanceof multer.MulterError || error) {
// LOG ERROR, AND RESPONSE
console.error(error);
res.status(400).send({
message: error.message
});
}
// FILE IS MISSING
else if (!req.file) {
// LOG ERROR, AND RESPONSE
res.status(400).send({
message: "File is required!"
});
}
// SUCCESS
else {
// STEP 1: CREATE SESSION
let session = await createSession({
file_length: req.file.size,
file_name: req.file.originalname,
file_type: req.file.mimetype
});
// HAS ERROR
if (session.body.error) {
// LOG ERROR, AND RESPONSE
console.error(session.body.error);
return res.status(400).send({
message: "Something went wrong, check error log!"
});
}
// STEP 2: INITIATE UPLOAD
let iUpload = await initiateUpload(session.body.id, req.file.buffer);
// SUCCESS
if (iUpload.body.h) {
// SUCCESS RESPONSE
console.error(iUpload.body);
return res.status(200).send({
message: "Uploaded!",
body: iUpload.body
});
}
// ERROR
else {
// LOG ERROR, AND RESPONSE
console.error(iUpload.body);
return res.status(400).send({
message: "Something went wrong please try again!"
});
}
}
});
}
We have completed our coding part here, make sure you have replaced the above functions in the Code Structure, where I have added the comments!
Run The Project
We are almost done with the coding part let’s execute our code by below command,
> node index.js
Listening on port 3000...
Execute The Upload Media API
Great Congratulations 🙌, we are almost done, let’s execute our API in Postman,
You can directly copy and paste the below CURL in Postman.
curl --location 'http://localhost:3000/uploadMedia' \
--form 'file=@"/path to media"'
Go to the Body section and select the file for the file parameter, and click on Send button, as you can see in the below screenshot,
The final response is your uploaded file handler in the h property, just use that string in your graph API requests.
{
"message": "Uploaded!",
"body": {
"h": "4:ZG93bmxvYWQgKDEpLmpwZw==:aW1hZ2UvanBlZw==:ARY3HNklArENsOBwJb7HnguOZ08W-Xj1tXwtgQwknQu3c_V0mmvLZUrWc2kWGXYwzPZrON63cOHHear9lkWEJuf31YbhOXjDJiitvHHgigwYOg:e:1692126354:1183230345850902:100068155247228:ARZgwdwGI7aa-LqOdww"
}
}
You can see the full project from this GitHub repository, if you are uploading the file for WhatsApp Business Templates then there is a demo for the create template API.
Thanks.