Intro to Express.js
Express.js is a web framework for Node.js server-side applications. Here’s a Hello World Express app.
const express = require("express")
const app = express()
const port = 3000
// When the path "/" is visited, send a page that says "Hello World!".
app.get("/", (req, res) => {
res.send("Hello World!")
})
// Run the app.
app.listen(port, () => {
console.log(`API listening at http://localhost:${port}`)
})
Visiting http://localhost:3000/
would present a page like this:
Some Definitions
- Backend Development: Has to do with working on server-side software, including databases, backend logic, application programming interfaces, architecture, and servers.
- Database: A file or service that stores data for an application.
- API: Application Programming Interface: A set of procedures enabling access to functionality and data.
- REST API: A RESTful API allows for client-server communication over HTTP, where resources are accessed via endpoints.
- Endpoints (URIs): Uniform Resource Identifiers. These are web addresses in an API that provide access to data. Endpoints can allow for different HTTP methods, such as GET, POST, PUT, PATCH, and DELETE, to perform operations on data (e.g.
http://example.com/users/
could have an endpoint to GET all users).
Example CRUD API
This app contains methods for Creating, Reading, Updating, and Deleting users. This sort of app is called a CRUD App and is a very popular architecture for managing data. Scroll down to see the endpoints and try them out.
Making a CRUD Express.js App
Fork the repl at https://replit.com/@buckldav/express-api-starter. This repl uses replit’s built-in Database to store data. All of the code below will be added in app.js
.
Types of Requests and Responses
These are common HTTP request and response methods and statuses.
Operation | HTTP Method | Successful Response | Failure Response |
---|---|---|---|
Create | POST | 201 Created | 400 Bad Request |
Read | GET | 200 OK | 404 Not Found |
Update | PUT | 200 OK | 404 Not Found |
Delete | DELETE | 204 No Content | 204 No Content |
Create User
All of the comments that start with #swagger
show up in the Swagger UI documentation (accessible at /docs
). Swagger UI is based on the OpenAPI schema and is a popular API client.
Post requests usually require a body (the data that you are creating on the database). The “schema” in the comments below is what will show up in the docs, but does not provide any validation (you would need to do that yourself). In this API, users are unique by username, so there is a bit of logic to check if the user already exists.
app.post("/users", jsonParser, async (req, res) => {
// #swagger.summary = "User Create"
// #swagger.description = "Create user route. Usernames must be unique."
/* #swagger.parameters['body'] = {
in: 'body',
description: 'Create a user',
schema: {
username: 'icanhazcheezburger',
age: 29,
about: ''
}
} */
// if user name exists in db, reject new user
const user = await db.get(req.body.username)
if (user) {
res.status(400).send("user already exists.")
} else {
await db.set(req.body.username, req.body)
res.status(201).send("user created.")
}
})
Read User
List all users
The list operation returns a list of all the keys.
app.get("/users", async (req, res) => {
// #swagger.summary = "User List"
// #swagger.description = "List all users."
const keys = await db.list()
res.status(200).send(JSON.stringify(keys))
})
Get one user by username
The :username
in the path indicates that whatever you put in there when sending a request to that endpoint will be stored in req.params.username
in the callback function. For example, if the request is sent to /users/david
, then req.params.username = "david"
.
app.get("/users/:username", async (req, res) => {
// #swagger.summary = "User Get"
// #swagger.description = "Get user by username."
const user = await db.get(req.params.username)
if (user) {
res.status(200).send(JSON.stringify(user))
} else {
res.status(404).send("user not found.")
}
})
Update User
Put requests are a complete update of an database entry. This means that the body for the Update operation is the same as the body for the Create operation. For a partial update, (i.e., just updating the username), you could use a patch request, but put requests are more common.[citation needed]
app.put("/users/:username", async (req, res) => {
// #swagger.summary = "User Update"
// #swagger.description = "Update user by username."
/* #swagger.parameters['body'] = {
in: 'body',
description: 'Update a user',
schema: {
username: 'icanhazcheezburger',
age: 29,
about: ''
}
} */
// if user name exists in db, reject new user
const user = await db.get(req.body.username)
if (user) {
await db.set(req.body.username, req.body)
res.status(200).send("user created.")
} else {
res.status(404).send("user doesn't exist.")
}
})
Delete User
app.delete("/users/:username", async (req, res) => {
// #swagger.summary = "User Delete"
// #swagger.description = "Delete user by username."
const user = await db.delete(req.params.username)
res.status(204).send("user deleted.")
})
To delete all users (clear the database), in replit’s Shell tab, run npm run clear
.
Further Reading
- Express.js Routing: https://expressjs.com/en/guide/routing.html