Mongoose is a MongoDB object modeling tool that provides a schema-based solution to model data. In the tutorial, we will show how to build get/post/put/delete
requests from Angular 6 Client to MongoDB with NodeJs/Express RestAPIs using Mongoose ODM.
Related posts:
– Node.js/Express RestAPIs – Angular 6 HttpClient – Get/Post/Put/Delete requests + Bootstrap 4
– Crud RestAPIs with NodeJS/Express, MongoDB using Mongoose
Technologies
- Angular 6
- RxJS 6
- Bootstrap 4
- Visual Studio Code – version 1.24.0
- Nodejs – v8.11.3
- Mongoose
- MongoDB
Demo
Overview
Goal
We create 2 projects:
– Angular Client Project:
– Node.js RestAPIs project:
UserCase
Start Node.js server -> Logs:
App listening at http://:::8080 Successfully connected to MongoDB. Customer collection removed >>> Done - Initial Data! |
-> MongoDB’s documents:
> db.customers.find(); { "_id" : ObjectId("5b432fb558cbf6427cc7d527"), "firstname" : "Joe", "lastname" : "Thomas", "age" : 36, "__v" : 0 } { "_id" : ObjectId("5b432fb558cbf6427cc7d528"), "firstname" : "Peter", "lastname" : "Smith", "age" : 18, "__v" : 0 } { "_id" : ObjectId("5b432fb558cbf6427cc7d529"), "firstname" : "Lauren", "lastname" : "Taylor", "age" : 31, "__v" : 0 } { "_id" : ObjectId("5b432fb558cbf6427cc7d52a"), "firstname" : "Mary", "lastname" : "Taylor", "age" : 24, "__v" : 0 } { "_id" : ObjectId("5b432fb558cbf6427cc7d52b"), "firstname" : "David", "lastname" : "Moore", "age" : 25, "__v" : 0 } { "_id" : ObjectId("5b432fb558cbf6427cc7d52d"), "firstname" : "Michael", "lastname" : "Brown", "age" : 45, "__v" : 0 } { "_id" : ObjectId("5b432fb558cbf6427cc7d52c"), "firstname" : "Holly", "lastname" : "Davies", "age" : 27, "__v" : 0 } |
– Angular client retrieve all customers from Node.js RestAPIs:
– Angular client update a customer -> Change the firstname of first customer: ‘Joe’ to ‘Robert’.
-> result:
– Delete ‘Peter’ customer:
– Add a new customer:
-> result:
– Check final customer’s list:
-> MongoDB’s documents:
> db.customers.find(); { "_id" : ObjectId("5b434e5ce30d33364834a8e9"), "firstname" : "Robert", "lastname" : "Thomas", "age" : 36, "__v" : 0 } { "_id" : ObjectId("5b434e5ce30d33364834a8eb"), "firstname" : "Lauren", "lastname" : "Taylor", "age" : 31, "__v" : 0 } { "_id" : ObjectId("5b434e5ce30d33364834a8ec"), "firstname" : "Mary", "lastname" : "Taylor", "age" : 24, "__v" : 0 } { "_id" : ObjectId("5b434e5ce30d33364834a8ed"), "firstname" : "David", "lastname" : "Moore", "age" : 25, "__v" : 0 } { "_id" : ObjectId("5b434e5ce30d33364834a8ef"), "firstname" : "Michael", "lastname" : "Brown", "age" : 45, "__v" : 0 } { "_id" : ObjectId("5b434e5ce30d33364834a8ee"), "firstname" : "Holly", "lastname" : "Davies", "age" : 27, "__v" : 0 } { "_id" : ObjectId("5b435016e30d33364834a8f0"), "firstname" : "Maria", "lastname" : "Garcia", "age" : 39, "__v" : 0 } > |
Node.js/Express RestAPIs
Node.js exposes 5 RestAPIs as below:
router.post(‘/api/customers’, customers.create);
router.get(‘/api/customers’, customers.findAll);
router.get(‘/api/customers/:id’, customers.findOne);
router.put(‘/api/customers’, customers.update);
router.delete(‘/api/customers/:id’, customers.delete);
– Configure cross-origin
for Angular-Client which running at port: 4200
.
const cors = require('cors') const corsOptions = { origin: 'http://localhost:4200', optionsSuccessStatus: 200 } app.use(cors(corsOptions)) |
Angular 6 HttpClient
Use Angular HttpClient APIs to do Get/Post/Put/Delete
requests to Node.js RestAPIs:
getCustomers (): Observable<Customer[]> { return this.http.get<Customer[]>(this.customersUrl) } getCustomer(id: string): Observable<Customer> { const url = `${this.customersUrl}/${id}`; return this.http.get<Customer>(url); } addCustomer (customer: Customer): Observable<Customer> { return this.http.post<Customer>(this.customersUrl, customer, httpOptions); } deleteCustomer (customer: Customer | string): Observable<Customer> { const id = typeof customer === 'string' ? customer : customer._id; const url = `${this.customersUrl}/${id}`; return this.http.delete<Customer>(url, httpOptions); } updateCustomer (customer: Customer): Observable<any> { return this.http.put(this.customersUrl, customer, httpOptions); } |
Practice
Express Mongoose RestAPIs
Setting up NodeJs/Express project
Following the guide to create a NodeJS/Express project.
Install Express, Mongoose, Body-Parser, and Cors:
$npm install express mongoose body-parse cors --save |
– Body-Parser -> Parse incoming request bodies in a middleware before your handlers, available under the req.body property.
– Express is one of the most popular web frameworks for NodeJs which is built on top of Node.js http module, and adds support for routing, middleware, view system etc.
– Cors is a mechanism that uses HTTP headers to tell a browser to let a web application running at one origin (domain) have permission to access selected resources from a server at a different origin.
– Sequelize is a promise-based ORM for Node.js v4 and up. It supports the dialects PostgreSQL, MySQL …
-> package.json file:
{ "name": "nodejs-restapi-mongodb", "version": "1.0.0", "description": "NodeJs-RestApis-MongoDB", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "NodeJs-RestAPIs-MongoDB" ], "author": "JSA", "license": "ISC", "dependencies": { "body-parser": "^1.18.2", "cors": "^2.8.4", "express": "^4.16.3", "mongoose": "^5.0.13", "npm": "^5.8.0" } } |
Mongoose Schema
– Create file ‘./app/models/customer.model.js’ ->
const mongoose = require('mongoose'); const CustomerSchema = mongoose.Schema({ firstname: String, lastname: String, age: { type: Number, min: 18, max: 65, required: true } }); |
Configure MongoDB’s URL
In the root folder, create a file ‘app/config/mongodb.config.js’ as below content ->
module.exports = { url: 'mongodb://localhost:27017/nodejs-demo' } |
Implement CRUD APIs
Routes ->
In the root folder, create a file ‘app/routes/customer.routes.js’ as below content ->
module.exports = function(app) { const customers = require('../controllers/customer.controller.js'); // Create a new Customer app.post('/api/customers', customers.create); // Retrieve all Customer app.get('/api/customers', customers.findAll); // Retrieve a single Customer by Id app.get('/api/customers/:customerId', customers.findOne); // Update a Customer with Id app.put('/api/customers', customers.update); // Delete a Customer with Id app.delete('/api/customers/:customerId', customers.delete); } |
Controllers
In root folder ‘nodejs-restapi’, create a controller folder ‘/app/controllers’. Then create a file ‘/app/controllers/customer.controller.js’ that contains methods for executing above URL requests ->
const Customer = require('../models/customer.model.js'); // POST a Customer exports.create = (req, res) => { // Create a Customer const customer = new Customer(req.body); // Save a Customer in the MongoDB customer.save() .then(data => { res.json(data); }).catch(err => { res.status(500).json({ msg: err.message }); }); }; // FETCH all Customers exports.findAll = (req, res) => { Customer.find() .then(customers => { res.json(customers); }).catch(err => { res.status(500).send({ msg: err.message }); }); }; // FIND a Customer exports.findOne = (req, res) => { Customer.findById(req.params.customerId) .then(customer => { if(!customer) { return res.status(404).json({ msg: "Customer not found with id " + req.params.customerId }); } res.json(customer); }).catch(err => { if(err.kind === 'ObjectId') { return res.status(404).json({ msg: "Customer not found with id " + req.params.customerId }); } return res.status(500).json({ msg: "Error retrieving Customer with id " + req.params.customerId }); }); }; // UPDATE a Customer exports.update = (req, res) => { // Find customer and update it Customer.findByIdAndUpdate(req.body._id, req.body, {new: true}) .then(customer => { if(!customer) { return res.status(404).json({ msg: "Customer not found with id " + req.params.customerId }); } res.json(customer); }).catch(err => { if(err.kind === 'ObjectId') { return res.status(404).json({ msg: "Customer not found with id " + req.params.customerId }); } return res.status(500).json({ msg: "Error updating customer with id " + req.params.customerId }); }); }; // DELETE a Customer exports.delete = (req, res) => { Customer.findByIdAndRemove(req.params.customerId) .then(customer => { if(!customer) { return res.status(404).json({ msg: "Customer not found with id " + req.params.customerId }); } res.json({msg: "Customer deleted successfully!"}); }).catch(err => { if(err.kind === 'ObjectId' || err.name === 'NotFound') { return res.status(404).json({ msg: "Customer not found with id " + req.params.customerId }); } return res.status(500).json({ msg: "Could not delete customer with id " + req.params.customerId }); }); }; |
Server.js
‘server.js’ ->
const express = require('express'); const app = express(); const bodyParser = require('body-parser'); app.use(bodyParser.json()) // Configuring the database const dbConfig = require('./app/config/mongodb.config.js'); const mongoose = require('mongoose'); const Customer = require('./app/models/customer.model.js'); mongoose.Promise = global.Promise; // Connecting to the database mongoose.connect(dbConfig.url) .then(() => { console.log("Successfully connected to MongoDB."); Customer.remove({}, function(err) { if(err){ console.log(err); process.exit(); } console.log('Customer collection removed'); // -> initial new data initial(); }); }).catch(err => { console.log('Could not connect to MongoDB.'); process.exit(); }); const cors = require('cors') const corsOptions = { origin: 'http://localhost:4200', optionsSuccessStatus: 200 } app.use(cors(corsOptions)) require('./app/routes/customer.routes.js')(app); // Create a Server const server = app.listen(8080, function () { let host = server.address().address let port = server.address().port console.log("App listening at http://%s:%s", host, port) }) function initial(){ let customers = [ { firstname: "Joe", lastname: "Thomas", age: 36 }, { firstname: "Peter", lastname: "Smith", age: 18 }, { firstname: "Lauren", lastname: "Taylor", age: 31 }, { firstname: "Mary", lastname: "Taylor", age: 24 }, { firstname: "David", lastname: "Moore", age: 25 }, { firstname: "Holly", lastname: "Davies", age: 27 }, { firstname: "Michael", lastname: "Brown", age: 45 } ] // Init data -> save to MongoDB for (let i = 0; i < customers.length; i++) { const customer = new Customer(customers[i]); customer.save(); } console.log(">>> Done - Initial Data!"); } |
Angular 6 Client
Data Model
customer.ts
->
export class Customer { _id: string; firstname: string; lastname: string; age: number; } |
Configure AppModule
In the developed application, we use:
- Angular
Forms
-> for building form HttpClient
-> for httpGet/Post/Put/Delete
requestsAppRouting
-> for app routing
-> Modify AppModule app.module.ts
:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { AppRoutingModule } from './app-routing/app-routing.module'; import { AppComponent } from './app.component'; import { CustomerComponent } from './customer/customer.component'; import { CustomerDetailsComponent } from './customer-details/customer-details.component'; import { AddCustomerComponent } from './add-customer/add-customer.component'; @NgModule({ declarations: [ AppComponent, CustomerComponent, CustomerDetailsComponent, AddCustomerComponent ], imports: [ BrowserModule, FormsModule, AppRoutingModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } |
HttpClient DataService
Implement CustomerService customer.service.ts
with HttpClient
for Get/Post/Put/Delete
:
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Customer } from './customer'; const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; @Injectable({ providedIn: 'root' }) export class CustomerService { private customersUrl = 'http://localhost:8080/api/customers'; // URL to web api constructor( private http: HttpClient ) { } getCustomers (): Observable<Customer[]> { return this.http.get<Customer[]>(this.customersUrl) } getCustomer(id: string): Observable<Customer> { const url = `${this.customersUrl}/${id}`; return this.http.get<Customer>(url); } addCustomer (customer: Customer): Observable<Customer> { return this.http.post<Customer>(this.customersUrl, customer, httpOptions); } deleteCustomer (customer: Customer | string): Observable<Customer> { const id = typeof customer === 'string' ? customer : customer._id; const url = `${this.customersUrl}/${id}`; return this.http.delete<Customer>(url, httpOptions); } updateCustomer (customer: Customer): Observable<any> { return this.http.put(this.customersUrl, customer, httpOptions); } } |
Angular Router
Implement App-Routing module app-routing.module.ts
->
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CustomerComponent } from '../customer/customer.component'; import { AddCustomerComponent } from '../add-customer/add-customer.component'; import { CustomerDetailsComponent } from '../customer-details/customer-details.component'; const routes: Routes = [ { path: 'customers', component: CustomerComponent }, { path: 'customer/add', component: AddCustomerComponent }, { path: 'customers/:id', component: CustomerDetailsComponent }, { path: '', redirectTo: 'customers', pathMatch: 'full' }, ]; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule {} |
Router Outlet & Router Links
-> Questions:
- How to show Componenets with Angular Routers? -> Solution: using
Router Outlet
- How to handle the routing that comes from user’s actions? (like clicks on anchor tag) -> Solution: using
Router Link
-> We can achieve above functions by using Angular’s router-outlet
and routerLink
.
Modify the template file app.component.html
of AppComponenet component as below:
<div class="container"> <div class="row"> <div class="col-sm-4"> <h1>Angular HttpClient</h1> <ul class="nav justify-content-center"> <li class="nav-item"> <a routerLink="customers" class="btn btn-light btn-sm" role="button" routerLinkActive="active">Retrieve</a> </li> <li class="nav-item"> <a routerLink="customer/add" class="btn btn-light btn-sm" role="button" routerLinkActive="active">Create</a> </li> </ul> <hr> <router-outlet></router-outlet> </div> </div> </div> |
Customer Component
Customer Component
->
– Implement CustomerComponent class customer.component.ts
:
import { Component, OnInit } from '@angular/core'; import { Customer } from '../customer'; import { CustomerService } from '../customer.service'; @Component({ selector: 'app-customer', templateUrl: './customer.component.html', styleUrls: ['./customer.component.css'] }) export class CustomerComponent implements OnInit { customers: Customer[]; constructor(private customerService: CustomerService) {} ngOnInit(): void { this.getCustomers(); } getCustomers() { return this.customerService.getCustomers() .subscribe( customers => { console.log(customers); this.customers = customers } ); } } |
– Implement the template customer.component.html
:
<h5>All Customers</h5> <div *ngFor="let cust of customers; let rowIndex=index"> <a [routerLink]="['/customers', cust._id]" style="color:black"><span class="badge badge-dark">{{rowIndex + 1}}</span> -> {{cust.firstname}}</a> </div> |
Customer Detail Component
Customer Detail
->
-> results:
– Implement CustomerDetails class customer-details.component.ts
:
import { Component, OnInit } from '@angular/core'; import { Customer } from '../customer'; import { CustomerService } from '../customer.service'; import { ActivatedRoute, Params } from '@angular/router'; import { Location } from '@angular/common'; @Component({ selector: 'app-customer-details', templateUrl: './customer-details.component.html', styleUrls: ['./customer-details.component.css'] }) export class CustomerDetailsComponent implements OnInit { customer = new Customer() ; submitted = false; message: string; constructor( private customerService: CustomerService, private route: ActivatedRoute, private location: Location ) {} ngOnInit(): void { const id = this.route.snapshot.paramMap.get('id'); this.customerService.getCustomer(id) .subscribe(customer => this.customer = customer); } update(): void { this.submitted = true; this.customerService.updateCustomer(this.customer) .subscribe(result => this.message = "Customer Updated Successfully!"); } delete(): void { this.submitted = true; this.customerService.deleteCustomer(this.customer._id) .subscribe(result => this.message = "Customer Deleted Successfully!"); } goBack(): void { this.location.back(); } } |
– Implement CustomerDetailsComponent template customer-details.component.html
:
<h5><span class="badge badge-light ">{{customer._id}}</span> -> {{customer.firstname}}</h5> <div [hidden]="submitted"> <form #detailCustomerForm="ngForm"> <div class="form-group"> <label for="firstname">First Name</label> <input type="text" class="form-control" id="firstname" required [(ngModel)]="customer.firstname" name="firstname" #firstname="ngModel"> <div [hidden]="firstname.valid || firstname.pristine" class="alert alert-danger"> First Name is required </div> </div> <div class="form-group"> <label for="lastname">Last Name</label> <input type="text" class="form-control" id="lastname" required [(ngModel)]="customer.lastname" name="lastname" #lastname="ngModel"> <div [hidden]="lastname.valid || lastname.pristine" class="alert alert-danger"> Last Name is required </div> </div> <div class="form-group"> <label for="age">Age</label> <input type="number" class="form-control" id="age" required [(ngModel)]="customer.age" name="age" #age="ngModel"> <div [hidden]="age.valid || age.pristine" class="alert alert-danger"> Age is required </div> </div> <div class="btn-group btn-group-sm"> <button type="button" class="btn btn-dark" (click)="goBack()">Back</button> <button type="button" class="btn btn-dark" (click)="update()" [disabled]="!detailCustomerForm.form.valid">Update</button> <button type="button" class="btn btn-dark" (click)="delete()">Delete</button> </div> </form> </div> <div [hidden]="!submitted"> <p>{{message}}</p> <div class="btn-group btn-group-sm"> <button type="button" class="btn btn-dark" (click)="goBack()">Back</button> </div> </div> |
We can change the value of ng-valid
& ng-invalid
for more visual feedback,
-> Create ./assets/forms.css
file:
.ng-valid[required], .ng-valid.required { border-left: 5px solid rgba(32, 77, 32, 0.623); } .ng-invalid:not(form) { border-left: 5px solid rgb(148, 27, 27); } |
Add ./assets/forms.css
file to index.html
:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Angular6Httpclient</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="stylesheet" href="assets/forms.css"> </head> <body> <app-root></app-root> </body> </html> |
Add-Customer Component
AddCustomer Component
->
-> result:
– Implement AddCustomerComponent class add-customer.component.ts
:
import { Component, OnInit } from '@angular/core'; import { Customer } from '../customer'; import { CustomerService } from '../customer.service'; import { Location } from '@angular/common'; @Component({ selector: 'app-add-customer', templateUrl: './add-customer.component.html', styleUrls: ['./add-customer.component.css'] }) export class AddCustomerComponent{ customer = new Customer(); submitted = false; constructor( private customerService: CustomerService, private location: Location ) { } newCustomer(): void { this.submitted = false; this.customer = new Customer(); } addCustomer() { this.submitted = true; this.save(); } goBack(): void { this.location.back(); } private save(): void { console.log(this.customer); this.customerService.addCustomer(this.customer) .subscribe(); } } |
– Implement the template add-customer.component.html
:
<h3>Add Customer</h3> <div [hidden]="submitted"> <form #addCustomerForm="ngForm"> <div class="form-group"> <label for="firstname">First Name</label> <input type="text" class="form-control" id="firstname" placeholder="Give Customer's FirstName" required [(ngModel)]="customer.firstname" name="firstname" #firstname="ngModel"> <div [hidden]="firstname.valid || firstname.pristine" class="alert alert-danger"> First Name is required </div> </div> <div class="form-group"> <label for="lastname">Last Name</label> <input type="text" class="form-control" id="lastname" placeholder="Give Customer's LastName" required [(ngModel)]="customer.lastname" name="lastname" #lastname="ngModel"> <div [hidden]="lastname.valid || lastname.pristine" class="alert alert-danger"> Last Name is required </div> </div> <div class="form-group"> <label for="age">Age</label> <input type="number" class="form-control" id="age" placeholder="Give Customer's Age" required [(ngModel)]="customer.age" name="age" #age="ngModel"> <div [hidden]="age.valid || age.pristine" class="alert alert-danger"> Age is required </div> </div> <div class="btn-group btn-group-sm"> <button type="button" class="btn btn-dark" (click)="goBack()">Back</button> <button type="button" class="btn btn-dark" (click)="addCustomer()" [disabled]="!addCustomerForm.form.valid">Add</button> </div> </form> </div> <div [hidden]="!submitted"> <p>Submitted Successfully! -> <span class="badge badge-light">{{customer.firstname}} {{customer.lastname}}</span></p> <div class="btn-group btn-group-sm"> <button type="button" class="btn btn-dark" (click)="goBack()">Back</button> <button type="button" class="btn btn-dark" (click)="newCustomer(); addCustomerForm.reset()">Continue</button> </div> </div> |
EssayOneDay provides students with professionally written essays, vsetcio.32essay.com/my-handbook/business-planmaker-professional-12-reviews.html investigate papers, with respect to papers, reviews, theses, dissertations and more. Straight away you run auspices of EssayOneDay also in behalf of your ms writing needs, you won’t desideratum to adjudicate any other advantage rantee first-class breeze scolding vsetcio.32essay.com/my-handbook/resume-for-entry-level-it-position.html and 100% plagiarism free papers.
I wont to help how to update with by id how to get name by id