DEV Community

Cover image for WTF is an API? (or, "Everything's a Lie")
Gabriel Cruz (he/him)
Gabriel Cruz (he/him)

Posted on

WTF is an API? (or, "Everything's a Lie")

This is a rewrite of an older post of mine (this one). It bothers me that a lot of people explaining APIs throw around loads of confusing, meaningless buzzwords, to an otherwise easily explainable topic. It feels like these people are just trying to sound smart instead of explaining APIs. The reason why this post could also be called "Everything's a Lie" is because a lot of what we'll be doing is proving that most things you see around about APIs are just lies.

In this post, we'll explore the meaning and reasoning behind the idea of an API, see some examples, and talk about REST. Oh, and we'll be doing some coding as well, as always.

Shall we?

The naming

First things first, let's talk about the acronym. API stands for Application Programming Interface, what does that mean?

The important part is Interface. An interface is anything that sits between two other things. We call the USB port a USB interface because it lets the computer communicate with peripherals (or vice-versa). Interfaces are important because they enforce a protocol that both ends must speak. It doesn't matter how the mouse or the computer are built, if they communicate through USB, they should follow the USB standard. Application Programming means creating an interface for applications to talk to each other, making application programming easier.

Protocols in the computer world are a set of rules that devices should adhere to to communicate. Something like: "To adhere to the USB standard, you must send three bytes with values x, y and z, then you should wait for the other device to send value a1, etc."

e.g.: In a valid HTTP request message, the first few characters must be the method (GET, POST, DELETE, etc) followed by a whitespace, followed by a path (/users, /, etc) followed by... You get the idea.

If you have ever read anything about APIs you might be wondering: what does this have to do with HTTP or GET or POST? And the answer is: nothing! Really, nothing. In theory, there's nothing that dictates that APIs have to be implemented using HTTP.

If you've never read anything about APIs before, however, the last paragraph made no sense whatsoever. Let's rewind a bit.

APIs are like libs

To explain what I mean, let's implement a simple Rust library (or lib) to print ASCII characters enclosing text, like so:

let my_text = "hello world";
enclose(my_text, '#');
Enter fullscreen mode Exit fullscreen mode

The output for the above should be:

###############
# hello world #
###############
Enter fullscreen mode Exit fullscreen mode

Here's the full Rust source code for the curious:

fn enclose(text: &str, enclose_char: char) {
    println!("{}", enclose_char.to_string().repeat(text.len() + 4));

    println!("{enclose_char} {text} {enclose_char}");

    println!("{}", enclose_char.to_string().repeat(text.len() + 4));
}

fn main() {
    let my_text = "hello world";
    enclose(my_text, '#');
}
Enter fullscreen mode Exit fullscreen mode

Now let's say that we want other people to use our code, for that we'd make this into a lib and the code wouldn't look much different:

pub fn enclose(text: &str, enclose_char: char) {
    println!("{}", enclose_char.to_string().repeat(text.len() + 4));

    println!("{enclose_char} {text} {enclose_char}");

    println!("{}", enclose_char.to_string().repeat(text.len() + 4));
}
Enter fullscreen mode Exit fullscreen mode

We just deleted the main function and added pub to make the enclose() function public. You'd need to alter some things in the package configs as well, but the point is that if we actually want our code to be modular and usable, there are much more important changes we need to do:

pub fn enclose(text: &str, enclose_char: char) -> String {
    let horizontal_line = enclose_char.to_string().repeat(text.len() + 4);
    format!("{horizontal_line}\n{enclose_char} {text} {enclose_char}\n{horizontal_line}")
}
Enter fullscreen mode Exit fullscreen mode

The important change is that we return the new enclosed string instead of printing it to the standard output. This is important because if we want people programming applications we need to make our code easily pluggable. This new version allows programmers to get the strings and do whatever they want with it. Maybe they don't want to print the string, maybe they want to plot it as a 3D model instead!

What we wrote is a library (lib, for short). Libs are collections of functions and types that help to develop something. An API is like a lib: it's code on top of which people build applications. Now, let's explore what people usually mean when they talk about APIs.

Note: "Technically," purists will say, "libs have APIs, because the API would be only the function signatures such as pub fn enclose(&str, char) -> String in our example."

Purists are bikeshedding dicks. Do not trust purists.

What people usually mean when they talk about APIs

In the early days of the internet websites were dead stupid: static HTML, manually updated from time to time.

Static HTML pages are what we call HTML that's in a file in the server (the HTTP server, also called a "web server"), which hands clients a copy of that file. Dynamic pages on the other hand contain code and style sheets, usually JavaScript and CSS respectively, that allow the client to dynamically generate the "final" HTML. With dynamic pages, the client downloads HTML templates, JS, CSS, and then generates one big HTML by crunching those three together.

Let's say we wanted to write a bot to get the news titles from your local newspaper website back in 2001. Back then, you'd probably have to download the HTML for the entire page, parse it, and spit out only the titles1. Here's what that would look like in Python (in this case we're parsing motherfuckingwebsite.com):

import requests
import re

text = requests.get("http://motherfuckingwebsite.com/").content.decode('utf-8')

pattern = re.compile(r'<h2>(.*?)</h2>', re.DOTALL)

# Find all text between <h2> and </h2> in the HTML content
titles = pattern.findall(str(text))

for t in titles:
    print(t)
Enter fullscreen mode Exit fullscreen mode

Output:

Seriously, what the fuck else do you want?
It's fucking lightweight
It's responsive
It fucking works
This is a website. Look at it.  You've never seen one before.
Enter fullscreen mode Exit fullscreen mode

This approach is (1) wasteful because we have to download many characters (the entire page!) only to get a few titles and (2) the code is hard to maintain because we have to figure out where the titles are in the HTML (in this case they're on the h2 tags), and if they decide to change the layout of the website, we'd have to rewrite our bot.

So when people talk about APIs, they usually mean HTTP APIs, which is a way to facilitate getting information for your website. Let's write an API for motherfuckingwebsite.com:

from flask import Flask, jsonify
import requests
import re

app = Flask(__name__)

@app.route('/titles', methods=['GET'])
def get_titles():
    text = requests.get("http://motherfuckingwebsite.com/").content.decode('utf-8')

    pattern = re.compile(r'<h2>(.*?)</h2>', re.DOTALL)

    # Find all text between <h2> and </h2> in the HTML content
    titles = pattern.findall(str(text))

    # Return the titles as JSON
    return jsonify(titles)

if __name__ == '__main__':
    app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

Here's what we do:

  1. Get the raw HTML
  2. Spawn a webserver of our own
  3. Make the list of titles available in JSON by GETting /titles

If our website were called amazingwebsite.com then you'd only have to GET http://amazingwebsite.com/titles to get the JSON with the titles.

REST: Representational State Transfer

Over time people started getting clever and realized that they could make their APIs receive data from the applications/users instead of only giving it away. A canonical example is Twitter's API, which allows programmers to create bots that send data (like a bio update or a tweet) to the server.

Here's what a bio update looks like:

curl -X POST https://api.twitter.com/1.1/account/update_profile.json?name=Sean%20Cook&description=Keep%20calm%20and%20rock%20on
Enter fullscreen mode Exit fullscreen mode

HTTP uses verbs/methods to allow us to do different things with a server. If we're requesting information, that's typically a GET request, but in the example above we're actually sending data to the server, so we use the POST method. The cool thing is that we can have different behaviors for the same route by using different methods:

# get data on the gmelodie account
curl -X GET mywebsite.com/accounts/gmelodie

# delete the gmelodie account
curl -X DELETE mywebsite.com/accounts/gmelodie

# update the description of the gmelodie account to newDescription
curl -X POST mywebsite.com/accounts/gmelodie?description=newDescription
Enter fullscreen mode Exit fullscreen mode

That's basically what a REST API is. Again, purists will say there's much more to REST APIs, but the gist of it is: REST APIs are APIs that do different things based on the HTTP method you pass to a route.

Appendix I: Idempotency

From dictionary.cambridge.org:

An idempotent element of a set does not change in value when multiplied by itself.

In programming, a function is idempotent if, when called twice with the same parameters, the operation only happens once. This is especially interesting when building APIs. For instance, if a client makes two requests to send the same tweet, they probably don't want to post the same tweet again.

Or do they? Maybe they do! To make sure the client wants to do the same thing twice, you can add an idempotency-key field to the requests. If the server gets two identical requests to post the same tweet with the same idempotency-key, it'll disregard one of them. Now, if the server receives two identical requests to post a tweet, but with two different idempotency-keys, it'll post both tweets.


Thank you for reading!

References


  1. RSS wasn't very popular then. 

Top comments (0)