Server-Side Application Programming Interfaces
Learning Objectives
- You know what application programming interfaces (APIs) are.
- You rehearse creating an API that serves and processes JSON data.
Application Programming Interfaces (APIs)
Application Programming Interfaces (APIs) refer to services that make it possible to communicate with systems that are hidden behind the API.
There are many different types of APIs, and there are no strict rules for what an API can or cannot do. APIs can be created for various purposes, including data retrieval, data storage, and data processing. An API could also, for example, be used to control a smart device, such as a door lock, a dishwasher, or a lamp.
As an example, Philips Hue smart lights can be controlled using an API, which — depending on the light bulb — allows e.g. changing the color, brightness, and on/off state of the light bulb.
When creating APIs, there are several aspects to consider, such as the protocol, the data representation format, and the security of the API. An API often has multiple endpoints for different purposes, such as for retrieving data, updating data, or deleting data.
In terms of the client-server model, most APIs are on the server-side, meaning that the server provides an API through which the client requests resources.
In this course, our primary focus is on APIs that use the HTTP protocol and serve and process JavaScript Object Notation (JSON) formatted data. The API endpoints are defined as routes, i.e. path and request method mappings to the functions, which define the functionality provided by the API.
Let’s look into creating a few server-side APIs that serve and process JSON data.
Server-side APIs
Here, we look into three small examples of what a server-side API could look like. The first one focuses on maintaining and updating a light bulb status, the second one on counting clicks, and the third one on managing a list of items.
Light bulb status
An API for changing the status of a light bulb could consist of two endpoints: one for retrieving the status and one for switching the status (on/off). HTTP GET method would be a natural choice for retrieving the status, while changing the status could be implemented with the HTTP POST.
The API could look as follows:
import { Hono } from "jsr:@hono/hono@4.6.5";
const app = new Hono();
let on = false;
app.get("/status", (c) => c.json({ on }));
app.post("/status", (c) => {
on = !on;
return c.json({ on });
});
Deno.serve(app.fetch);
The above server would respond to requests as follows.
curl localhost:8000/status
{"on":false}%
curl -X POST localhost:8000/status
{"on":true}%
curl localhost:8000/status
{"on":true}%
We could also have a third endpoint, which would be explicitly used to set the status of the light bulb to on or off. For this, the HTTP PUT method would be a suitable choice, as it is used to update a resource.
In this case, the API would also have to accept data in the request body, which would specify the new status of the light bulb. The following outlines how the API could be extended to include the PUT method.
import { Hono } from "jsr:@hono/hono@4.6.5";
const app = new Hono();
let on = false;
app.get("/status", (c) => c.json({ on }));
app.post("/status", (c) => {
on = !on;
return c.json({ on });
});
app.put("/status", async (c) => {
const body = await c.req.json();
on = body.on;
return c.json({ on });
});
Deno.serve(app.fetch);
Now, we could also explicitly set the status of the light bulb to on or off.
curl -X PUT -d '{"on":true}' localhost:8000/status
{"on":true}%
curl localhost:8000/status
{"on":true}%
curl -X PUT -d '{"on":false}' localhost:8000/status
{"on":false}%
Click counter
In a similar way, we could create an API for counting clicks. In the following, GET is used for retrieving the count, POST is used for incrementing the count, while DELETE is used for for decrementing the count.
import { Hono } from "jsr:@hono/hono@4.6.5";
const app = new Hono();
let count = 0;
app.get("/count", (c) => c.json({ count }));
app.post("/count", (c) => {
count++;
return c.json({ count });
});
app.delete("/count", (c) => {
count--;
return c.json({ count });
});
Deno.serve(app.fetch);
The above server would respond to requests as follows.
curl localhost:8000/count
{"count":0}%
curl -X POST localhost:8000/count
{"count":1}%
curl -X POST localhost:8000/count
{"count":2}%
curl localhost:8000/count
{"count":2}%
curl -X DELETE localhost:8000/count
{"count":1}%
List of items
Another example could be an API for managing a list of items. Such an API could have three endpoints: GET for retrieving the list of items, POST for adding an item to the list, and DELETE for removing an item from the list.
For simplicity, we assume that the items have an id property, which is unique for each item. When deleting, we only need to provide information about the id of the item to be removed.
The following outlines how the API could be implemented.
import { Hono } from "jsr:@hono/hono@4.6.5";
const app = new Hono();
let items = [];
app.get("/items", (c) => c.json({ items }));
app.post("/items", async (c) => {
const body = await c.req.json();
items.push(body);
return c.json({ items });
});
app.delete("/items", async (c) => {
const body = await c.req.json();
items = items.filter((item) => item.id !== body.id);
return c.json({ items });
});
Deno.serve(app.fetch);
The above server would respond to requests as follows.
curl localhost:8000/items
{"items":[]}%
curl -X POST -d '{"id":1,"name":"apple"}' localhost:8000/items
{"items":[{"id":1,"name":"apple"}]}%
curl -X POST -d '{"id":2,"name":"banana"}' localhost:8000/items
{"items":[{"id":1,"name":"apple"},{"id":2,"name":"banana"}]}%
curl -X DELETE -d '{"id":1}' localhost:8000/items
{"items":[{"id":2,"name":"banana"}]}%