The term API stands for application programming interface, a concept that applies everywhere from command-line tools to enterprise code, microservices, and cloud-native architectures. An API is an interface that software developers use to programmatically interact with software components or resources outside of their own code. An even simpler definition is that an API is the part of a software component that is accessible to other components.
Unless you write every line of code from scratch, you will interact with external software components, and each of these will have its own API. Even if you do write all of your code from scratch, a well-designed application should have internal APIs to help organize the code and make its components more reusable.
The API is a key concept in software development, from simple programs to the most advanced design and architectural considerations. This article will help you understand APIs and how they are used in software development.
APIs in software development
An API is the part of a software program that is accessible to other programs. It is the external area of a software component. For this reason, you might see a reference to the API surface area of a program. The API surface area is the outside layer of the program or component, like the wall of a cell, as shown in Figure 1.
When one program is used by another program, we call the first program the provider and the second one the client. The part of the provider that is accessible to clients is the API. This arrangement is found in software applications and systems of almost every type. What is consistent is that the API is a way for clients to make calls to the provider. The API defines a known range of allowable inputs and associated outputs to the component. Therefore, the API defines the protocol for communicating with a component.
All but the most trivial software uses capabilities provided by other components. A software program calls a component’s API to access its capabilities. In addition to using other components, most software is used as a component by other programs, as shown in Figure 2.
APIs vs. UIs
You might notice some similarities between APIs and user interfaces, or UIs. This makes sense because both are interfaces. An interface is a means of interacting with a system’s internals. Generally, the interface’s job is to simplify and concentrate internal capabilities into a form that is useful for the client. What is different about APIs and UIs is that they interface with different types of clients.
On a laptop, the UI consists of input devices such as a keyboard and mouse and output devices such as a monitor and keyboard. The client is the person using the laptop. In addition to the operating system, many of the programs running on the laptop also present a UI, which the user can interact with via the laptop’s input and output devices. For example, the web browser presents a set of visual elements on the screen that can be controlled with the mouse and keyboard.
The browser API
Now, let’s think about that web browser. We know that a browser is used to open a variety of web pages. Most web pages load JavaScript as part of their makeup. The browser runs the JavaScript to help display the page. In order to work, the JavaScript program requires access to the browser’s capabilities. In this case, the JavaScript program is the API client and the browser is the API provider. The browser is a provider that offers web browsing capabilities that the JavaScript program accesses via a programming interface, the browser’s API.
For example, if you type F12 right now and open a JavaScript console, you can type in a small JavaScript program to interact with your browser’s API. If you enter the code in Listing 1 into the console, you’ll begin to see output.
Listing 1. Tracking mouse in browser console
window.onmousemove = function(e){console.log("mouse cursor:", e.clientX, e.clientY)}
Note that after you start seeing console output, you can unset this setting by entering:
window.onmousemove = null
The point of this example is that the window object is a part of the browser’s API. Also, the onmousemove
function (or method) is a member of the window object. In turn, it is part of the window object’s API. So, once you are in the code, you can start to see that APIs are everywhere. If you want to use a component’s functionality, you can access it through the component’s API.
Nested APIs
Another observation is that APIs exist at different levels of a program and contain each other. The window API is in a sense nested inside the browser API.
Let’s think a bit more about how the browser does its job. This will help us get a feel for a couple of other kinds of API. For one thing, how does the browser know the mouse’s position? It registers with the operating system via an API. The operating system then loads a mouse driver, which offers a standardized API to provide streaming information about what the mouse is doing. (The driver itself, if you keep digging, ultimately relies on low-level hardware and software interfaces—a third kind of interface along with UI and API.)
APIs in libraries, packages, and modules
The browser console is a specialized context in that all the libraries are preloaded by the runtime environment. It’s more common for libraries to be explicitly loaded by the programmer. How the loading happens varies by programming language, but whenever you see import
, include
, or require
in your output, it means the current program is pulling in another program’s API.
All but the most trivial programs consist of language-level expressions and constructs (like if
s, loops, and operators) used in conjunction with calls to the APIs found in other packages. In turn, each program is also a component that can potentially be included and used by other programs.
Modules, packages, and libraries are all the same notion, broadly speaking: they are collections of code that can be included. The sum of their publicly visible parts—classes, objects, methods, functions, variables, and so on—is the surface area of the API in that collection.
Remote APIs and microservices
At the highest level, we could divide APIs into two types: local and remote. Remote APIs are useful in that they don’t require updates to the code on client devices, they can be scaled independently, and they present a standardized API form that any client can call provided that it is authorized.
In general, we could say that remote APIs (also known as services) are strongly decoupled and offer a standard protocol (HTTP/S over TCP) that works regardless of the implementation stack behind them.
Let’s set up a quick interaction with a remote API. Again in your JavaScript console, enter the code in Listing 2.
Listing 2. Calling the Lord of the Rings API
const fetchData = async () => {
const rawQuotes = await fetch('https://the-one-api.dev/v2/quote', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer xvi06TocPJvBmrQC4yZv'
}
})
const quotes = await rawQuotes.json();
const quote = quotes.docs[Math.floor(Math.random() * quotes?.docs?.length)];
const rawCharacters = await fetch('https://the-one-api.dev/v2/character?_id=' + quote.character, { headers: headers })
const characters = await rawCharacters.json();
const character = characters.docs[0];
console.log(character.name + " said: " + quote.dialog);
};
fetchData();
If you run this code, you’ll get a console log, something like: “Gollum said: They are young. They are tender. They are nice. Yes, they are. Eat them. Eat them!” What’s happened is you created a JavaScript program that used the browser API (specifically, the fetch API), to issue an HTTP call against the server that lives at https://the-one-api.dev. That is a REST API, which means it follows certain architectural conventions.
Microservices and API gateways
A microservices architecture essentially uses remote APIs for activities that were traditionally done by local API. A microservices architecture decomposes an application into components that are remotely available.
The concept of an API gateway is specific to the microservices architecture. In an API gateway, a single point of contact is defined on the network for orchestrating routes to specific services. This allows for certain benefits (like authentication and rate limiting across services), and as such operates as a kind of interface of interfaces.
Remote APIs are everywhere
The internet is basically a universe of interacting remote APIs. Everything that runs on a device is a local API. Servers are collections of local APIs that conspire to provide a remote API. Clients are collections of local APIs that work together to consume remote APIs. Middleware is a collection of local APIs that both conspire to provide a remote API and work together to consume other remote APIs.
APIs and good software design
Across all platforms and languages, there are different ways to control what is visible and how it is used by client code. API design pays much attention to the idea of information hiding, which is the linchpin of maintainable software. Good software design depends on it.
The idea is to write software components that do everything required of them with the smallest possible point of contact. As developers, we want to provide only the most essential information about a component’s internal workings. This concept applies to everything from a tiny function—whose signature is an API writ small—to powerful remote services.
Good APIs are the opposite of spaghetti code. In spaghetti code, the flow of execution and dependency is very hard to follow, which makes code hard to maintain and debug. (This is why GOTO is considered harmful.)
In good API design, software components behave much like mechanical components. They perform a particular job behind a well-understood interface. You can deal with the component as a single entity. The alternator on an automobile does one thing: it creates a charge. If it fails, the mechanic can isolate that part. Just as important, the rest of the car only needs to know the belt is going around the alternator pulley. In this analogy, the pulley and the alternator’s electrical terminals would be the alternator’s API.
Isolation is never perfect, of course. Even in the case of an alternator, the belt and tensioner condition interact in important ways. Developers try to create good components, subsystems, and architectures. We do this to keep a grip on the main antagonists in software: the forces of entropy and complexity.
Concentrating complexity in components and making their interfaces and protocols as simple as possible makes our software systems much easier to manage.
Good APIs make good software
APIs are essential to good software design, and they assume a range of incarnations in the different layers of our software. When you are writing software, it is valuable to think of the code you are writing as a component, potentially reusable in other systems. Consider the characteristics of your component’s API. If you do, you’ll likely focus on a limited set of functionality, and you’ll think about how the API surface area will interact with other components. Think in terms of the separation of concerns, and try to expose only as much information about your component as is strictly required.
Using well-designed APIs lets us compose our software of logically distinct components. These components can be maintained in relative isolation, and the functionality behind them can be reused between different applications.