This site lists the protips that we shared with students during our courses
One of the concepts that we’ve seen a lot of confusion about early on in the classes we have given is the notion of an API (Application Programming Interface). It has somehow got tangled up with a Web API which is just one form of API, that we can access over the web. Or a REST API which is an API that adheres to the constraints posed by REST.
In this post, I wanted to clear out some of the confusion and also give a tip or two on how these concepts can be used to make you a better programmer.
When two machines (or programs) are interacting with each other sometimes it can lead to a new little machine~ (sorry - I couldn’t help myself).
Ok - once again; two machines interacting with each other means that they need an agreed way to communicate. This is called an interface. The machines, or programs, have decided what kind of information to communicate and how to communicate that information.
Sadly, for JavaScripters, the language we are using is dynamically typed which gives us less strict tools to define an interface. But still - it can be done. Consider the following code in a calculator.js
-file:
const add = (a, b) => a + b;
module.exports.add = add;
There’s no way for another module to interact with this code except for the exported add
function. Calling a non-existing subtract
function will render an error.
The interface of the calculator
is defined as one function add
. A client of the calculator
only sees a function called add
that takes two parameters. This is the Application Interface (API) defined by calculator
to users of it.
Sadly calling add('Marcus', 'Hammarberg')
will not only work but return a result that probably is not what we wanted. It works but does the wrong thing. Also, calling the function with too few add(1)
or two many add(1,2,3)
will not render an error until the code is run.
This is due to the dynamic typing of JavasScript and also to the fact that JavaScript is interpreted, meaning that the script is run top to bottom why we often need to write some protection code and checks to ensure we got passed what we wanted and only what we wanted.
In a language like TypeScript, there’s strict typing that will protect us better. But also constrain the code a bit more.
A web API means that we expose an API on the web, usually through HTTP. What is cool about this is that we could now call our code by just using a simple HTTP call. HTTP is a well-defined protocol from a very long time ago. Hence it’s supported by just about any general-purpose language you can come up with. So we can now easily have a python script calling our Node code.
Let’s create a Web API that calls into our calculator, using an Express server with the following code in a server.js
-file (you need to do npm init -y && npm i express
before this runs):
const app = require('express')()
const calc = require('./calculator')
app.get('/add/:num1/:num2', (req, res) => {
const num1 = Number.parseInt(req.params.num1)
const num2 = Number.parseInt(req.params.num2)
res.json({ result: calc.add(num1, num2)})
})
const port = 3000
app.listen(port, () =>
console.log(`You can try http://localhost:${port}/add/1/3`)
)
Ok, this is also an interface - it’s a bit harder to see but let’s squint and try to see it:
/add/1/2
for this to work. /adder/1/2/
or /add/1
will not respond or crash./add/Marcus/Elin
will sadly lead to a failed adding, with a result of null
result
property. There’s nothing else returned. The .json()
function will ensure to return a header with content-type application/json
.So even if it’s not enforced by a compiler or strict typing there is an interface defined, that dictates what we can send and expect back from this simple little program.
Notice how our Web API is using the calculator
to do its job. The actual calculation is done in the calculator
. The server
module only sees the API of the calculator
.
Before only other JavaScript code could use our wonderful calculator
but now anyone that can issue an HTTP request to /add/:num1/:num2
can get a JSON-response from us with the result
set. Python, Java, C# - hey even curl
- we don’t care.
This is much longer and beyond the scope of this post, but a REST API is an API that adheres to the constraints of REST. It is typically a Web API and uses HTTP - but doesn’t have too. As long as the API follows the rules of REST it is considered a REST API.
Or Why on earth is this important?
Programming to an interface rather than implementation is a great tip that will make your code more modular, easier to understand from the outside and easier to maintain and change as it grows. It’s a little bit like separating the WHAT (the interface) and the HOW (the implementation).
Let’s go back to the calculator
example and see if I can explain my reasoning:
A client from the outside only sees a .add
function that takes two (dynamically typed parameters) - what is happening with that on the inside is not known. All we know is that we pass it two parameters and get a return value (because all JavaScript functions return something, although it might be undefined or an Error that gets thrown).
For our client, it means that we can use the calculator
API as we did in the server
above
res.json({ result: calc.add(num1, num2)})
On the inside, we can do whatever we want, as long as we don’t break that API. For example, we might use other tools and restructure the function totally - it doesn’t matter to our client:
const validator = require('./validator')
const scientificCalculator = require('./sciCalc')
const add = (a, b) => {
const intA = validator.validateForInt(a)
const intB = validator.validateForInt(b)
const result = scientificCalculator.add(intA, intB)
return result
}
module.exports.add = add
WHOA! That’s one stable calculator. Not only validating but also using another, much better (?), SciCalc-module.
Here’s the thing - our client is still just using the .add
function. It doesn’t care. The interface is not changed, but the implementation has. We have completely changed HOW the code does its thing, without touching the WHAT it does.
We could even add new functions to our calculator
- our client
still doesn’t care. Should be we rename or change the signature of the add
function, then we have done what’s known as a breaking change and our client
code will care and break.
Understanding the difference between an interface and the implementation can help you to write better code. Even though JavaScript has weak support for typing interface we can still make use of that difference.
Some definitions: