[go: up one dir, main page]

0% found this document useful (0 votes)
18 views20 pages

Mean Notes

The document outlines methods for interacting with a MongoDB database using Mongoose, including GET, POST, PUT, and DELETE methods for querying, creating, updating, and deleting documents and subdocuments. It details how to handle API requests and responses, including error handling and data validation. Additionally, it emphasizes the importance of separating concerns in code by moving rendering logic into named functions.

Uploaded by

cammusowmiya13
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views20 pages

Mean Notes

The document outlines methods for interacting with a MongoDB database using Mongoose, including GET, POST, PUT, and DELETE methods for querying, creating, updating, and deleting documents and subdocuments. It details how to handle API requests and responses, including error handling and data validation. Additionally, it emphasizes the importance of separating concerns in code by moving rendering logic into named functions.

Uploaded by

cammusowmiya13
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 20

IIIGET methods: Reading data from MongoDB

GET methods are all about querying the database and returning some data. In our routes.
for Loc8r we have three GET requests doing different things.
We’ll look at how to find a single location first, because it provides a good intro-
duction to the way Mongoose works. Next we’ll locate a single document using an ID,
and then we’ll expand into searching for multiple documents.

Finding a single document in MongoDB using Mongoose


Mongoose interacts with the database through its models, which is why we imported
the Locations model as Loc at the top of the controller files. A Mongoose model has
several associated methods to help manage the interactions as noted in the follow-
ing sidebar.
For finding a single database document with a known ID in MongoDB, Mongoose
has the findById method.

Finding a single subdocument based on IDs


To find a subdocument you first have to find the parent document like we’ve just done
to find a single location by its ID. Once you’ve found the document you can look for a
specific subdocument.
This means that we can take the locationsReadOne controller as the starting point
and add a few modifications to create the reviewsReadOne controller. These modifica-
tions are
■ Accept and use an additional reviewid URL parameter.
■ Select only the name and reviews from the document, rather than having Mon-
goDB return the entire document.
■ Look for a review with a matching ID.
■ Return the appropriate JSON response.
To do these things we can use a couple of new Mongoose methods.

Finding multiple documents with geospatial queries


The homepage of Loc8r should display a list of locations based on the user’s current
geographical location. MongoDB and Mongoose have some special geospatial query
methods to help find nearby places.
Here we’ll use the Mongoose method geoNear to find a list of locations close to a
specified point, up to a specified maximum distance. geoNear is a model method that
accepts three parameters:
■ A geoJSON geographical point
■ An options object
■ A callback function

POST methods: Adding data to MongoDB


POST methods are all about creating documents or subdocuments in the database,
and then returning the saved data as confirmation. In the routes for Loc8r we have
two POST requests doing different things.
POST methods work by taking form data posted to them and adding it to the database.
In the same way that URL parameters are accessed using req.params and query strings
are accessed via req.query, Express controllers access posted form data via req.body.
Let’s make a start by looking at how to create documents.

Creating new documents in MongoDB


In the database for Loc8r each location is a document, so this is what we’ll be creating
in this section. Mongoose really couldn’t make the process of creating MongoDB doc-
uments much easier for you. You take your model, apply the create method, and
send it some data and a callback function. This is the minimal construct, as it would
be attached to our Loc model:
Creating new subdocuments in MongoDB
In the context of Loc8r locations, reviews are subdocuments. Subdocuments are cre-
ated and saved through their parent document. Put another way, to create and save a
new subdocument you have to
1 Find the correct parent document.
2 Add a new subdocument.
3 Save the parent document.
var doAddReview = function(req, res, location) {
if (!location) {
sendJsonResponse(res, 404, {
"message": "locationid not found"
});
} else {
location.reviews.push({
author: req.body.author,
rating: req.body.rating,
reviewText: req.body.reviewText
});
location.save(function(err, location) {
var thisReview;
if (err) {
sendJsonResponse(res, 400, err);
} else {
updateAverageRating(location._id);
thisReview = location.reviews[location.reviews.length - 1];
sendJsonResponse(res, 201, thisReview);
}
});
}
};

PUT methods: Updating data in MongoDB


PUT methods are all about updating existing documents or subdocuments in the data-
base, and then returning the saved data as confirmation. In the routes for Loc8r we
have two PUT requests doing different things.
PUT methods are similar to POST methods because they work by taking form data
posted to them. But instead of using the data to create new documents in the data-
base, PUT methods use the data to update existing documents.

Using Mongoose to update a document in MongoDB


In Loc8r we might want to update a location to add new facilities, change the open
times, or amend any of the other data. The approach to updating data in a document
is probably starting to look familiar, following these steps:
1 Find the relevant document.
2 Make some changes to the instance.
3 Save the document.
4 Send a JSON response.
This approach is made possible by the way that an instance of a Mongoose model
maps directly to a document in MongoDB. When your query finds the document you
get a model instance. If you make changes to this instance and then save it, Mongoose
will update the original document in the database with your changes.
Updating an existing subdocument in MongoDB
Updating a subdocument is exactly the same as updating a document, with one excep-
tion. After finding the document you then have to find the correct subdocument to
make your changes. After this, the save method is applied to the document, not the
subdocument. So the steps to updating an existing subdocument are
1 Find the relevant document.
2 Find the relevant subdocument.
3 Make some changes to the subdocument.
4 Save the document.
5 Send a JSON response.

DELETE method: Deleting data from MongoDB

The DELETE method is, unsurprisingly, all about deleting existing documents or
subdocuments in the database. In the routes for Loc8r we have a DELETE request
for deleting a location, and another for deleting a review.
Deleting documents in MongoDB
Mongoose makes deleting a document in MongoDB extremely simple by giving us the
method findByIdAndRemove. This method expects just a single parameter—the ID of
the document to be deleted.
The API should respond with a 404 in case of an error and a 204 in case of success.
The following listing shows this all in place in the locationsDeleteOne controller.

module.exports.locationsDeleteOne = function(req, res) {


var locationid = req.params.locationid;
if (locationid) {
Loc
.findByIdAndRemove(locationid)
.exec(
function(err, location) {
if (err) {
sendJsonResponse(res, 404, err);
return;
}
sendJsonResponse(res, 204, null);
}
);
} else {
sendJsonResponse(res, 404, {
"message": "No locationid"
});
}
};

Deleting a subdocument from MongoDB


The process for deleting a subdocument is no different from the other work we’ve
done with subdocuments—everything is managed through the parent document. The
steps for deleting a subdocument are
1 Find the parent document.
2 Find the relevant subdocument.
3 Remove the subdocument.
4 Save the parent document.
5 Confirm success or failure of operation.
Actually deleting the subdocument itself is really easy, as Mongoose gives us another
helper method. You’ve already seen that we can find a subdocument by its ID with the
id method like this:
location.reviews.id(reviewid)

Mongoose allows you to chain a remove method to the end of this statement like so:
location.reviews.id(reviewid).remove()
This will delete the subdocument from the array. Remember, of course, that the par-
ent document will need saving after this to persist the change back to the database.
Putting all the steps together—with a load of error trapping—into the reviewsDelete-
One controller looks like the following listing.

Unit. 4

Consuming a REST API: Using an API from inside Express

call an API from Express

The first part we need to cover is how to call an API from Express. This isn’t actually
limited to our API; the approach can be used to call any API.
Adding the request module to our project
The request module is just like any of the other packages we’ve used so far, and can
be added to our project using npm. To install the latest version and add it to the pack-
age.json file, head to terminal and type the following command:
$ npm install --save request
When npm has finished doing its thing, we can include request into the files that will
use it. In Loc8r we only have one file that needs to make API calls, and that’s the file with all of
the controllers for the main server-side application. So right at the top of
locations.js in app_server/controllers add the following line to require request:
var request = require('request');

Setting up default options


