WHAT ARE APIs?
API is short for Application Programming Interface. You can think of it as an intermediary between computers or software systems, It provides an environment for those systems to interact with each other, Usually you have a client(your application) which requests information from the server(service) and the API determines how that entire environment will function.
How is Data passed through APIs?
The predominant form in which data is passed in an environment leveraging APIs is through JSON which stands for JavaScript Object Notation which is a light-weight text-based format used for representing structured data based on JavaScript object syntax.
Data in JSON is represented in key - value pairs
Those data can be encapsulated in an array or object form. Data encapsulated in "curly braces" {}
denote object-form while Data encapsulated in "square brackets" []
denote array-form.
NB : As you can see JSON objects can be nested into one another, the same goes for JSON arrays
REST APIs
If you've been in the software development ecosystem for even a little while, you've most likely heard of REST APIs. REST is short for Representational State Transfer
which is a software architectural style. A REST
API an API that follows the REST design principles. Unlike some other API models, REST APIs are flexible, user-friendly and easy to work with across multiple programming languages and it has non-strict design principles which is why it's easy to work with.You can read on REST design principles here.
Working with REST APIs
So I've chosen the Qt application framework as the tool which I will use to demonstrate how to work with REST APIs.
Qt ?
Qt at it's base is an application framework written in C++ which can be used to develop wide range of cross-platform applications and graphical user interfaces.
I've decided to use the GitHub REST API for the demonstration on working with REST APIs
First I will create a Project called GitHub_API
in Qt Creator(the IDE for Qt framework) which inherits the QMainWindow
base class and add the network
module in our project file because we will be working with networks in this project.
I have designed a simple GUI to use with our API program using Qt Designer
NB : The goal of this article is to show how to work with REST APIs in Qt which already assumes you have basic knowledge on how Qt works
Important Qt Objects Used
QNetworkAccessManager
- allows our application to send and receive network repliesQNetworkRequest
- contains a request to be sent to aQNetworkAccessManager
objectQNetworkReply
- embodies areply
which is gotten when a request is carried out byQNetworkAccessManager
We define a few variables that we will be using in our program:
netManager
- will manage our network requests and repliesnetReply
- will handle most of the replies we get from the APIrepoReply
- will handle the replies we get concerning repositories from GitHubdataBuffer
- will contain the data we receive from the APIimg
- will contain the an image (profile pic)
We want this program to collect a 'GitHub Username' from the user and then it will request information on the GitHub user(if it exists) through the GitHub API, the API will return the information in form of JSON and the program will make use of that data to display some information through the GUI
When the user clicks on the USERNAME
Button, it should bring up a simple dialog window in which the user would input the username of the GitHub Account
And then when the user inputs the username and clicks on the ok
button, we first store that username into a variable then we call the clearValues()
function which resets all the information displayed i.e It resets the program to its default state which is when no information is being displayed.
Those variables that are being cleared are the GUI elements that you see visually in the program when information on a GitHub User is being showed, we want to make sure they're empty before we display info
After they're cleared, we create two QNetworkRequest
variables (req, repoReq) which we will use to request for information on the user and the repositories of the user
How do we request information from the GitHub API
Open your browser and type in api.github.com/users/YOUR_USERNAME
and you'll see that it will return information on the GitHub username you put in place of YOUR_USERNAME
in form of a JSON document. In my case:
So we put in api.github.com/users/%1
as the URL
for req
and api.github.com/users/%1/repos
for repoReq
.
NB : %1 is a placeholder text, it will be replaced by what is passed into the arg method, In this case it will be replaced with the username variable..It's similar to how C's printf() function works
We then call the get
method for the QNetworkAccesManager
object and pass in req
as the argument. It basically gets the content of what the req
requests (which in this case is the information of the user in JSON format) and returns it in form of a QNetworkReply
object.
Then we store what is returned into the netReply
variable and then we connect its signals(readyRead
and finished
) to our custom slots readData
and finishedReading
readyRead
signal is emitted when there is data to be read from thenetReply
objectfinished
signal is emitted when thenetReply
object has finished processing the data it got from the request
So basically readData
and finishedReading
is called when the readyRead
and finished
signal for the netReply object is emitted
//READ DATA CUSTOM SLOT
void MainWindow::readData()
{
dataBuffer.append(netReply->readAll());
}
//FINISHED READING CUSTOM SLOT
void MainWindow::finishReading()
{
if(netReply->error() != QNetworkReply::NoError){
qDebug() << "Error : " << netReply->errorString();
QMessageBox::warning(this,"Error",QString("Request[Error] : %1").arg(netReply->errorString()));
}else{
//CONVERT THE DATA FROM A JSON DOC TO A JSON OBJECT
QJsonObject userJsonInfo = QJsonDocument::fromJson(dataBuffer).object();
//SET USERNAME
QString login = userJsonInfo.value("login").toString();
ui->usernameLabel->setText(login);
// SET DISPLAY NAME
QString name = userJsonInfo.value("name").toString();
ui->nameLabel->setText(name);
//SET BIO
auto bio = userJsonInfo.value("bio").toString();
ui->bioEdit->setText(bio);
//SET FOLLOWER AND FOLLOWING COUNT
auto follower = userJsonInfo.value("followers").toInt();
auto following = userJsonInfo.value("following").toInt();
ui->followerBox->setValue(follower);
ui->followingBox->setValue(following);
//SET ACCOUNT TYPE
QString type = userJsonInfo.value("type").toString();
ui->typeLabel->setText(type);
//SET PICTURE
auto picLink = userJsonInfo.value("avatar_url").toString();
QNetworkRequest link{QUrl(picLink)};
netReply = netManager->get(link);
connect(netReply,&QNetworkReply::finished,this,&MainWindow::setUserImage);
dataBuffer.clear();
}
}
void MainWindow::setUserImage()
{
img->loadFromData(netReply->readAll());
QPixmap temp = img->scaled(ui->picLabel->size());
ui->picLabel->setPixmap(temp);
}
- readData - appends the data(JSON document) received from the executed request into the data buffer(
QByteArray
object)
NB : A QByteArray is used to store raw bytes
finishedReading - first checks to see if an error occurred during the request, if there was an error it will display a simple dialog window notifying the user of the error. If there was no error it does the following steps :
- Convert the bytes of data in the
dataBuffer
to aJSON
Document and then convert that document to a JSON object nameduserJsonInfo
, we can do this because remember when i said JSON objects are denoted using curly-braces{}
, If you go to the site on your browser you will see that all the data is in fact enclosed in a curly brace which means that we can convert that document straight-away into a JSON object. - We then retrieve the username of the GitHub user by calling the
value
method on theuserJsonInfo
variable which expects a "key", it will return the value associated with the key (it will return aQJsonValue
object which we convert to astring
by calling it'stoString()
method), We pass in the "login" key and we expect that it would return theusername
value associated with it. Then we pass the value in to theusernameLabel
Label object which is in our GUI.- We repeat the same action multiple times and get the display name, bio, number of followers and followings and the account type
- We then request for the display picture of the user and connect the
finished
signal to a custom slotsetUserImage
then we clear our buffer because we dont need to store any more data.
setUserImage
- gets the image from the buffer then it scales it down to the size of thepicLabel
object so it can fit in it and then sets thepicLabel
object to show the image by callingsetPixmap()
- Convert the bytes of data in the
NB : The dataBuffer object gets cleared after the setUserImage function is completed
So that's how the user info is retrieved from the API...Then we retrieve the repository info of that user because we haven't done that.
All the things we explained above happened in those two lines, then the next line of code executes which executes the request for repository info on the user. We connect some more custom slots to the repoReply
object which will contain the JSON document that has information on the repositories of the GitHub Account.
void MainWindow::readDataForRepo()
{
dataBuffer.append(repoReply->readAll());
}
void MainWindow::finishedGettingRepos()
{
if(repoReply->error() != QNetworkReply::NoError){
qDebug() << "Error Getting List of Repo: " << netReply->errorString();
QMessageBox::warning(this,"Error",QString("Request[Error] : %1").arg(netReply->errorString()));
}else{
QJsonArray repoInfo = QJsonDocument::fromJson(dataBuffer).array();
ui->repoBox->setValue(repoInfo.size());
for(int i{0}; i < ui->repoBox->value(); ++i){
auto repo = repoInfo.at(i).toObject();
QString repoName = repo.value("name").toString();
ui->repoList->addItem(repoName);
}
}
}
readDataForRepo
- appends the data(JSON document) into the dataBufferfinishedGettingRepos
- first checks if there was an error with the request and displays the error to the user, if there was no error it creates a JSON document out of the data stored in our dataBuffer and then it converts it into an array because the information in the document is enclosed in square braces which means it's a json array. Then it sets the value of therepoBox
GUI element to the size of the array(the size of the array would be the number of public repos that user has)
We then use a loop
(that is constricted to the length of the array) to go through each item(repo) in the array and get its name and then add it to our list widget
which is another of our GUI component
NB : A QListWidget is used to display items in a .........list form
And that's all, if we build and run the application we get the GUI i had designed and pass in my GitHub username, we get this:
Aside from the Trash UI, the program works as intended, It successfully interacts with the GitHub API!!
PS : Here is the
REPO which contains this entire program in case you want to view the entire source code