In JavaScript Essentials: Part 7, I hinted that we'd look into developing APIs ([0] [1]) and this is where we start. We will have a taste of what it takes to develop a simple API to track expenditures.
For this expense tracker, we will need to keep track of the item purchased, the amount and the date the item was purchased. An expense will look like this:
{ "name": "Legion Tower 7i Gen 8 (Intel) Gaming Desktop", "amount": 2099.99, "date": "2024-31-12" }
At this point, since a real database has not been added yet, we can use a list (an array) to hold the data that we will create. In this excerpt, we will go through each major concept surrounding creating APIs and add some ways to improve this app later.
Know that we will be building on top of this project, so, keep it clean and explore, experiment, fidget, etc as much as you can.
As usual, we need a fresh working environment for each project, so we will begin by creating and initializing a Node JS project. If you are not sure, check out JavaScript Essentials: Part 6 (Mastermind in Javascript).
Now we have to create the parent folder for our API by doing:
# create a folder for the project at the path of your choice mkdir expense-tracker-simple-api # open the project with vscode # code expense-tracker-simple-api code -r expense-tracker-simple-api # open the built-in terminal and init node npm init -y # this should create the package.json file # create the entry file, index.js echo "console.log(\"expense-tracker-simple-api\");" > index.js # run the index.js file node index.js
All we are doing with this script is quite direct.
An alternative is to go to wherever you want to create this folder and create it over there then open the folder in vscode and init node project - check out, JavaScript Essentials: Part 6 (Mastermind in Javascript).
At this point, we need to install a nodejs package called express. Express is a library that will help us create our APIs.
We can install this package by running, npm i express. This should modify the package.json file, and create the package-lock.json file and node_modules folder. Consult the excerpt, What is Nodejs, for further understanding and information regarding how to use npm to install packages.
In our index.js file, we can add this code.
console.log("expense-tracker-simple-api"); // import the express lib const express = require("express"); // create an express application const app = express(); // create a GET request on the base endpoint app.get("/", (req, res) => res.send("Hello world")); // create a server that listens to requests on port 3000 app.listen(3000, () => console.log(`Api running on ${"http://localhost:3000"}`) );
All that we did was to create an express application, use the application to create a GET request to send a message and let the application listen in on requests coming from port 3000.
const app = express();
Creates an express application (? that how to create an express application)
app.get("/", (req, res) => res.send("Hello world"));
We used the express application instance to create a GET request. app has several methods and properties that include the HTTP methods. Check out the except about http methods here.
{ "name": "Legion Tower 7i Gen 8 (Intel) Gaming Desktop", "amount": 2099.99, "date": "2024-31-12" }
We used the express application to listen on a port and used an arrow function to tell us, tell developers, that our application is running. For the port, we can alter it to another port of our choice. However, some special ports are already meant or used for some particular task and they are well known in the community and as such servers as default when such applications or programs are running on our PC. Check these out - 0 1 2
Note: There are some that are no-go because your system comes with them and there are some that come with applications we install such as some servers and databases and others. Fret not, when we use a port that is already in use, our application will let us know. All we have to do is add one or subtract one. No sweat.
To create a GET request, use app.get(...), for POST use app.post(...) and so on. For each route we want to create, the app.SomeMethod(...), will take a route or path, indicating which resource the user client desires or action they want to execute. As part of the route, it can take at least one request handler. This means we can have app.SomeMethod(path, hanlder1, handler2, ..., handlern);.
For the GET request above, the path or route is / (a string) and the single request handler we have is (req, res) => res.send("Hello world"), a handler function (a callback or a simple arrow function). The request handlers can be middleware and controllers.
A request handler normally takes in two arguments, the request and the response which are shortened as req and res respectively (you don't have to call them that but the first is always request and the second is the response). The request holds the data (or some information) about who made the request and what they want. The response is a means to provide feedback to the user who made the request. In this case, we send "Hello world" when the client makes a GET request to the / path.
Here you notice that the client and user are interchangeable, in the sense of which device makes the HTTP request to our api server and not a user, as in a user account.
Usually, the request handler will take a third argument which will point to the next handler after the previous handler has done its job. We call this next. It looks more or less like:
# create a folder for the project at the path of your choice mkdir expense-tracker-simple-api # open the project with vscode # code expense-tracker-simple-api code -r expense-tracker-simple-api # open the built-in terminal and init node npm init -y # this should create the package.json file # create the entry file, index.js echo "console.log(\"expense-tracker-simple-api\");" > index.js # run the index.js file node index.js
The next argument is useful, it points to the request handler and at times it takes an argument, an error. We will implement an error handler to generally handle errors we did not handle or errors that we "pass" to the next request handler.
Now let's cancel the running nodejs process that was running (in the terminal), using control c. Then run it again. This time with the updated content from Create a simple server and a GET route section, we should see an output in the console (terminal) similar to,
{ "name": "Legion Tower 7i Gen 8 (Intel) Gaming Desktop", "amount": 2099.99, "date": "2024-31-12" }
Open http://localhost:3000 in the browser. What do you see? A text saying, Hello world ?
Rome was not built in one day as the saying goes. The same applies to software development. Maybe here what we mean is that we will gradually add more features as we develop and in this continuous process, it becomes irritating to start and stop the server all the time.
Go on, add another GET request (route) with /hello path and a request handler that says something you would want to say. Be happy.
You'd have to restart the server (the running nodejs process) and visit, http://localhost:3000/hello in the browser to see the response from that endpoint.
This,GET http://localhost:3000/hello, is an endpoint. You share this with api consumers. Among ourselves, we say route, because we don't have to know the whole URL (including protocol - http, domain - localhost, port - 3000, and path - /hello). Route is METHOD PATH, more or less, GET /hello.
On macOS or Windows, we can do node --watch index.js or we can look out for changes not just in our entry file but in the whole folder path by, node --watch-path=./ index.js to watch for changes in the file path and also the file itself.
Currently, this is the content of my package.json file:
# create a folder for the project at the path of your choice mkdir expense-tracker-simple-api # open the project with vscode # code expense-tracker-simple-api code -r expense-tracker-simple-api # open the built-in terminal and init node npm init -y # this should create the package.json file # create the entry file, index.js echo "console.log(\"expense-tracker-simple-api\");" > index.js # run the index.js file node index.js
We can add a script called dev under the script section.
console.log("expense-tracker-simple-api"); // import the express lib const express = require("express"); // create an express application const app = express(); // create a GET request on the base endpoint app.get("/", (req, res) => res.send("Hello world")); // create a server that listens to requests on port 3000 app.listen(3000, () => console.log(`Api running on ${"http://localhost:3000"}`) );
We can stop the running server with control c and now execute npm run dev. This will monitor saved changes in our files and reload the server.
So if this is not working for you, we have an alternative. We will install nodemone, npm i nodemon -g. We'd use it everywhere as a utility tool so we don't have to install it as part of our packages. We can watch changes by executing, nodemon index.js. There are cases where this won't work and when it doesn't, dom nodemon --exec node index.js
We can modify our dev script to use nodemon by,
const app = express();
At this point, you can freely modify your .js files and on save, the server will restart to reload the load changes applied.
We have already created a GET request. In this section, we will look into what each method means briefly since we have discussed them at length in Request and Response.
In this application, we are only serving one kind of resource to our clients, and that is expenditures. Assuming we are serving several resources, then we'd group requests under each resource.
So let's say we have user and expenditure, we have GET, POST, PUT, DELETE, etc for both users and expenditures.
For now, we'd use the /expenditures route to represent the expenditure resource.
GET: This means we will create a route to list, get all, fetch all, etc the records we have on expenditures. We can have a GET request that fetches one of the records. We have created a similar GET
POST: The post method is often used for creating resources
PUT: The put method is used to update recourses
DELETE: The delete method is used to delete resource
Now we can add the following lines of code to the index.js file but above app.listen(3000,...).
{ "name": "Legion Tower 7i Gen 8 (Intel) Gaming Desktop", "amount": 2099.99, "date": "2024-31-12" }
When you save your file, do you notice the logs in the terminal? Test the GET route for the expenditure in the browser.
We can only run the GET requests in the browser. We will discuss api clients next.
An API client in this context is a tool, a program or an application that is used to interact (consume, integrate, or test) APIs. Most commonly used are Postman, Insomnia, curl, etc...
In vscode and alongside some other IDEs, there is an extension that provides extensions to api clients. vscode has some of these extensions for that matter. However, we will be considering an api client known as REST Client. For our use case, it will be simpler to use Rest Client as such don't fret. We are covered.
Note: postman or any other api client of your choice is okay to use
{ "name": "Legion Tower 7i Gen 8 (Intel) Gaming Desktop", "amount": 2099.99, "date": "2024-31-12" }
# create a folder for the project at the path of your choice mkdir expense-tracker-simple-api # open the project with vscode # code expense-tracker-simple-api code -r expense-tracker-simple-api # open the built-in terminal and init node npm init -y # this should create the package.json file # create the entry file, index.js echo "console.log(\"expense-tracker-simple-api\");" > index.js # run the index.js file node index.js
console.log("expense-tracker-simple-api"); // import the express lib const express = require("express"); // create an express application const app = express(); // create a GET request on the base endpoint app.get("/", (req, res) => res.send("Hello world")); // create a server that listens to requests on port 3000 app.listen(3000, () => console.log(`Api running on ${"http://localhost:3000"}`) );
As an exercise, create the request for the expenditure endpoints. Refer to when you are having any difficulties. We have more to do.
At this point, I don't have to stress that we'd be using JSON to communicate with our api using the API client.
As mentioned earlier, we can pass data to our api using the body, header, or URL of our request. We have seen how to pass data via the request body and the header (We will look into passing some specific data at another time). Check the POST request created. What we have not looked at is how to pass data as part of the URL.
Let's say we want to read an expenditure which has an id of 4, we can pass the add a parameter (as part of the URL) as, /expenditures/2. For the request which will be handling this requirement, we do, /expenditures/:id, where :id refers to the id of the expenditure. Assuming it is something else other than an id, let's say a name, then we'd do :name. Express will process this a provide us with a means to extract this value without sweat.
Now, for a query string, the idea is similar to request parameters however, it comes after a question, followed by a key1=value1&key2=value2...&keyN=valueN, where the key is the identifier of the value you want to pass. A very direct example is the REST-Client URL, https://marketplace.visualstudio.com/items?itemName=humao.rest-client. The question mark marks the beginning of the query string and whatever follows it maps a key to a value. Eg: ?itemName=humao.rest-client.
It will be a good time to test all your api endpoints and experience playing with it.
Now we are going to process a request that passes data using the request body - The POST endpoint.
{ "name": "Legion Tower 7i Gen 8 (Intel) Gaming Desktop", "amount": 2099.99, "date": "2024-31-12" }
The request object has a property, body, and on this property, are the values that we passed in the request body of our request - req.body.
This is the request that will be running.
# create a folder for the project at the path of your choice mkdir expense-tracker-simple-api # open the project with vscode # code expense-tracker-simple-api code -r expense-tracker-simple-api # open the built-in terminal and init node npm init -y # this should create the package.json file # create the entry file, index.js echo "console.log(\"expense-tracker-simple-api\");" > index.js # run the index.js file node index.js
This is our endpoint implementation that will just log the request body to the console.
console.log("expense-tracker-simple-api"); // import the express lib const express = require("express"); // create an express application const app = express(); // create a GET request on the base endpoint app.get("/", (req, res) => res.send("Hello world")); // create a server that listens to requests on port 3000 app.listen(3000, () => console.log(`Api running on ${"http://localhost:3000"}`) );
What happened? We had the usual response but... undefined was logged in the console. Well, it means everything is alright however, our api server doesn't know that it should parse the incoming as json. Remember json?
Let's add this one line below the const app = express(); which should solve parse the incoming data as json.
const app = express();
Now, let's test the POST endpoint again. What did you get this time? Did you get something similar to this?
app.get("/", (req, res) => res.send("Hello world"));
Now you know how to get the data from the body. Now as exercise, destructure the body and pass an object in the response. So instead of logging it, return it as the response.
We will create a new GET endpoint to read an expenditure by id.
This will be our API request:
app.listen(3000, () => console.log(`Api running on ${"http://localhost:3000"}`) );
The request object has a property, params and on this property, are the values that we passed in the request param of our request - req.params.
Now the implementation will be similar to what we have done so far but a little different.
app.get("/", (req, res, next) => ...);
We can access the id directly too. I hope you noticed that the :id key or name passed as part of the route matches the key in the logged object. Try renaming the params key in the route and see the output logged.
For the query strings, there is a property on the request object, query, which exposes the query strings passed.
To demonstrate this will pass a query string to filter the records to return. This endpoint should do enough.
expense-tracker-simple-api Api running on http://localhost:3000
Now the implementation will be something similar to:
{ "name": "expense-tracker-simple-api", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "express": "^4.21.2" } }
Now run your api and check your logs. Experiment with this.
At this point, we are not connecting to a database yet so we have to manipulate data from memory. What we intend to do is create an array, add to the array, update an element in it, and remove an element. It sounds feasible as such that's what we are going to do.
We will be doing some modifications to some previously written code as such, feel free to alter your too. The final excerpt will be shared at the end.
Let's create an array of expenditures (dummy data) below const express = require("express");
{ "name": "Legion Tower 7i Gen 8 (Intel) Gaming Desktop", "amount": 2099.99, "date": "2024-31-12" }
The current endpoint returns just a message using the res.send(message) but what we want to return is json. Though the .send can also take in an object or json, we will use res.json(obj).
I didn't mention but the default status code returned is 200. Have you noticed that? Except that another occurs or there is an issue with the request, the status code remains the same. There is a section under status codes, glance through.
We can alter the status code by passing the desired status code in res.status(desireStatusCode).json(obj). I will maintain the 200 status code throughout.
Make sure the server is still running
We can pass the list of expenditures directly.
# create a folder for the project at the path of your choice mkdir expense-tracker-simple-api # open the project with vscode # code expense-tracker-simple-api code -r expense-tracker-simple-api # open the built-in terminal and init node npm init -y # this should create the package.json file # create the entry file, index.js echo "console.log(\"expense-tracker-simple-api\");" > index.js # run the index.js file node index.js
What was the response received? Check the status code as well as the response payload.
From experience and also to avoid ambiguity, I prefer to return status code 200 by default and have a either success property, message or data property to return a message or requested resource. By default, when status is false, message will be passed else, message or data may be passed.
console.log("expense-tracker-simple-api"); // import the express lib const express = require("express"); // create an express application const app = express(); // create a GET request on the base endpoint app.get("/", (req, res) => res.send("Hello world")); // create a server that listens to requests on port 3000 app.listen(3000, () => console.log(`Api running on ${"http://localhost:3000"}`) );
We need to display the id (index of each row)
const app = express();
app.get("/", (req, res) => res.send("Hello world"));
Why was the filter done after the mapping?
app.listen(3000, () => console.log(`Api running on ${"http://localhost:3000"}`) );
Does this implementation hint to you about, Why was the filter done after the mapping? ?
app.get("/", (req, res, next) => ...);
expense-tracker-simple-api Api running on http://localhost:3000
{ "name": "expense-tracker-simple-api", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "express": "^4.21.2" } }
We have covered the root of most API developments. This project is as basic as it comes. Relax and glance through again. There is more to look into such as
crud api = create, list, read, update, and delete. It is how you approach these problems.
To-Do List
Calculator
Currency Converter
You are converting from one currency to another. Do for as many currencies as you can (3 is enough)
Know that the excess was removed.
{ "name": "Legion Tower 7i Gen 8 (Intel) Gaming Desktop", "amount": 2099.99, "date": "2024-31-12" }
API requests
# create a folder for the project at the path of your choice mkdir expense-tracker-simple-api # open the project with vscode # code expense-tracker-simple-api code -r expense-tracker-simple-api # open the built-in terminal and init node npm init -y # this should create the package.json file # create the entry file, index.js echo "console.log(\"expense-tracker-simple-api\");" > index.js # run the index.js file node index.js
The above is the detailed content of Basic CRUD API with express. For more information, please follow other related articles on the PHP Chinese website!