Every API call with request must have a fully qualified URL, meaning that it must
include the full address and not be a relative link. But this URL will be different for
development and live environments.
To avoid having to make this check in every controller that makes an API call, we
can set a default configuration option once at the top of the controllers file. To use the
correct URL depending on the environment we can use our old friend the NODE_ENV
environment variable.
Using the request module
The basic construct for making a request is really simple, being just a single command
taking parameters for options and a callback like this:
The following code snippet shows an example of how you might put these together
for a GET request. A GET request shouldn’t have a body to send, but might have query
string parameters.
var requestOptions = {
url : "http://yourapi.com/api/path",
method : "GET",
json : {},
qs : {
offset : 20
}
};

There are many more options that you could specify, but these are the common four,
and the ones we’ll be using in this chapter. For more information on other possible
options, take a look at the reference in the GitHub repository: https://github.com/
mikeal/request.
lists of data from an API

By now the controllers file that will be doing the work should already have the
request module required in, and some default values set. So now comes the fun
part—let’s update the controllers to call the API and pull the data for the pages from
the database.
We’ve got two main pages that pull data: the homepage showing a list of locations,
and a Details page giving more information about a specific location. Let’s start at the
beginning and get the data for the homepage from the database.

Separating concerns: Moving the rendering into a named function


There are a couple of reasons for moving the rendering into its own named function.
First, we decouple the rendering from the application logic. The process of rendering
doesn’t care where or how it got the data; if it’s given data in the right format it will
use it.

var renderHomepage = function(req, res){


res.render('locations-list', {
title: 'Loc8r - find a place to work with wifi',

});
};
module.exports.homelist = function(req, res){
renderHomepage(req, res);
};

Building the API request


The data we want by asking the API for it, and to do this we need to build the
request. To build the request we need to know the URL, method, JSON body, and
query string to send.
Using the API response data
The effort of calling the API, the least we can do is use the data
it’s sending back. We can make this more robust later, but we’ll start with making it
work. In making it work we’re going to assume that a response body is returned to the callback,
and we can just pass this straight into the renderHomepage function, as high-
lighted in the following listing.
request(
requestOptions,
function(err, response, body) {
renderHomepage(req, res, body);
}
);
Catching errors returned by the API
The API is always going to return an array of data along with a 200 success code. But this isn’t
necessarily the case. We coded the API to return a 200 status even if no locations are found
nearby. As things stand, when this happens the homepage will display without any content in
the central area. A far better user expe-rience will be to output a message to the user that there
are no places nearby.
We also know that our API can give 404 errors, so we’ll need to make sure we han-
dle these appropriately. We don’t really want to show a 404 to the user in this case,
because the error will not be due to the homepage itself being missing.

Getting single documents from an API: The Loc8r Details page


The Details page should display all of the information we have about a specific loca-
tion, from the name and address, to ratings, reviews, facilities, and a location map.
Setting URLs and routes to access specific MongoDB documents
The current path we have to the Details page is just /location. This doesn’t offer a
way to specify which location we want to look at. To address this we can borrow the approach
from the API routes, where we specify the ID of the location document as a
URL parameter.
The API route for a single location is /api/locations/:locationid. We can do the
same thing for the main Express application and update the route to contain the
locationid parameter. The main application routes for locations are in locations.js in
the /routes folder. The following code snippet shows the simple change needed to
update the location detail route to accept the locationid URL parameter:
router.get('/', ctrlLocations.homelist);
router.get('/location/:locationid', ctrlLocations.locationInfo);
router.get('/location/review/new', ctrlLocations.addReview);

Separating concerns: Moving the rendering into a named function


Just like we did for the homepage, we’ll move the rendering of the Details page into
its own named function. Again, this is to keep the rendering functionality separate
from the API call and data processing.
The following listing shows a trimmed-down version of the new renderDetailPage
function, and how it’s called from the locationInfo controller.
var renderDetailPage = function (req, res) {
res.render('location-info', {
title: 'Starcups',
...
});
};
module.exports.locationInfo = function(req, res){
renderDetailPage(req, res);
};

