"Editor's note" in the 90 's, the linux+apache+mysql+php architecture was rage until now, still the basic architecture of many WEB applications. However, with the change of demand and the explosion of data flow, LAMP has inevitably gone down the altar. Recently, in MongoDB Blog, Dana Groce introduced a new era architecture based on the practice of--mean,mongodb/mongoose.js, Express.js, Angular.js and node. js. This article is compiled by OneAPM engineers:
The two articles in this series of blogs focus on the use of the MEAN technology stack--mongodb/mongoose.js, Express.js, Angular.js, and node. js. These technologies all use JAVASCRIPT for higher software performance and developer productivity.
The first blog post mainly describes the basic structure of the application and the process of data modeling, while the second creates tests to validate the application's behavior and then describes how to set up and run the application.
This series of blog posts does not require practical experience with these technologies, and developers of all skill levels can benefit from it. If you haven't used MongoDB, JavaScript, or the experience of building a REST API before, don't worry, these topics will be covered in enough detail, including authentication, building code in multiple files, writing test cases, and more. First, start with the definition of MEAN stack.
What is MEAN Stack
The MEAN stack can be summarized as:
- M = Mongodb/mongoose.js. The popular database for node. JS is an elegant ODM.
- E = express.js: A lightweight WEB application framework.
- A = Angular.js: A robust framework for creating HTML5 and Javascript-rich Web applications.
- N = node. js server-side JavaScript interpreter.
MEAN Stack is a modern alternative to the lamp (Linux, Apache, Mysql,php/python) stack, where lamp was the dominant form of WEB application construction in the late 90.
Angular.js is not used in this application because it is not intended to build an HTML user interface. Instead, it creates a REST API without a user interface, but it can be used as the basis for any interface, such as a Web site, an Android application, or an IOS application. It can also be said that we are building the REST API on the ME (a) N stack, but this is not the point!
What is the REST API?
REST represents representational state Transfer, a more lightweight alternative to the SOAP and WSDL xml-based API protocols.
REST uses the client-server model, where the server is an HTTP server, and the client sends HTTP behavior (GET, POST, PUT, DELETE), as well as URL-encoded variable parameters and a URL. The URL specifies the scope of the object, and the server responds with a result code and a valid JavaScript object Notation (JSON).
Because the server uses JSON to reply, MongoDB interacts well with JSON, and all components use JavaScript, so the MEAN stack is well suited for applications in this use case. You'll see some examples of JSON as you go into the beginning to define the data model.
CRUD abbreviations are often used to describe database operations. CRUD represents create, read, update, and delete. These database operations are well mapped to HTTP actions:
- POST: The customer wants to insert or create an object.
- GET: The client wants to read an object.
- PUT: The customer wants to update an object.
- Delete: The customer wants to delete an object.
These operations become more intuitive after the API is defined. Some common HTTP result codes that are commonly used in REST APIs are:
- 200--"ok".
- 201--"created" (used with post).
- 400--"bad request" (the required parameters may be lost).
- 401--"unauthorized" (the authentication parameter is missing).
- 403--"forbidden" (verified, but not sufficient).
- 404--"not found".
A complete description can be found in the RFC documentation, which is listed in resources at the end of this blog. These result codes will be used in this application, and then some examples will be shown.
Why start with the REST API?
Deploying a REST API can lay the groundwork for building any type of application. As mentioned earlier, these applications may be web-based or designed specifically for certain platforms, such as Android or IOS.
Nowadays, many companies are no longer using HTTP or WEB interfaces such as Uber, WHATSAPP, Postmates, and Wash.io when they build applications. From a simple application to a powerful platform, rest APIs can greatly simplify the implementation of other interfaces and applications in this process.
Building the REST API
This will create an RSS aggregator, similar to Google Reader, where the application will consist of two components:
- REST API
- Feed Grabber (similar to Google Reader)
This series of posts will focus on the REST API's build-up, and will not focus on the complexities of RSS feeds. Now, the code for the Feed Grabber can be found in GitHub repository, as detailed in the resources listed in the blog post. The steps required to build this API are described below. The data model is defined first according to the specific requirements:
- Store user information in user accounts
- Track RSS feeds that need to be monitored
- Pull the feed record to the database
- Tracking User Feed Subscriptions
- Keep track of which subscribed feeds the user will read
The user will need to be able to complete the following actions:
- Create an Account
- Subscribe to a feed or unsubscribe
- Read Feed Records
- Mark Feed/Record reading status (read/unread)
Data modeling
The data modeling in MongoDB is not discussed in depth, and details can be found in the data listed in the post-blog post. This use case requires 4 collections to manage this information:
- Feed Collection
- Feed Entry Collection
- User Collection
- User-feed-entry Mapping Collection
Feed Collection
Following a snippet of code, the modeling of the Feed Collection can be done in the following JSON document:
{"_id": ObjectId("523b1153a2aa6a3233a913f8"),"requiresAuthentication": false,"modifiedDate": ISODate("2014-08-29T17:40:22Z"),"permanentlyRemoved": false,"feedURL": "http://feeds.feedburner.com/eater/nyc","title": "Eater NY","bozoBitSet": false,"enabled": true,"etag": "4bL78iLSZud2iXd/vd10mYC32BE","link": "http://ny.eater.com/","permanentRedirectURL": null,"description": "The New York City Restaurant, Bar, and Nightlife Blog”}
If you are proficient in relational database technology, you will learn about databases, tables, columns, and rows. In MongoDB, most of the relational concepts can be mapped. At the high level, MongoDB deployments support 1 or more databases. 1 databases may contain multiple collection, which is similar to a table in a traditional relational database. There will be more than one document in Collection, which, from the high level, is equivalent to a row in a relational database. It should be noted here that the document in MongoDB does not have a preset format, instead, each document can have 1 or more key-value pairs, where the value may be simple, such as date, can be complex, such as 1 address object array.
The JSON document above is an example of a eater Blog RSS feed that tracks all the restaurant information in New York. Therefore, there may be many fields here, and the main concern in use cases is the URL in the feed and the description. The description is very important, so when building a mobile application, it will be a good summary of the feed.
The other fields in JSON are used for internal use, where the very important field is _id. In MongoDB, each document needs to have a _id field. If you build a document,mongodb without--id, it will be added automatically for you. In MongoDB, this field is the presence of the primary key, so MongoDB guarantees that the field value is unique within the collection range.
Feed Entry Collection
After the feed, the use case is also expected to track the feed record. Here is an example of a Feed Entry Collection document:
{ "_id": ObjectId("523b1153a2aa6a3233a91412"), "description": "Buzzfeed asked a bunch of people...”, "title": "Cronut Mania: Buzzfeed asked a bunch of people...", "summary": "Buzzfeed asked a bunch of people that were...”, "content": [{ "base": "http://ny.eater.com/", "type": "text/html", "value": ”LOTS OF HTML HERE ", "language": "en" }], "entryID": "tag:ny.eater.com,2013://4.560508", "publishedDate": ISODate("2013-09-17T20:45:20Z"), "link": "http://ny.eater.com/archives/2013/09/cronut_mania_41 .php", "feedID": ObjectId("523b1153a2aa6a3233a913f8")}
Again, you must also have a _id field, and you can see the description, title, and summary fields. For the Content field, an array is used here, and a document is stored in the data. MongoDB allows the use of document nesting in this way, and this usage is also necessary in many scenarios, as use cases often require that information be stored centrally.
The EntryID field uses the tag format to avoid copying feed records. It is important to note that the use of Feedid and ObjectId-value is the _id of Eater Blog document. This provides a reference model that resembles a foreign key in a relational database. Therefore, if you expect to see this ObjectId associated feed document, you can take the value 523b1153a2aa6a3233a913f8, and query the Feed collection on _id, which will return the Eater Blog document 。
User Collection
Here is a document that the user needs to use:
{ "_id" : ObjectId("54ad6c3ae764de42070b27b1"), "active" : true, "email" : "[email protected]", "firstName" : "Test", "lastName" : "User1", "sp_api_key_id" : "6YQB0A8VXM0X8RVDPPLRHBI7J", "sp_api_key_secret" : "veBw/YFx56Dl0bbiVEpvbjF”, "lastLogin" : ISODate("2015-01-07T17:26:18.996Z"), "created" : ISODate("2015-01-07T17:26:18.995Z"), "subs" : [ ObjectId("523b1153a2aa6a3233a913f8"), ObjectId("54b563c3a50a190b50f4d63b") ],}
The user should have an email address, first name, and last name. Similarly, there is the SPAPIkeyID and SPAPIkeysecret--in the subsequent sections using both fields in conjunction with Stormpath (a user management API). The last field, Subs, is a 1-subscription array. The subs field will indicate which feeds the user subscribes to.
User-feed-entry Mapping Collection
{ "_id" : ObjectId("523b2fcc054b1b8c579bdb82"), "read" : true, "user_id" : ObjectId("54ad6c3ae764de42070b27b1"), "feed_entry_id" : ObjectId("523b1153a2aa6a3233a91412"), "feed_id" : ObjectId("523b1153a2aa6a3233a913f8")}
The last collection allows mapping the user to feeds and keeps track of which feeds have been read. Here, a Boolean type (True/false) is used to mark both the read and the unread.
Some functional requirements for the REST API
As mentioned above, the user needs to be able to do the following:
- Create an Account
- Subscribe to a feed or unsubscribe
- Read Feed Records
- Mark Feed/Record reading status (read/unread)
In addition, the user needs to be able to reset the password. The following table shows how these operations are mapped to HTTP routes and actions.
In a production environment, HTTP (HTTPS) security requirements use a standard way to send sensitive information, such as passwords.
Enable real-world authentication with Stormpath
In a robust real-world application, the provision of user authentication is unavoidable. Therefore, there is a secure way to manage user, password, and password resets.
In this use case, authentication can be done in several ways. One is to use node. js with Passport Plugin, which is often used for social media account verification, such as Facebook or Twitter. However, Stormpath is also a very good way. Stormpath is a user management-as-a-service, supporting authentication and authorization via the API keys. Fundamentally, Stormpath maintains a database of user details and passwords so that the client application API can invoke the Stormpath REST API for user authentication.
Shows the request and response flow after using Stormpath.
In detail, Stormpath provides a security key for each application, defined by their service. As an example, here you can define an application as "reader production" or "reader test". If you have been developing and testing your application all the time, it is very practical to define both applications because it is very frequent to add and remove test users. Here, Stormpath also provides an API Key Properties file. Stormpath also allows you to define password attributes based on your application's requirements, such as:
- No less than 8 characters
- Must contain a case
- Must contain a number
- Must contain 1 non-alphabetic characters
Stormpath keeps track of all users and assigns their API keys (for REST API authentication), which greatly simplifies the application setup process because it eliminates the need to write code for validating users.
node. js
node. JS is the run-time environment for server-side and network applications. node. JS uses JavaScript and is suitable for many different platforms, such as Linux, Microsoft Windows, and Apple OS x.
The node. JS application needs to be built through multiple library modules, and there are already a lot of resources in the community that will be used in subsequent application builds.
To use node. js, the developer needs to define the Package.json file to describe the application and the dependencies of all libraries.
node. js Package Manager installs a copy of all libraries to a subdirectory of the application directory, which is node_modules/. This is advantageous because it isolates the library versions of different applications and avoids the complexity of the code that is caused by the uniform installation of all libraries into the standard directory, such as/usr/lib.
Command NPM to build the node_modules/directory and all the required libraries.
Below is the JavaScript under the Package.json file:
{ "name": "reader-api", "main": "server.js", "dependencies": { "express" : "~4.10.0", "stormpath" : "~0.7.5", "express-stormpath" : "~0.5.9", "mongodb" : "~1.4.26”, "mongoose" : "~3.8.0", "body-parser" : "~1.10.0”, "method-override" : "~2.3.0", "morgan" : "~1.5.0”, "winston" : "~0.8.3”, "express-winston" : "~0.2.9", "validator" : "~3.27.0", "path" : "~0.4.9", "errorhandler" : "~1.3.0", "frisby" : "~0.8.3", "jasmine-node" : "~1.14.5", "async" : "~0.9.0" }}
The application is named Reader-api, and the primary file is named Server.js, followed by a series of dependent libraries and their versions. Some of these libraries are designed to parse HTTP queries. Here, we use Frisby as the Test tool, while Jasmine-node is used to run the Frisby script.
In these libraries, Async is particularly important. If you've never used node. js, be aware that node. JS uses an asynchronous mechanism. Therefore, any action that blocks input/output (I/O), such as reading from a socket or 1 database queries, takes a callback function as the last parameter, and then continues to control the flow, which is not resumed until the blocking operation has ended. Let's look at a simple example to understand this.
function foo() { someAsyncFunction(params, function(err, results) { console.log(“one”); }); console.log(“two”); }
In the above example, the output you imagined might be:
onetwo
But the actual output is:
twoone
This result is caused by the asynchronous mechanism that node. JS uses, and the code that prints "one" may be executed in subsequent callback functions. It's possible because it only happens under certain circumstances. The uncertainty brought about by this asynchronous programming is called non-deterministic execution. For many programming tasks, this can be very high performance, but in the case of sequential requirements is very troublesome. The following usage can be used to obtain an ideal order:
actionArray = [ function one(cb) { someAsyncFunction(params, function(err, results) { if (err) { cb(new Error(“There was an error”)); } console.log(“one”); cb(null); }); }, function two(cb) { console.log(“two”); cb(null); } ] async.series(actionArray);
Summarize
Through this article, I believe that you have a certain understanding of node. js and asynchronous function settings, so the next post will describe a deeper level of knowledge. Instead of starting to build the application, this will go into building tests and validating the application's behavior. This approach is known as test-driven development, and it brings two major benefits:
First, it helps developers figure out how to consume data and functions, but also helps to clarify some strange requirements, such as storing multiple objects in an array.
By writing tests prior to building the application, the model is converted from "assumed to being working until a test fails" to "broken/unimplemented until proven tested ok". For building a more robust application, the former is obviously more secure.
A new era alternative to mean practice--lamp (bottom)
Original link: Building your first application with mongodb:creating a REST API using the MEAN Stack-part 1
This article is compiled and collated by OneAPM engineers. OneAPM is an emerging leader in application performance management, enabling enterprise users and developers to easily implement slow program code and real-time crawling of SQL statements. To read more technical articles, please visit the OneAPM official blog.
Mean practice--lamp's new Era alternative (top)