Hello to all, welcome to therichpost.com. In this post, I will tell you, Angular 16 + Node.js Express + MySQL example: CRUD App.
Key features:
- Angular 16
- Bootstrap 5
- RxJs
- Nodejs
- Express
- Cors
- Mysql
- Add, update, delete, deleteAll, searchFilter
Angular 16 came and if you are new then please check below links:
Guys here is the working complete code snippet and please use carefully and if you have any kind of doubt then please comment below:
1. Very first, we need run below commands into our terminal to get angular 16 fresh setup:
npm install -g @angular/cli //Setup Angular16 atmosphere ng new angularcrud //Install New Angular App /**You need to update your Nodejs also for this verison**/ cd angularcrud //Go inside the Angular 16 Project
2. Now guys we need to run below commands into your terminal to get components, services, models and bootstrap 5 modules into your angular 16 application:
ng g class models/tutorial --type=model ng g c components/add-tutorial ng g c components/tutorial-details ng g c components/tutorials-list ng g s services/tutorial npm install bootstrap npm i @popperjs/core
After all above commands we will below image like folder structure:
3. Now add below code into your angular.json file for bootstrap styles:
"styles": [ ... "node_modules/bootstrap/dist/css/bootstrap.min.css", ], "scripts": [ ... "node_modules/bootstrap/dist/js/bootstrap.min.js", ]
4. Now add below code into your src/app/app.module.ts file:
... import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ ... ], imports: [ ... FormsModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
5. Now add below code into your src/app/app-routing.module.ts file:
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { TutorialsListComponent } from './components/tutorials-list/tutorials-list.component'; import { TutorialDetailsComponent } from './components/tutorial-details/tutorial-details.component'; import { AddTutorialComponent } from './components/add-tutorial/add-tutorial.component'; const routes: Routes = [ { path: '', redirectTo: 'tutorials', pathMatch: 'full' }, { path: 'tutorials', component: TutorialsListComponent }, { path: 'tutorials/:id', component: TutorialDetailsComponent }, { path: 'add', component: AddTutorialComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
6. Now add below code into your src/app/app-component.html file:
<div> <nav class="navbar navbar-expand navbar-dark bg-dark"> <a href="#" class="navbar-brand">therichpost</a> <div class="navbar-nav mr-auto"> <li class="nav-item"> <a routerLink="tutorials" class="nav-link">Tutorials</a> </li> <li class="nav-item"> <a routerLink="add" class="nav-link">Add</a> </li> </div> </nav> <div class="container mt-5"> <router-outlet></router-outlet> </div> </div>
7. Now add below code into your src/app/models/tutorial.model.ts file for our four fields:
export class Tutorial { id?: any; title?: string; description?: string; published?: boolean; }
8. Now add below code into your src/app/services/tutorial.service.ts file for api crud request:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Tutorial } from '../models/tutorial.model'; const baseUrl = 'http://localhost:8080/api/tutorials'; @Injectable({ providedIn: 'root' }) export class TutorialService { constructor(private http: HttpClient) { } getAll(): Observable<Tutorial[]> { return this.http.get<Tutorial[]>(baseUrl); } get(id: any): Observable<Tutorial> { return this.http.get<Tutorial>(`${baseUrl}/${id}`); } create(data: any): Observable<any> { return this.http.post(baseUrl, data); } update(id: any, data: any): Observable<any> { return this.http.put(`${baseUrl}/${id}`, data); } delete(id: any): Observable<any> { return this.http.delete(`${baseUrl}/${id}`); } deleteAll(): Observable<any> { return this.http.delete(baseUrl); } findByTitle(title: any): Observable<Tutorial[]> { return this.http.get<Tutorial[]>(`${baseUrl}?title=${title}`); } }
9. Now add below code into your src/app/components/add-tutorial/add-tutorial.component.ts file:
import { Component, OnInit } from '@angular/core'; import { Tutorial } from 'src/app/models/tutorial.model'; import { TutorialService } from 'src/app/services/tutorial.service'; @Component({ selector: 'app-add-tutorial', templateUrl: './add-tutorial.component.html', styleUrls: ['./add-tutorial.component.scss'] }) export class AddTutorialComponent implements OnInit { tutorial: Tutorial = { title: '', description: '', published: false }; submitted = false; constructor(private tutorialService: TutorialService) { } ngOnInit(): void { } saveTutorial(): void { const data = { title: this.tutorial.title, description: this.tutorial.description }; this.tutorialService.create(data) .subscribe({ next: (res) => { console.log(res); this.submitted = true; }, error: (e) => console.error(e) }); } newTutorial(): void { this.submitted = false; this.tutorial = { title: '', description: '', published: false }; } }
10. Now add below code into your src/app/components/add-tutorial/add-tutorial.component.html file:
<div class=" mt-5"> <h1 class="mb-3 text-center">Add Tutorial</h1> <div class="submit-form"> <div *ngIf="!submitted"> <div class="form-group mb-3"> <label for="title" class="mb-2">Title</label> <input type="text" class="form-control" id="title" required [(ngModel)]="tutorial.title" name="title" /> </div> <div class="form-group mb-3"> <label for="description" class="mb-2">Description</label> <input class="form-control" id="description" required [(ngModel)]="tutorial.description" name="description" /> </div> <button (click)="saveTutorial()" class="btn btn-success">Submit</button> </div> <div *ngIf="submitted"> <h4>Tutorial was submitted successfully!</h4> <button class="btn btn-success" (click)="newTutorial()">Add</button> </div> </div> </div>
11. Now add below code into your src/app/components/add-tutorial/add-tutorial.component.scss file:
.submit-form { max-width: 400px; margin: auto; }
12. Now add below code into your src/app/components/tutorial-details/tutorial-details.component.ts file:
import { Component, Input, OnInit } from '@angular/core'; import { TutorialService } from 'src/app/services/tutorial.service'; import { ActivatedRoute, Router } from '@angular/router'; import { Tutorial } from 'src/app/models/tutorial.model'; @Component({ selector: 'app-tutorial-details', templateUrl: './tutorial-details.component.html', styleUrls: ['./tutorial-details.component.scss'] }) export class TutorialDetailsComponent implements OnInit { @Input() viewMode = false; @Input() currentTutorial: Tutorial = { title: '', description: '', published: false }; message = ''; constructor( private tutorialService: TutorialService, private route: ActivatedRoute, private router: Router) { } ngOnInit(): void { if (!this.viewMode) { this.message = ''; this.getTutorial(this.route.snapshot.params["id"]); } } getTutorial(id: string): void { this.tutorialService.get(id) .subscribe({ next: (data) => { this.currentTutorial = data; console.log(data); }, error: (e) => console.error(e) }); } updatePublished(status: boolean): void { const data = { title: this.currentTutorial.title, description: this.currentTutorial.description, published: status }; this.message = ''; this.tutorialService.update(this.currentTutorial.id, data) .subscribe({ next: (res) => { console.log(res); this.currentTutorial.published = status; this.message = res.message ? res.message : 'The status was updated successfully!'; }, error: (e) => console.error(e) }); } updateTutorial(): void { this.message = ''; this.tutorialService.update(this.currentTutorial.id, this.currentTutorial) .subscribe({ next: (res) => { console.log(res); this.message = res.message ? res.message : 'This tutorial was updated successfully!'; }, error: (e) => console.error(e) }); } deleteTutorial(): void { this.tutorialService.delete(this.currentTutorial.id) .subscribe({ next: (res) => { console.log(res); this.router.navigate(['/tutorials']); }, error: (e) => console.error(e) }); } }
13. Now add below code into your src/app/components/tutorial-details/tutorial-details.component.html file:
<div *ngIf="viewMode; else editable"> <div *ngIf="currentTutorial.id"> <h4>Tutorial</h4> <div> <label><strong>Title:</strong></label> {{ currentTutorial.title }} </div> <div> <label><strong>Description:</strong></label> {{ currentTutorial.description }} </div> <div> <label><strong>Status:</strong></label> {{ currentTutorial.published ? "Published" : "Pending" }} </div> <a class="btn btn-warning" routerLink="/tutorials/{{ currentTutorial.id }}" > Edit </a> </div> <div *ngIf="!currentTutorial"> <br /> <p>Please click on a Tutorial...</p> </div> </div> <ng-template #editable> <div *ngIf="currentTutorial.id" class="edit-form"> <h4>Tutorial</h4> <form> <div class="form-group mb-3"> <label for="title" class="mb-2">Title</label> <input type="text" class="form-control" id="title" [(ngModel)]="currentTutorial.title" name="title" /> </div> <div class="form-group mb-3"> <label for="description" class="mb-2">Description</label> <input type="text" class="form-control" id="description" [(ngModel)]="currentTutorial.description" name="description" /> </div> <div class="form-group mb-5"> <label><strong>Status:</strong></label> {{ currentTutorial.published ? "Published" : "Pending" }} </div> </form> <button class="btn btn-primary me-2 mb-2" *ngIf="currentTutorial.published" (click)="updatePublished(false)" > UnPublish </button> <button *ngIf="!currentTutorial.published" class="btn btn-primary me-2 mb-2" (click)="updatePublished(true)" > Publish </button> <button class="btn btn-danger me-2 mb-2" (click)="deleteTutorial()"> Delete </button> <button type="submit" class="btn btn-success mb-2" (click)="updateTutorial()" > Update </button> <p>{{ message }}</p> </div> <div *ngIf="!currentTutorial.id"> <br /> <p>Cannot access this Tutorial...</p> </div> </ng-template>
14. Now add below code into your src/app/components/tutorial-details/tutorial-details.component.scss file:
.list { text-align: left; max-width: 750px; margin: auto; } .edit-form { max-width: 400px; margin: auto; }
15. Now add below code into your src/app/components/tutorials-list/tutorials-list.component.ts file:
import { Component, OnInit } from '@angular/core'; import { Tutorial } from 'src/app/models/tutorial.model'; import { TutorialService } from 'src/app/services/tutorial.service'; @Component({ selector: 'app-tutorials-list', templateUrl: './tutorials-list.component.html', styleUrls: ['./tutorials-list.component.scss'] }) export class TutorialsListComponent implements OnInit { tutorials?: Tutorial[]; currentTutorial: Tutorial = {}; currentIndex = -1; title = ''; constructor(private tutorialService: TutorialService) { } ngOnInit(): void { this.retrieveTutorials(); } retrieveTutorials(): void { this.tutorialService.getAll() .subscribe({ next: (data) => { this.tutorials = data; console.log(data); }, error: (e) => console.error(e) }); } refreshList(): void { this.retrieveTutorials(); this.currentTutorial = {}; this.currentIndex = -1; } setActiveTutorial(tutorial: Tutorial, index: number): void { this.currentTutorial = tutorial; this.currentIndex = index; } removeAllTutorials(): void { this.tutorialService.deleteAll() .subscribe({ next: (res) => { console.log(res); this.refreshList(); }, error: (e) => console.error(e) }); } searchTitle(): void { this.currentTutorial = {}; this.currentIndex = -1; this.tutorialService.findByTitle(this.title) .subscribe({ next: (data) => { this.tutorials = data; console.log(data); }, error: (e) => console.error(e) }); } }
16. Now add below code into your src/app/components/tutorials-list/tutorials-list.component.html file:
<div class="list row mt-5"> <div class="col-md-8"> <div class="input-group mb-3"> <input type="text" class="form-control" placeholder="Search by title" [(ngModel)]="title" /> <div class="input-group-append"> <button class="btn btn-outline-secondary" type="button" (click)="searchTitle()" > Search </button> </div> </div> </div> <div class="col-md-6 mt-3"> <h4 class="mb-3">Tutorials List</h4> <ul class="list-group mb-3"> <li class="list-group-item" *ngFor="let tutorial of tutorials; let i = index" [class.active]="i == currentIndex" (click)="setActiveTutorial(tutorial, i)" > {{ tutorial.title }} </li> </ul> <button class="btn btn-sm btn-danger" (click)="removeAllTutorials()"> Remove All </button> </div> <div class="col-md-6"> <app-tutorial-details [viewMode]="true" [currentTutorial]="currentTutorial" ></app-tutorial-details> </div> </div>
Guys now we have all the angular code and below I am going to write nodejs+express code and please follow it carefully also:
1. Now guys create folder `nodejs-express-sequelize-mysql` inside angular project root and run below command inside the folder:
npm init name: (nodejs-express-sequelize-mysql) version: (1.0.0) description: Node.js Rest Apis with Express, Sequelize & MySQL. entry point: (index.js) server.js test command: git repository: keywords: nodejs, express, sequelize, mysql, rest, api author: therichpost license: (ISC) Is this ok? (yes) yes
2. Guys now inside `nodejs-express-sequelize-mysql` folder create server.js file and add below code inside it:
const express = require("express"); const cors = require("cors"); const app = express(); var corsOptions = { origin: "http://localhost:4200" }; app.use(cors(corsOptions)); // parse requests of content-type - application/json app.use(express.json()); // parse requests of content-type - application/x-www-form-urlencoded app.use(express.urlencoded({ extended: true })); const db = require("./app/models"); db.sequelize.sync() .then(() => { console.log("Synced db."); }) .catch((err) => { console.log("Failed to sync db: " + err.message); }); // simple route app.get("/", (req, res) => { res.json({ message: "Welcome to therichpost application." }); }); // set port, listen for requests require("./app/routes/turorial.routes")(app); const PORT = process.env.PORT || 8080; app.listen(PORT, () => { console.log(`Server is running on port ${PORT}.`); });
3. Guys also run below command inside `nodejs-express-sequelize-mysql` folder:
npm install express sequelize mysql2 cors --save
4. Now guys create `app` folder inside `nodejs-express-sequelize-mysql` folder.
5. Now guys create `config` folder inside `nodejs-express-sequelize-mysql/app` folder and then create `db.config.js` file inside nodejs-express-sequelize-mysql/app/config and add below code inside it for mysql connection:
module.exports = { HOST: "localhost", USER: "root", PASSWORD: "", DB: "testdb", dialect: "mysql", pool: { max: 5, min: 0, acquire: 30000, idle: 10000 } };
6. Now guys create `controllers` folder inside `nodejs-express-sequelize-mysql/app` folder and then create `tutorial.controller.js` file inside nodejs-express-sequelize-mysql/app/controllers and add below code inside it for crud functions:
const db = require("../models"); const Tutorial = db.tutorials; const Op = db.Sequelize.Op; // Create and Save a new Tutorial exports.create = (req, res) => { // Validate request if (!req.body.title) { res.status(400).send({ message: "Content can not be empty!" }); return; } // Create a Tutorial const tutorial = { title: req.body.title, description: req.body.description, published: req.body.published ? req.body.published : false }; // Save Tutorial in the database Tutorial.create(tutorial) .then(data => { res.send(data); }) .catch(err => { res.status(500).send({ message: err.message || "Some error occurred while creating the Tutorial." }); }); }; // Retrieve all Tutorials from the database. exports.findAll = (req, res) => { const title = req.query.title; var condition = title ? { title: { [Op.like]: `%${title}%` } } : null; Tutorial.findAll({ where: condition }) .then(data => { res.send(data); }) .catch(err => { res.status(500).send({ message: err.message || "Some error occurred while retrieving tutorials." }); }); }; // Find a single Tutorial with an id exports.findOne = (req, res) => { const id = req.params.id; Tutorial.findByPk(id) .then(data => { if (data) { res.send(data); } else { res.status(404).send({ message: `Cannot find Tutorial with id=${id}.` }); } }) .catch(err => { res.status(500).send({ message: "Error retrieving Tutorial with id=" + id }); }); }; // Update a Tutorial by the id in the request exports.update = (req, res) => { const id = req.params.id; Tutorial.update(req.body, { where: { id: id } }) .then(num => { if (num == 1) { res.send({ message: "Tutorial was updated successfully." }); } else { res.send({ message: `Cannot update Tutorial with id=${id}. Maybe Tutorial was not found or req.body is empty!` }); } }) .catch(err => { res.status(500).send({ message: "Error updating Tutorial with id=" + id }); }); }; // Delete a Tutorial with the specified id in the request exports.delete = (req, res) => { const id = req.params.id; Tutorial.destroy({ where: { id: id } }) .then(num => { if (num == 1) { res.send({ message: "Tutorial was deleted successfully!" }); } else { res.send({ message: `Cannot delete Tutorial with id=${id}. Maybe Tutorial was not found!` }); } }) .catch(err => { res.status(500).send({ message: "Could not delete Tutorial with id=" + id }); }); }; // Delete all Tutorials from the database. exports.deleteAll = (req, res) => { Tutorial.destroy({ where: {}, truncate: false }) .then(nums => { res.send({ message: `${nums} Tutorials were deleted successfully!` }); }) .catch(err => { res.status(500).send({ message: err.message || "Some error occurred while removing all tutorials." }); }); }; // Find all published Tutorials exports.findAllPublished = (req, res) => { Tutorial.findAll({ where: { published: true } }) .then(data => { res.send(data); }) .catch(err => { res.status(500).send({ message: err.message || "Some error occurred while retrieving tutorials." }); }); };
7. Now guys create `routes` folder inside `nodejs-express-sequelize-mysql/app` folder and then create `turorial.routes.js` file inside nodejs-express-sequelize-mysql/app/routes and add below code inside it for api routing:
module.exports = app => { const tutorials = require("../controllers/tutorial.controller.js"); var router = require("express").Router(); // Create a new Tutorial router.post("/", tutorials.create); // Retrieve all Tutorials router.get("/", tutorials.findAll); // Retrieve all published Tutorials router.get("/published", tutorials.findAllPublished); // Retrieve a single Tutorial with id router.get("/:id", tutorials.findOne); // Update a Tutorial with id router.put("/:id", tutorials.update); // Delete a Tutorial with id router.delete("/:id", tutorials.delete); // Delete all Tutorials router.delete("/", tutorials.deleteAll); app.use('/api/tutorials', router); };
8. Now guys create `models` folder inside `nodejs-express-sequelize-mysql/app` folder and then create `index.js` file inside nodejs-express-sequelize-mysql/app/models and add below code inside it for models:
const dbConfig = require("../config/db.config.js"); const Sequelize = require("sequelize"); const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, { host: dbConfig.HOST, dialect: dbConfig.dialect, operatorsAliases: false, pool: { max: dbConfig.pool.max, min: dbConfig.pool.min, acquire: dbConfig.pool.acquire, idle: dbConfig.pool.idle } }); const db = {}; db.Sequelize = Sequelize; db.sequelize = sequelize; db.tutorials = require("./tutorial.model.js")(sequelize, Sequelize); module.exports = db;
9. Now guys create `models` folder inside `nodejs-express-sequelize-mysql/app` folder and then create `tutorial.model.js` file inside nodejs-express-sequelize-mysql/app/models and add below code inside it for models
(This Sequelize Model represents tutorials table in MySQL database. These columns will be generated automatically: id, title, description, published, createdAt, updatedAt.):
module.exports = (sequelize, Sequelize) => { const Tutorial = sequelize.define("tutorial", { title: { type: Sequelize.STRING }, description: { type: Sequelize.STRING }, published: { type: Sequelize.BOOLEAN } }); return Tutorial; };
Guys in the end for angular 16 please run ng serve command and nodejs please run node server.js command.
Jassa
Thanks
Great and it helped a lot.
You are welcome.
Thank you Buddy it works perfectly!
And.. what about uploading the project to the hosting? If I generate the dist folder with “ng build” the folder with server.js is not included into the dist folder. Could you please let me know, how can I do that?
Thanks!!
You are welcome and I will make video post on that. Thanks.