Last Sync: 2022-08-30 10:00:04

This commit is contained in:
tactonbishop 2022-08-30 10:00:04 +01:00
parent c5d152e2c4
commit 50dadb7340
11 changed files with 104 additions and 102 deletions

View file

@ -0,0 +1,3 @@
h1, h2, h3, h4, h5, h6, p {
font-family: 'Inter';
}

View file

@ -1,10 +0,0 @@
---
categories:
- Programming Languages
- Databases
tags: [backend, node-js, REST, APIs, mongo-db]
---
# RESTful API with Node, Express and MongoDB: Integrating the database
So far we have set up the application and a `/api/courses` route which handles requests for RESTful API operations on an array of course objects. We now want to have the endpoints operate on a MongoDB `courses` collection rather than the array.

View file

@ -0,0 +1,10 @@
---
categories:
- Programming Languages
- Databases
tags: [backend, node-js, REST, APIs, mongo-db]
---
# Creating a RESTful API: Integrating the database
So far we have set up the application and an `/api/courses` route which handles requests for RESTful API operations on a local array of course objects. We now want to have the endpoints operate on a MongoDB `courses` collection rather than the array.

View file

@ -4,7 +4,7 @@ categories:
tags: [backend, node-js, REST, APIs]
---
# RESTful API with Node, Express and MongoDB: Introduction
# Creating a RESTful API: Introduction
We are going to use Express to create a [RESTful API](/Databases/RESTful_APIs.md) in Node.js.
@ -27,15 +27,15 @@ We will mainly work with the following array of objects:
const courses = [
{
id: 1,
name: 'First course',
name: "First course",
},
{
id: 2,
name: 'Second course',
name: "Second course",
},
{
id: 3,
name: 'Third course',
name: "Third course",
},
];
```
@ -45,19 +45,19 @@ const courses = [
We first create an instance of Express within `index.js`. This will be the main coordinating file and we will aim to minimise the amount of business logic we have in this file. It should really just be for initialization and managing [middleware](/Programming_Languages/NodeJS/Architecture/Middleware.md).
```js
const express = require('express');
const express = require("express");
const app = express();
```
## Routing
We are going to receive all our HTTP requests at the path `/api/courses` and we will place all code related to this route in a dedicated Node module (`routes/courses.js`).
We are going to receive all our HTTP requests at the path `/api/courses` and we will place all code related to this route in a dedicated Node module (`routes/courses.js`) rather than within `index.js` which should remain as uncluttered as possible.
First we need to declare this in `index.js`:
```js
const courses = require('./routes/courses');
app.use('/api/courses', courses);
const courses = require("./routes/courses");
app.use("/api/courses", courses);
```
Then within `courses.js` we instantiate an express router that `app.js` will route to:
@ -69,7 +69,7 @@ const router = express.Router();
Our REST requests will all follow the following pattern:
```js
router.get('/', (req, res) => {
router.get("/", (req, res) => {
res.send(courses);
});
```
@ -78,10 +78,10 @@ We target the `Router` instance we created and all paths will be expressed as `/
## Create listener
With the routes setup and the Express instance created we can now listen for requests:
With the routing established and the Express instance created we can now listen for requests:
```js
app.listen(3000, () => console.log('Listening on port 30000...'));
app.listen(3000, () => console.log("Listening on port 30000..."));
```
## REST endpoints

View file

@ -4,7 +4,7 @@ categories:
tags: [backend, node-js, REST, APIs]
---
# RESTful API with Node, Express and MongoDB: `GET` requests
# Creating a RESTful API: `GET` requests
With our GET request we will simply return the array of course objects.
@ -12,7 +12,7 @@ We create an [event emitter](Events%20module.md#event-emitters) and listener tha
```js
// Return a value as response from specified URI
router.get('/', (req, res) => {
router.get("/", (req, res) => {
res.send(courses);
});
```
@ -28,7 +28,7 @@ We can now call the endpoint:
```js
const getAllCourses = async () => {
try {
const resp = await axios.get('http://localhost:3000/api/courses');
const resp = await axios.get("http://localhost:3000/api/courses");
console.log(resp.data);
} catch (err) {
console.error(err);
@ -42,9 +42,9 @@ Returns:
```js
[
{id: 1, name: 'First course'},
{id: 2, name: 'Second course'},
{id: 3, name: 'Third course'},
{ id: 1, name: "First course" },
{ id: 2, name: "Second course" },
{ id: 3, name: "Third course" },
];
```
@ -53,7 +53,7 @@ Returns:
The previous example serves the entire set of our data. But we will also need to retrieve specific values, we do this by adapting the GET callback to accept parameters. These parameters will correspond to the specific entry in our main data array.
```js
router.get('/:id', (req, res) => {
router.get("/:id", (req, res) => {
res.send(req.params.id);
});
```
@ -63,9 +63,9 @@ We use the `:` symbol in the URI to indicate that we looking to parse for a spec
The block above is the most basic format but we would want to add some kind of error handling, for example:
```js
router.get('/:id', (req, res) => {
router.get("/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course) res.status(404).send('A course with the given ID was not found');
if (!course) res.status(404).send("A course with the given ID was not found");
res.send(course);
});
```

View file

@ -4,12 +4,12 @@ categories:
tags: [backend, node-js, REST, APIs]
---
# RESTful API with Node, Express and MongoDB: `POST` requests
# Creating a RESTful API: `POST` requests
To demonstrate the handling of POST requests, we will create a handler that add a new element to the array of courses.
```js
router.post('/', (req, res) => {
router.post("/", (req, res) => {
const course = {
id: courses.length + 1,
name: req.body.name,
@ -32,7 +32,7 @@ To execute the PUT request from the frontend:
```js
const addCourse = async (newCourse) => {
try {
const resp = await axios.post('http://localhost:3000/api/courses', {
const resp = await axios.post("http://localhost:3000/api/courses", {
name: newCourse,
});
console.log(resp.data);
@ -40,7 +40,7 @@ const addCourse = async (newCourse) => {
console.error(err);
}
};
addCourse('Biology and Life Sciences');
addCourse("Biology and Life Sciences");
```
Which returns:
@ -61,7 +61,7 @@ function validateCourse(course) {
name: Joi.string().min(3).required(),
});
const {error} = schema.validate(course);
const { error } = schema.validate(course);
return error;
}
```
@ -69,13 +69,14 @@ function validateCourse(course) {
We can then add the validation as part of our general error handling:
```js
router.post('/', (req, res) => {
router.post("/", (req, res) => {
const course = {
id: courses.length + 1,
name: req.body.name,
};
const {error} = schema.validate(req.body);
if (error) return error.details.map((joiErr) => res.status(400).send(joiErr.message));
const { error } = schema.validate(req.body);
if (error)
return error.details.map((joiErr) => res.status(400).send(joiErr.message));
courses.push(course);
res.send(course);

View file

@ -4,18 +4,20 @@ categories:
tags: [backend, node-js, REST, APIs]
---
# RESTful API with Node, Express and MongoDB: `PUT` requests
# Creating a RESTful API: `PUT` requests
To demonstrate the handling of PUT requests, we will create a handler that updates an element in the course array, based on its `id` and return the updated entry:
```js
router.put('/:id', (req, res) => {
router.put("/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course) return res.status(404).send('A course with the given ID was not found');
const {error} = validateCourse(req.body);
if (!course)
return res.status(404).send("A course with the given ID was not found");
const { error } = validateCourse(req.body);
if (error) return error.details.map((joiErr) => res.status(400).send(joiErr.message));
if (error)
return error.details.map((joiErr) => res.status(400).send(joiErr.message));
course.name = req.body.name;
res.send(course);
@ -27,7 +29,7 @@ Our request:
```js
const updateCourse = async (courseChange) => {
try {
const resp = await axios.put('http://localhost:3000/api/courses/1', {
const resp = await axios.put("http://localhost:3000/api/courses/1", {
name: courseChange.name,
});
console.log(resp.data);
@ -36,7 +38,7 @@ const updateCourse = async (courseChange) => {
}
};
updateCourse({name: 'A new course'});
updateCourse({ name: "A new course" });
```
Returns:

View file

@ -4,12 +4,13 @@ categories:
tags: [backend, node-js, REST, APIs]
---
# RESTful API with Node, Express and MongoDB: `DELETE` requests
# Creating a RESTful API: `DELETE` requests
```js
router.delete('/:id', (req, res) => {
router.delete("/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course) return res.status(404).send('A course with the given ID was not found');
if (!course)
return res.status(404).send("A course with the given ID was not found");
courses.indexOf(course);
courses.splice(index, 1);

View file

@ -4,19 +4,19 @@ categories:
tags: [backend, node-js, REST, APIs]
---
# Full example
# Creating a RESTful API: Full example
```js
const express = require('express');
const express = require("express");
const app = express(); // convention to name Express as the app
const port = process.env.PORT || 3000;
const Joi = require('joi');
const helmet = require('helmet');
const morgan = require('morgan');
const courses = require('./routes/courses');
const Joi = require("joi");
const helmet = require("helmet");
const morgan = require("morgan");
const courses = require("./routes/courses");
// Routes
app.use('/api/courses', courses);
app.use("/api/courses", courses);
// Middlewear
app.use(express.json());
@ -25,20 +25,20 @@ app.use(helmet());
const courses = [
{
id: 1,
name: 'First course',
name: "First course",
},
{
id: 2,
name: 'Second course',
name: "Second course",
},
{
id: 3,
name: 'Third course',
name: "Third course",
},
];
if (app.get('env') === 'development') {
app.use(morgan('common'));
if (app.get("env") === "development") {
app.use(morgan("common"));
}
app.listen(port, () => console.log(`Listening on ${port}`));
@ -48,30 +48,35 @@ function validateCourse(course) {
name: Joi.string().min(3).required(),
});
const {error} = schema.validate(course);
const { error } = schema.validate(course);
return error;
}
/**
* Note that the following request handlers would not be stored in `index.js` they would be in their dedicated routing file.
**/
// Return all data from API
courses.get('/', (req, res) => {
courses.get("/", (req, res) => {
res.send(courses);
});
// Return a specific value
courses.get('/:id', (req, res) => {
courses.get("/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course) res.status(404).send('A course with the given ID was not found');
if (!course) res.status(404).send("A course with the given ID was not found");
res.send(course);
});
// Add a new course
courses.post('/', (req, res) => {
courses.post("/", (req, res) => {
const schema = Joi.object({
name: Joi.string().min(3).required(),
});
const {error} = schema.validate(req.body);
if (error) return error.details.map((joiErr) => res.status(400).send(joiErr.message));
const { error } = schema.validate(req.body);
if (error)
return error.details.map((joiErr) => res.status(400).send(joiErr.message));
const course = {
id: courses.length + 1,
@ -82,22 +87,25 @@ courses.post('/', (req, res) => {
});
// Update a course
courses.put('/:id', (req, res) => {
courses.put("/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course) return res.status(404).send('A course with the given ID was not found');
const {error} = validateCourse(req.body);
if (!course)
return res.status(404).send("A course with the given ID was not found");
const { error } = validateCourse(req.body);
if (error) return error.details.map((joiErr) => res.status(400).send(joiErr.message));
if (error)
return error.details.map((joiErr) => res.status(400).send(joiErr.message));
course.name = req.body.name;
res.send(course);
});
// Delete a course
courses.delete('/:id', (req, res) => {
courses.delete("/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course) return res.status(404).send('A course with the given ID was not found');
if (!course)
return res.status(404).send("A course with the given ID was not found");
courses.indexOf(course);
courses.splice(index, 1);

View file

@ -1,12 +1,12 @@
---
tags:
- Programming_Languages
- backend
- node-js
- validation
categories:
- Programming Languages
tags: [backend, node-js, REST, APIs, validation]
---
We can provide server-side validation for our projects by using a **schema validator**. This is a program that declaratively parses the JSON values received as requests from the client. This makes it easy to systematically validate the data that we receive from any HTTP requests where the client sends a body to the endpoint.
# Creating a RESTful API: Validation
We can provide server-side validation for our projects by using a **schema validator**. This is a program that declaratively parses the JSON values received as requests from the client. This makes it easy to systematically validate the data that we receive from any HTTP requests where the client sends a body to the endpoint.
One of the most popular schema validators for NodeJS is [joi](https://www.npmjs.com/package/joi).
@ -15,24 +15,22 @@ One of the most popular schema validators for NodeJS is [joi](https://www.npmjs.
Let's say we have a POST request that expects a single field as the body that must be a string and greater than two characters long. First we define our schema:
```js
const schema = Joi.object({
name: Joi.string().min(3).required(),
});
const { error } = schema.validate(req.body);
const schema = Joi.object({
name: Joi.string().min(3).required(),
});
const { error } = schema.validate(req.body);
```
The `schema` variable is an object whose keys should match those of the intended request body. Instead of actual values we provide Joi's in-built validators, concatenated as necessary. We then store the results of the validation in a variable.
The `schema` variable is an object whose keys should match those of the intended request body. Instead of actual values we provide Joi's in-built validators, concatenated as necessary. We then store the results of the validation in a variable.
Next we add handling in the case of errors:
````js
```js
if (error) {
error.details.map((joiErr) => res.status(400).send(joiErr.message));
return;
}
````
error.details.map((joiErr) => res.status(400).send(joiErr.message));
return;
}
```
We loop through the error array and return 400s as the response if they are found. If there are no errors, the Joi object will return `undefined`.

View file

@ -1,11 +0,0 @@
* {
font-family: 'Inter';
}
pre, code {
font-family: 'Liga Liberation Mono' !important;
}
code {
font-family: 'Liga Liberation Mono' !important;
}