Querying the API using a unique ID from a URL parameter


The URL for the API call needs to contain the ID of the location. Our Details page now
has this ID as the URL parameter locationid, so we can get the value of this using
req.params and add it to the path in the request options. The request is a GET
request, and as such the json value will be an empty object.
Passing the data from the API to the view
We’re currently assuming that the API is returning the correct data—we’ll get around
to error trapping soon. This data only needs a small amount of preprocessing: the
coordinates are returned from the API as an array, but the view needs them to be
named key-value pairs in an object.

request(
requestOptions,
function(err, response, body) {
var data = body;
data.coords = {
lng : body.coords[0],
lat : body.coords[1]
};
renderDetailPage(req, res, data);
}
);

Creating status-specific error pages


If the ID from the URL isn’t found in the database, the API will return a 404 error. This
error originates from the URL in the browser, so the browser should also return a
404—the data for the ID wasn’t found, so in essence the page cannot be found.
Using techniques we’ve already seen in this chapter we can quite easily catch when
the API returns a 404 status, using response.statusCode in the request callback. We
don’t really want to deal with it inside the callback, so we’ll just pass the flow into a
new function that we can call, _showError.

Adding Data to the Database via the API

Set Up the API Endpoint

Ensure the API has a properly configured POST endpoint for data insertion.
Example: POST /api/items
Prepare the Request

Use JSON format for data transmission.


Include necessary fields as per the database schema.
Example JSON body:
json
Copy code
{
"name": "Item Name",
"description": "Item Description",
"price": 100
}
Authentication & Authorization

Include authentication tokens (e.g., JWT, API key).


Verify user roles and permissions to prevent unauthorized access.
Send the Request

Use tools like cURL, Postman, or a frontend application to send the request.
Example cURL command:
curl -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"name": "Item Name", "description": "Item Description", "price": 100}'

Handle API Response

Expect HTTP status codes:


● 201 Created (Success)
● 400 Bad Request (Validation Error)
● 401 Unauthorized (Authentication issue)
● 500 Internal Server Error (Server issue)
Process the response to confirm data insertion.
Validate & Store Data

API should validate incoming data to prevent SQL injection and bad entries.
Use ORM (e.g., SQLAlchemy, Django ORM) or raw SQL to insert data into the database.
Confirm Data Storage

Fetch newly added data via GET request to verify insertion.


Example: GET /api/items/{id}

Adding Angular components to an Express application

Getting Angular up and running


Angular is the second JavaScript framework in the MEAN stack, with Express being the
other. Express, as we’ve seen, sits on the server, whereas Angular sits on the client side
in the browser. Like Express, Angular helps you separate your concerns, dealing with
views, data, and logic in distinct areas. The approach is very much in the MVC style,
but Angular has been defined as an MVW framework, where the W stands for “what-
ever works for you.” Sometimes it might be controllers, or the view-model, or services.
Uncovering two-way data binding
This means that changes to the view update the model, and changes to the
model update the view. Remember that we’re not talking about any type of database
here—all of this happens in the browser.

<!DOCTYPE html>
<html ng-app>
<head>
<script src="angular.min.js"></script>
<meta charset="utf-8">
<title>Angular binding test</title>
</head>
<body>
<input ng-model="myInput" />
<h1>Hello {{ myInput }}</h1>
</body>
</html>

Setting up for greatness (and JavaScript code)


To cover the necessary concepts and jargon for getting started with some Angular cod-
ing we’ll go for the simple goal of assigning a default value to the myInput model. To
do this we’ll need to define our Angular application as a module, and create a control-
ler to manage the scope.
angular
.module('myApp')
.controller('myController', function() {
// controller code here
});

Displaying and filtering the homepage list

