diff --git a/Databases/MongoDB/Create_collections_and_documents.md b/Databases/MongoDB/Create_collections_and_documents.md index 32b250a..af12967 100644 --- a/Databases/MongoDB/Create_collections_and_documents.md +++ b/Databases/MongoDB/Create_collections_and_documents.md @@ -1,7 +1,7 @@ --- categories: - Databases -tags: [mongo-db, node-js, mongoose] +tags: [mongo_db, node_js, mongoose] --- # Create collections and documents with Mongoose @@ -46,7 +46,7 @@ Note that we set our validation criteria as the second property for each schema Note that this validation only occurs at the level of Mongoose. There is no mechanism for running the validation within the database itself. This is one of the ways that Mongo differs from an SQL database where you would be able to set the property of `required` for a cell in the table. If we didn't set any validation via Mongoose, Mongo would accept whatever we sent to it. -What is the relationship between this Mongoose validation and the [Joi](/Programming_Languages/NodeJS/REST_APIs/Validation.md) validation that we use when validating API requests in Node/Express? They complement each other. We use Joi to validate the client request to the API. If this is valid, the process would then move onto the next stage which would be transforming the data from a REST request to a database request implemented through Mongoose. +What is the relationship between this Mongoose validation and the [Joi](/Programming_Languages/NodeJS/REST_APIs/Validation.md) validation that we use when validating API requests in Node/Express? They complement each other. We use Joi to validate the client request to the API. If this is valid, the process would then move onto the next stage which would be transforming the data from a REST request to a database request implemented through Mongoose, where a second layer of validation would appy. We would also have validation on the client-side. #### Validator functions diff --git a/Programming_Languages/NodeJS/Architecture/Structuring_Express_applications.md b/Programming_Languages/NodeJS/Architecture/Structuring_Express_applications.md index 7246097..6a47ea7 100644 --- a/Programming_Languages/NodeJS/Architecture/Structuring_Express_applications.md +++ b/Programming_Languages/NodeJS/Architecture/Structuring_Express_applications.md @@ -1,72 +1,29 @@ --- -tags: - - Programming_Languages - - backend - - node-js - - express - - rest +categories: + - Programming Languages +tags: [backend, node-js, REST, APIs, mongo-db] --- # Structuring Express applications -In the examples provided in [Creating a RESTFul API with Express](./Create_RESTful_API_with_Express.md), we had all our API routes being served via the `app` variable in `index.js`. This was very cluttered. It is best to structure your application by having a dedicated `/routes/` directory and within this, a dedicated file for each route. - In general, the following application structure works well: ``` \ routes\ + models\ middlewear\ config\ public\ + index.js ``` -We will demonstrate this for the `/courses` route. +- **`routes/`** -## Using the `Router` object -Previously we instantiated an Expres instance with `app`: + - This should house all our RESTful API endpoints for a given resource which we define as a module. For instance if we are handling API requests to `/api/courses/` we would have a module at `/routes/courses.js` -```js -const express = require('express') -const app = express() -``` -When we partition our routes we can no longer do this, since we are only serving a single route, not the whole app, so we instead use Express's `Router` object: - -```js -const router = express.Router() -``` - -And then update all references to `app` with `router`, e.g: -```js -router.get('/api/courses', (req, res) => { - res.send(courses) -}) -``` - -We would then export: - -``` -module.exports = router -``` - -Now all the functions pertaining to that route are handled in a dedicated file. - -We would then import it within the main entry file, `index.js` and invoke it. The invocation syntax here is the same as when we utilise [middlewear](./Middleware.md): - -```js -const courses = require('./routes/courses') -app.use('/api/courses', courses); -``` - -Given that we define the `courses` route in `index.js` as being served from `/api/courses`, we no longer have to use that slug within `courses.js`, we can change all our routes to `/` , e.g. - -```js -router.get('/', (req, res) => { - res.send(courses) -}) -``` -````js -app.get("/:id", (req, res) => { - res.send(req.params.id); -}); +- **`models/`** + - Here we define our database schemas: definitions for the structure of the data objects we want to return from the database. + - In the case of Mongo we would have a file for each of our Mongoose models which will belong to an overall collection of which there are document instances. + - Our model files should map onto our route modules; there should generally be at least one model file for each module. The model stands as an abstract representation of the data that we will be handling with our request paths in the routes file. diff --git a/Programming_Languages/NodeJS/REST_APIs/05_ Integrating_the_database.md b/Programming_Languages/NodeJS/REST_APIs/05_ Integrating_the_database.md new file mode 100644 index 0000000..3105d5f --- /dev/null +++ b/Programming_Languages/NodeJS/REST_APIs/05_ Integrating_the_database.md @@ -0,0 +1,10 @@ +--- +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. diff --git a/Programming_Languages/NodeJS/REST_APIs/0_Introduction.md b/Programming_Languages/NodeJS/REST_APIs/0_Introduction.md index a65ed6c..ef82fd3 100644 --- a/Programming_Languages/NodeJS/REST_APIs/0_Introduction.md +++ b/Programming_Languages/NodeJS/REST_APIs/0_Introduction.md @@ -1,52 +1,97 @@ --- -tags: - - Programming_Languages - - backend - - node-js - - express - - REST - - apis +categories: + - Programming Languages +tags: [backend, node-js, REST, APIs] --- -# Creating a REST API with Node and Express: Introduction +# RESTful API with Node, Express and MongoDB: Introduction -We are going to use Express to create a [RESTful API](/Databases/RESTful_APIs.md) in Node.js. +We are going to use Express to create a [RESTful API](/Databases/RESTful_APIs.md) in Node.js. ## Request types Express provides us with methods corresponding to each of the [HTTP request types](/Databases/HTTP_request_types.md): -* `app.get()` -* `app.post()` -* `app.put()` -* `app.delete()` - -## Creating an Express instance - -````js -const express = require('express') -const app = express() -```` +- `.get()` +- `.post()` +- `.put()` +- `.delete()` ## Our data set -> Typically when you create a RESTful API you are going to be returning data from a database. For simplicity we are just going simulate this with a simple data array so that we can focus on the Express syntax rather than database handling. +> Typically when you create a RESTful API you are going to be returning data from a database. For simplicity we are just going simulate this with a simple data array so that we can focus on the Express syntax rather than database handling. Later we will integrate this with a [MongoDB database](/Programming_Languages/NodeJS/REST_APIs/05_%20Integrating_the_database.md). We will mainly work with the following array of objects: -````js +```js 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', }, ]; -```` \ No newline at end of file +``` + +## Creating an Express instance + +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 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`). + +First we need to declare this in `index.js`: + +```js +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: + +```js +const router = express.Router(); +``` + +Our REST requests will all follow the following pattern: + +```js +router.get('/', (req, res) => { + res.send(courses); +}); +``` + +We target the `Router` instance we created and all paths will be expressed as `/` since the app will route by default to `/courses/api` from `index.js` + +## Create listener + +With the routes setup and the Express instance created we can now listen for requests: + +```js +app.listen(3000, () => console.log('Listening on port 30000...')); +``` + +## REST endpoints + +We can now proceed to set up our RESTful endpoints: + +[GET requests](/Programming_Languages/NodeJS/REST_APIs/1_GET.md) + +[POST requests](/Programming_Languages/NodeJS/REST_APIs/2_POST.md) + +[PUT requests](/Programming_Languages/NodeJS/REST_APIs/3_PUT.md) + +[DELETE requests](/Programming_Languages/NodeJS/REST_APIs/4_DELETE.md) diff --git a/Programming_Languages/NodeJS/REST_APIs/1_GET.md b/Programming_Languages/NodeJS/REST_APIs/1_GET.md index 52484ea..6a3916d 100644 --- a/Programming_Languages/NodeJS/REST_APIs/1_GET.md +++ b/Programming_Languages/NodeJS/REST_APIs/1_GET.md @@ -1,33 +1,26 @@ --- -tags: - - Programming_Languages - - backend - - node-js - - express - - REST - - apis +categories: + - Programming Languages +tags: [backend, node-js, REST, APIs] --- -# Creating a REST API with Node and Express: GET requests +# RESTful API with Node, Express and MongoDB: `GET` requests With our GET request we will simply return the array of course objects. -We create an [event emitter](Events%20module.md#event-emitters) and listener that listens for GET requests on a specified port and sends data in response to requests. +We create an [event emitter](Events%20module.md#event-emitters) and listener that listens for GET requests on a specified port and sends data in response to requests. ```js // Return a value as response from specified URI -app.get('/api/courses', (req, res) => { - res.send(courses) -}) - -app.listen(3000, () => console.log('Listening on port 30000...')) +router.get('/', (req, res) => { + res.send(courses); +}); ``` Our server is now set up: ![](/img/server-listening.png) - > When creating our API this structure of creating handlers for specific routes will be iterated. Every endpoint will be specified with `[app].[http_request_type]` and followed by a callback. We can now call the endpoint: @@ -35,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); @@ -44,57 +37,57 @@ const getAllCourses = async () => { getAllCourses(); ``` -Returns: + +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'}, +]; ``` ## Parameters -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. +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 -app.get("/api/courses/:id", (req, res) => { +```js +router.get('/:id', (req, res) => { res.send(req.params.id); }); -```` +``` We use the `:` symbol in the URI to indicate that we looking to parse for a specific value in the data. Now if we call `/api/courses/2`, we will get the second item in the array. The block above is the most basic format but we would want to add some kind of error handling, for example: ```js -app.get("/api/courses/: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); }); ``` - ## Queries Whereas parameters return specific data points, queries don't get data they aggregate or present the data that is returned in a certain way, such as for instance applying a search function. We indicate queries with a `?` in our URI. For example: `/api/posts/2018/1?sortBy=name`. -To facilitate a request like this we have to create a GET path that allows for it: +To facilitate a request like this we have to create a GET path that allows for it: -````js -app.get("/api/posts/:year/:month", (req, res) => { +```js +router.get("/:year/:month", (req, res) => { res.send(req.query); })[](); -```` +``` We would get the following back: -````json +```json { - "sortBy": "name" + "sortBy": "name" } -```` +``` Again a JSON object with key-value pairs is returned. diff --git a/Programming_Languages/NodeJS/REST_APIs/2_POST.md b/Programming_Languages/NodeJS/REST_APIs/2_POST.md index b32b3f2..1cc2a8e 100644 --- a/Programming_Languages/NodeJS/REST_APIs/2_POST.md +++ b/Programming_Languages/NodeJS/REST_APIs/2_POST.md @@ -1,35 +1,30 @@ --- -tags: - - Programming_Languages - - backend - - node-js - - express - - REST - - apis +categories: + - Programming Languages +tags: [backend, node-js, REST, APIs] --- -# Creating a REST API with Node and Express: POST requests +# RESTful API with Node, Express and MongoDB: `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 -app.post('/api/courses', (req, res) => { - const course = { - id: courses.length + 1, - name: req.body.name - } - courses.push(course); - res.send(course) - -}) +router.post('/', (req, res) => { + const course = { + id: courses.length + 1, + name: req.body.name, + }; + courses.push(course); + res.send(course); +}); ``` Here we use the body that is sent from the client and isolate the field `name`. This presupposes that the client is sending us data with the following shape as the body: ```json { - "name": "Biology and Life Sciences" -} + "name": "Biology and Life Sciences" +} ``` To execute the PUT request from the frontend: @@ -37,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); @@ -45,16 +40,16 @@ const addCourse = async (newCourse) => { console.error(err); } }; -addCourse("Biology and Life Sciences"); +addCourse('Biology and Life Sciences'); ``` -Which returns: +Which returns: ```js { id: 4, name: 'Biology and Life Sciences' } ``` -The `id` is added by the server, not the client. Having created the new value we add it to our `courses` array. (In reality we would be creating a new entry in a database.) Then we follow the convention of returning the new value back to the client. +The `id` is added by the server, not the client. Having created the new value we add it to our `courses` array. (In reality we would be creating a new entry in a database.) Then we follow the convention of returning the new value back to the client. ## Validation @@ -66,22 +61,23 @@ function validateCourse(course) { name: Joi.string().min(3).required(), }); - const { error } = schema.validate(course); + const {error} = schema.validate(course); return error; } ``` + We can then add the validation as part of our general error handling: ```js -app.post('/api/courses', (req, res) => { - const course = { - id: courses.length + 1, - name: req.body.name - } - const { error } = schema.validate(req.body); +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)); - + courses.push(course); - res.send(course) -}) + res.send(course); +}); ``` diff --git a/Programming_Languages/NodeJS/REST_APIs/3_PUT.md b/Programming_Languages/NodeJS/REST_APIs/3_PUT.md index 2a64dc1..9bff156 100644 --- a/Programming_Languages/NodeJS/REST_APIs/3_PUT.md +++ b/Programming_Languages/NodeJS/REST_APIs/3_PUT.md @@ -1,39 +1,33 @@ --- -tags: - - Programming_Languages - - backend - - node-js - - express - - REST - - apis +categories: + - Programming Languages +tags: [backend, node-js, REST, APIs] --- -# Creating a REST API with Node and Express: PUT requests +# RESTful API with Node, Express and MongoDB: `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 -app.put("/api/courses/: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); }); - ``` + 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); @@ -42,11 +36,11 @@ const updateCourse = async (courseChange) => { } }; -updateCourse({ name: "A new course" }); +updateCourse({name: 'A new course'}); ``` Returns: ```js { id: 1, name: 'A new course' } -``` \ No newline at end of file +``` diff --git a/Programming_Languages/NodeJS/REST_APIs/4_DELETE.md b/Programming_Languages/NodeJS/REST_APIs/4_DELETE.md index 6f43fc4..fb95c78 100644 --- a/Programming_Languages/NodeJS/REST_APIs/4_DELETE.md +++ b/Programming_Languages/NodeJS/REST_APIs/4_DELETE.md @@ -1,24 +1,18 @@ --- -tags: - - Programming_Languages - - backend - - node-js - - express - - REST - - apis +categories: + - Programming Languages +tags: [backend, node-js, REST, APIs] --- -# Creating a REST API with Node and Express: DELETE requests +# RESTful API with Node, Express and MongoDB: `DELETE` requests ```js -app.delete("/api/course/: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); res.send(course); }); - -``` \ No newline at end of file +``` diff --git a/Programming_Languages/NodeJS/REST_APIs/Full_example.md b/Programming_Languages/NodeJS/REST_APIs/Full_example.md index 08ad914..da553cc 100644 --- a/Programming_Languages/NodeJS/REST_APIs/Full_example.md +++ b/Programming_Languages/NodeJS/REST_APIs/Full_example.md @@ -1,22 +1,22 @@ --- -tags: - - Programming_Languages - - backend - - node-js - - express - - REST - - apis +categories: + - Programming Languages +tags: [backend, node-js, REST, APIs] --- # Full example ```js -onst 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 Joi = require('joi'); +const helmet = require('helmet'); +const morgan = require('morgan'); +const courses = require('./routes/courses'); + +// Routes +app.use('/api/courses', courses); // Middlewear app.use(express.json()); @@ -25,54 +25,53 @@ 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}`)); + function validateCourse(course) { const schema = Joi.object({ name: Joi.string().min(3).required(), }); - const { error } = schema.validate(course); + const {error} = schema.validate(course); return error; } -app.listen(port, () => console.log(`Listening on ${port}`)); - // Return all data from API -app.get("/api/courses", (req, res) => { +courses.get('/', (req, res) => { res.send(courses); }); // Return a specific value -app.get("/api/courses/: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 -app.post("/api/courses", (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, @@ -83,31 +82,26 @@ app.post("/api/courses", (req, res) => { }); // Update a course -app.put("/api/courses/: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 -app.delete("/api/course/: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); res.send(course); }); - - -``` \ No newline at end of file +```