๐ Day 5 โ Write Your First Express API
17 min readยทJan 1, 2026
Welcome to Day 5.
In yesterday's lesson, you introduced configuration so the project can behave differently depending on its environment.
In today's lesson, you'll expose your notes through a real HTTP API using Express.
You'll learn what an API endpoint is, how a server responds with JSON, and how to test your API like a backend developer using curl.
By the end of this lesson, you'll be able to request your notes over HTTP and get a JSON response back.
Set up the server
Express is popular Node.js web framework that allows developers to easily create web servers and APIs.
Let's start by installing the express package using the npm install command.
~/projects/lb_notes$ npm install --save express
Then, let's add the following "scripts" property to the package.json file that will allow you to run the web server using the npm run server command.
{
"name": "lb_notes",
"version": "1.0.0",
"type": "module",
"bin": {
"lb_notes": "bin/lb_notes.js"
},
"scripts": {
"server": "node src/api/server.js"
},
"dependencies": {
"express": "^5.2.1"
}
}
๐ In npm, scripts are used to define a list of reusable shell commands that be easily run using the
npm runcommand followed by their name.
Add the server port to the configuration
Within the .env file, let's add a new configuration variable named LB_SERVER_PORT that will be used by the server to listen for incoming HTTP requests.
LB_NOTES_FILE_PATH=data/notes.json
LB_SERVER_PORT=5000
๐ In web development, a port is an integer number that allows to uniquely identify a service (e.g., a web server) on a machine connected to the network (e.g., the Internet). You can think of it as the street number of a house.
Create the server
Within the src directory, let's create a new directory named api, and within it, a new file named server.js.
~/projects/
โโ lb_notes/
โโ .env
โโ bin/
โโ package.json
โโ src/
โโ api/
โ โโ server.js
โโ ...
Within this file, let's create a new server instance by executing the top-level express() function.
import express from 'express';
const server = express();
Load the configuration
Let's execute the loadEnv() function to load the .env file into the environment, and parse the LB_NOTES_FILE_PATH and LB_SERVER_PORT variables that will be used next by the server and its endpoints.
import express from 'express';
import path from 'node:path';
import loadEnv from '../config/load_env.js';
const server = express();
loadEnv();
const NOTES_FILE_PATH = path.resolve(process.env.LB_NOTES_FILE_PATH || 'data/notes.json');
const SERVER_PORT = parseInt(process.env.LB_SERVER_PORT, 10);
๐ก Environment variables are always loaded as strings. They must therefore be manually converted using functions such as
parseInt(),parseFloat(),JSON.parse(), and so on.
Listen for incoming HTTP requests
Let's connect the server to the port defined in the SERVER_PORT variable using the .listen() method of the server instance, to enable it to recieve HTTP requests sent by clients.
import express from 'express';
import path from 'node:path';
import loadEnv from '../config/load_env.js';
const server = express();
loadEnv();
const NOTES_FILE_PATH = path.resolve(process.env.LB_NOTES_FILE_PATH || 'data/notes.json');
const SERVER_PORT = parseInt(process.env.LB_SERVER_PORT, 10);
server.listen(SERVER_PORT);
Implement the GET /notes endpoint
Let's declare a new HTTP GET /notes endpoint whose role is to send an HTTP response to the client containing the array of notes retrieved from the same JSON file used by the CLI tool.
import express from 'express';
import path from 'node:path';
import loadEnv from '../config/load_env.js';
import { readJSON } from '../storage/json_fs.js';
const server = express();
loadEnv();
const NOTES_FILE_PATH = path.resolve(process.env.LB_NOTES_FILE_PATH || 'data/notes.json');
const SERVER_PORT = parseInt(process.env.LB_SERVER_PORT, 10);
server.get('/notes', (req, res) => {
//
});
server.listen(SERVER_PORT);
Where:
.get()is a method of the server instance used to define an HTTP GET route.'/notes'is a string that represents the path on the web server associated with this method.(req, res) => {}is a function executed by the server when an incoming HTTP request reaches this endpoint. Here,reqandresare both objects that represent the incoming HTTP request sent by a client and the ougoing HTTP response sent by the server.
๐ก In web development, HTTP GET endpoints are generally used to retrieve data from a server.
Within the request handler function, let's first read the content of the JSON file containing the notes using the readJSON() function.
import express from 'express';
import path from 'node:path';
import loadEnv from '../config/load_env.js';
import { readJSON } from '../storage/json_fs.js';
const server = express();
loadEnv();
const NOTES_FILE_PATH = path.resolve(process.env.LB_NOTES_FILE_PATH || 'data/notes.json');
const SERVER_PORT = parseInt(process.env.LB_SERVER_PORT, 10);
server.get('/notes', (req, res) => {
const data = readJSON(NOTES_FILE_PATH);
});
server.listen(SERVER_PORT);
Then, let's implement the same logic as the one used in the list() function of the CLI tool, however:
- Instead of returning upon success, let's respond to the client with an HTTP
200 OKcontaining an object with anotesproperty. - Instead of throwing upon error, let's respond to the client with an
HTTP 500 Internal Server Errorcontaining an object with anerrorproperty.
import express from 'express';
import path from 'node:path';
import loadEnv from '../config/load_env.js';
import { readJSON } from '../storage/json_fs.js';
const server = express();
loadEnv();
const NOTES_FILE_PATH = path.resolve(process.env.LB_NOTES_FILE_PATH || 'data/notes.json');
const SERVER_PORT = parseInt(process.env.LB_SERVER_PORT, 10);
server.get('/notes', (req, res) => {
try {
let data = readJSON(NOTES_FILE_PATH);
if (data === null) {
res.json({ notes: [] });
}
else if (data?.notes === undefined || !Array.isArray(data?.notes)) {
throw new Error('Invalid JSON file');
}
else {
res.json(data);
}
} catch(error) {
res.status(500).json({ error: error.message });
}
});
server.listen(SERVER_PORT);
Notes:
- In Express, the
.json()method of the response object (i.e.res) is used to send an HTTP response in the JSON format.- In Express, the
.status()method of the response object is used to change the HTTP status code of the response, which is set to200by default.
Test the API
Let's first launch the server using the server command defined in the "scripts" property of the package.json file.
~/projects/lb_notes$ npm run server
> lb_notes@1.0.0 server
> node src/api/server.js
Once up and running, let's open a second terminal window, and let's use the curl command to send an HTTP GET request to the /notes endpoint of the server, which should respond with an HTTP 200 OK containing an object with a notes property.
~/projects/lb_notes$ curl -i 127.0.0.1:5000/notes
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 51
ETag: W/"33-n2heD3aWuF5hdObDO06586LaQB0"
Date: Mon, 02 Feb 2026 16:44:26 GMT
Connection: keep-alive
Keep-Alive: timeout=5
{"notes":["Free scheduled appointments on Sunday"]}
Notes:
curlis a CLI tool used to send data to a server using URLs. By default,curlsends HTTP requests.The
-iflag is used to include the HTTP headers of the response in the output.In the address
127.0.0.1:5000:
127.0.0.1is the local IP address of your marchine.5000is the port the Express server listens on for incoming HTTP requests.