To change the way the homepage is coded, using Angular to display the list of locations on the
homepage rather than having Express deliver the HTML. Throughout the course of
this section we’ll cover quite a lot of Angular functionality that will be useful in most
other projects, including filtering lists, data format filters, services, dependency injec-
tion, and using directives for adding in reusable HTML.
Adding Angular to an Express application
■ Download the Angular library file.
■ Create a new JavaScript file for our code.
■ Include these files in the HTML.
■ Define the Angular application in the HTML.
Moving data delivery from Express to Angular
If we’re going to use Angular to display the list of locations, then Angular will need to
have the data for that list. We’ll start off validating the approach by using data hard-
coded in Angular—much like we did when building the Express application—before
eventually pulling it from the database.
To achieve this we need to do three things:
■ Remove the API call from the Express controller for the homepage.
■ Add some hard-coded data into the Angular application scope.
■ Update the view template to bind to the Angular data.

Using Angular filters to format data


Filters allow you to specify your chosen format for a given piece of data. Angular has
some built-in filters such as formatting a date, currency, and text. You can apply these
filters directly within the data binding in the HTML. To apply one insert a pipe | after
the value followed by the name of the filter.
Using Angular directives to create HTML snippets
In Angular, directives essentially allow you to create HTML snippets. A single snippet
can be used by as many different controllers and views as you like. This is a really
handy feature for making your application more consistent and easier to maintain.
And because these snippets run in the context of an Angular application.

Getting data from an API

When we’ve calmed down a bit we’ll use our API from Angular to get the data for the home-
page, and then also make the page location-aware.

Using services for data


Services are self-contained units of functionality that can be combined to provide the
complete functionality of a software application. You’ll end up using services quite a
lot in Angular, as most application logic should be deferred to services, making it reus-
able from multiple controllers.

Making HTTP requests from Angular to an API


Making HTTP requests from JavaScript is nothing particularly new. jQuery has had
good Ajax support for a while, and in chapter 7 we used the request module to make
HTTP requests from Node. Angular has a built-in service called $http to manage this
type of request.

var locationListCtrl = function ($scope, loc8rData) {


loc8rData
.success(function(data) {
$scope.data = { locations: data };
})
.error(function (e) {
console.log(e);
});
};

Adding HTML geolocation to find places near you


The main premise of Loc8r is that it will be location-aware, and thus able to find
places that are near you. So far we’ve been faking it by hard-coding geographic coordi-
nates into the API requests. We’re going to change that right now by adding in HTML5
geolocation.
To get this working we’ll need to do the following:
■ Add a call to the HTML5 location API into our Angular application.
■ Only look for places when we have the location.
■ Pass the coordinates to our Angular data service, removing the hard-coded
location.
■ Output messages along the way so the user knows what’s going on.
var geolocation = function () {
var getPosition = function (cbSuccess, cbError, cbNoGeo) {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(cbSuccess, cbError);
}
else {
cbNoGeo();
}
};
return {
getPosition : getPosition
};
};

Ensuring forms work as expected


By adding Angular to the site template in layout.jade we’ve inadvertently created a
bug in the form that adds a review. When we created the review form we kept the
action blank, as this meant that the form would submit to the current URL.

When Angular encounters a form without an action it prevents the submit action
from happening. In many ways this makes sense. If you’re creating an Angular appli-
cation and include a form, you’ll probably want Angular to handle the form and man-
age the submission and destination.
This is very helpful, except for the situation we find ourselves in now. The way to fix
it is to add an action to the form; this will stop Angular from interfering with the form.
Step one is to pass the current URL to Jade from the controller. An easy way to get
an accurate representation of the URL is via the originalUrl property of the req
object. The following listing shows the update required to the renderReviewForm
function in locations.js in the app_server/controllers folder.

var renderReviewForm = function (req, res, locDetail) {


res.render('location-review-form', {
title: 'Review ' + locDetail.name + ' on Loc8r',
pageHeader: { title: 'Review ' + locDetail.name },
error: req.query.err,
url: req.originalUrl
});
};

Step two is to simply output this url parameter in the action attribute of the form
definition in location-review-form.jade in the app_server/views folder. This is shown
in the following code snippet:
form.form-horizontal(action="#{url}", method="post", role="form")

You might also like