Angular + Kotlin +Spring Boot + Mongo DB CRUD Example

Hello everyone, today we will learn how to develop a full-stack web application that is a basic User Management Application using Angular 10, Kotlin, Spring Boot, and MongoDB.You can download the source code from our GitHub Repository.



After completing this tutorial what we will build? 

We will build a full-stack web application that is a basic User Management Application with CRUD features: 

• Create User 
• List User 
• Update User 
• Delete User 

Following is the screenshot of our application -



A quick overview of Angular 10, Kotlin, Spring Boot, and MongoDB 

Kotlin

Kotlin is a statically typed, general-purpose programming language targeting the Java platform. Kotlin is concise, safe, pragmatic, and fixated on interoperability with Java code. It can be used virtually everywhere Java is utilized today: for server-side development, Android apps, and much more. Kotlin works great with all subsisting Java libraries and frameworks and runs with the same level of performance as Java.

MongoDB

MongoDB is a document database built on a scale-out architecture that has to propagate with developers of all kinds who are building scalable applications utilizing agile methodologies.

MongoDB was built for people who are building internet and business applications who need to evolve expeditiously and scale elegantly. If you are doing that, you should consider MongoDB.

Companies and development teams of all sizes use MongoDB because:
  • The document data model is a potent way to store and retrieve data that sanctions developers to move expeditiously.
  • MongoDB’s horizontal, scale-out architecture can fortify astronomically immense volumes of both data and traffic.
  • MongoDB has a great utilizer experience for developers who can install MongoDB and commence inscribing code immediately.

Angular

Angular is a TypeScript-predicated open-source web application framework led by the Angular Team at Google and by a community of individuals and corporations. Angular is a consummate re-indite from the same team that built AngularJS.

Spring Boot

Spring boot to develop REST web services and microservices. Spring Boot has taken the Spring framework to the next level. It has drastically reduced the configuration and setup time required for spring projects. We can set up a project with almost zero configuration and start building the things that actually matter to your application.

We divided this tutorial into two parts

PART 1 - Restful API Development with Kotlin + Spring Boot (back end)
PART 2 - UI development using Angular 10 (front end/client)



PART 1 - Restful API Development with Kotlin + Spring Boot 

These are APIs that Spring backend App will export:



Backend project structure:




pom.xml[Maven]

Include spring-boot-starter-web for Spring MVC and REST structure, and spring-boot-starter-data-mongodb for CRUD repository.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev</groupId>
<artifactId>springboot-kotlin-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-kotlin-crud</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<kotlin.version>1.3.72</kotlin.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin
</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project> 



Creating the Document

import org.bson.types.ObjectId
import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.web.bind.annotation.CrossOrigin

@Document(collection = "users")
data class User(
@Id
var id: String? = ObjectId().toHexString(),
val firstName: String,
val lastName: String,
val emailId: String
)



Creating the Repository

Let’s now create the repository for accessing the data from the database.
package com.knf.dev.repository

import com.knf.dev.model.User
import org.springframework.data.mongodb.repository.MongoRepository
import org.springframework.stereotype.Repository

@Repository
interface UserRepository : MongoRepository<User, String>



Creating the controller End-points

package com.knf.dev.controller

import com.knf.dev.model.User
import com.knf.dev.repository.UserRepository
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import java.util.*

@RestController
@RequestMapping("/api/v1/")
@CrossOrigin(origins = arrayOf("http://localhost:4200"))
class UserController(private val userRepository: UserRepository) {

@GetMapping("/users")
fun getAllUsers(): List<User> =
userRepository.findAll()

@PostMapping("/users")
fun createNewUser(@RequestBody user: User): User =
userRepository.save(user)

@GetMapping("/users/{id}")
fun getUserById(@PathVariable(value = "id") userId: String):
ResponseEntity<User> {
return userRepository.findById(userId).map { user ->
ResponseEntity.ok(user)
}.orElse(ResponseEntity.notFound().build())
}

@PutMapping("/users/{id}")
fun updateUserById(@PathVariable(value = "id") userId: String,
@RequestBody newUser: User): ResponseEntity<User> {

return userRepository.findById(userId).map { existingUser ->
val updatedUser: User = existingUser
.copy(firstName = newUser.firstName, lastName =
newUser.lastName, emailId = newUser.emailId)
ResponseEntity.ok().body(userRepository.save(updatedUser))
}.orElse(ResponseEntity.notFound().build())

}

@DeleteMapping("/users/{id}")
fun deleteUserById(@PathVariable(value = "id") userId: String):
ResponseEntity<Void> {
return userRepository.findById(userId).map { user ->
userRepository.delete(user)
ResponseEntity<Void>(HttpStatus.OK)
}.orElse(ResponseEntity.notFound().build())

}
}



Spring Boot Main Driver

package com.knf.dev

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class SpringbootKotlinCrudApplication

fun main(args: Array<String>) {
runApplication<SpringbootKotlinCrudApplication>(*args)
}


PART 2 - UI development using Angular

Project Structure:




package.json

A package.json is a JSON file that subsists at the root of a Javascript/Node project. It holds metadata pertinent to the project and is utilized for managing the project's dependencies, scripts, version, and a whole lot more.
{
"name": "front-end-simple-crud",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~10.1.2",
"@angular/common": "~10.1.2",
"@angular/compiler": "~10.1.2",
"@angular/core": "~10.1.2",
"@angular/forms": "~10.1.2",
"@angular/platform-browser": "~10.1.2",
"@angular/platform-browser-dynamic": "~10.1.2",
"@angular/router": "~10.1.2",
"bootstrap": "^4.5.3",
"jquery": "^3.5.1",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.1100.3",
"@angular/cli": "~10.1.2",
"@angular/compiler-cli": "~10.1.2",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.0.2"
}
}



The User.ts file(User Model)

Path - src/app/user.ts 
Before defining the UserListComponent, let’s define a User class for working with users. create a new file user.ts inside src/app folder and add the following code to it -
export class User {
_id: string;
firstName: string;
lastName: string;
emailId: string;
active: boolean;

}



Create User List Template and Component

User List Component

Path - src/app/user-list/user-list.component.ts
Let's create the UserListComponent component, which will display a list of users, create a new user, and delete a user.
import { UserDetailsComponent } from '../user-details/user-details.component';
import { Observable } from "rxjs";
import { UserService } from "../user.service";
import { User } from "../user";
import { Component, OnInit } from "@angular/core";
import { Router } from '@angular/router';

@Component({
selector: "app-user-list",
templateUrl: "./user-list.component.html",
styleUrls: ["./user-list.component.css"]
})
export class UserListComponent implements OnInit {
users: Observable<User[]>;

constructor(private userService: UserService,
private router: Router) { }

ngOnInit() {
this.reloadData();
}

reloadData() {
this.users = this.userService.getUsersList();
}

deleteUser(_id: string) {
this.userService.deleteUser(_id)
.subscribe(
data => {
console.log(data);
this.reloadData();
},
error => console.log(error));
}
updateUser(id: string) {
this.router.navigate(['update', id]);
}
userDetails(_id: string) {
this.router.navigate(['details', _id]);
}
}



User List Template

Path - src/app/user-list/user-list.component.html
Add user-list.component.html file with the following code to it -
<div class="panel panel-primary">
<div class="panel-heading">
<h2>User List</h2>
</div>
<div class="panel-body">
<table class="table table-striped">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users | async">
<td>{{user.firstName}}</td>
<td>{{user.lastName}}</td>
<td>{{user.emailId}}</td>
<td>
<button (click)="updateUser(user.id)" class="btn btn-warning"
style="margin-left: 10px">Update</button>
<button (click)="deleteUser(user.id)" class="btn btn-danger"
style="margin-left: 10px">Delete</button>
<button (click)="userDetails(user.id)" class="btn btn-info"
style="margin-left: 10px">Details</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>



Create Add User Template and Component

Add User Component

Path - src/app/create-user/create-user.component.ts
CreateUserComponent is used to create and handle new user form data. Add the following code to it -
import { UserService } from '../user.service';
import { User } from '../user';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
selector: 'app-create-user',
templateUrl: './create-user.component.html',
styleUrls: ['./create-user.component.css']
})
export class CreateUserComponent implements OnInit {

user: User = new User();
submitted = false;

constructor(private userService: UserService,
private router: Router) { }

ngOnInit() {
}

newUser(): void {
this.submitted = false;
this.user = new User();
}

save() {
this.userService
.createUser(this.user).subscribe(data => {
console.log(data)
this.user = new User();
this.gotoList();
},
error => console.log(error));
}

onSubmit() {
this.submitted = true;
this.save();
}

gotoList() {
this.router.navigate(['/users']);
}
}



Create a User Template

Path - src/app/create-user/create-user.component.html 
The create-user.component.html shows the add using HTML form. Add the following code to it -
<h3>Create User</h3>
<div [hidden]="submitted" style="width: 400px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">First Name</label>
<input type="text" class="form-control" id="firstName"
required [(ngModel)]="user.firstName" name="firstName">
</div>

<div class="form-group">
<label for="name">Last Name</label>
<input type="text" class="form-control" id="lastName"
required [(ngModel)]="user.lastName" name="lastName">
</div>

<div class="form-group">
<label for="name">Email Id</label>
<input type="text" class="form-control" id="emailId"
required [(ngModel)]="user.emailId" name="emailId">
</div>

<button type="submit" class="btn btn-danger">Submit</button>
</form>
</div>

<div [hidden]="!submitted">
<h4>Successfully submitted!</h4>
</div>



Update User Template and Component

Update User Component

Path - src/app/update-user/update-user.component.ts 
UpdateUserComponent is used to update an existing user.
import { Component, OnInit } from '@angular/core';
import { User } from '../user';
import { ActivatedRoute, Router } from '@angular/router';
import { UserService } from '../user.service';

@Component({
selector: 'app-update-user',
templateUrl: './update-user.component.html',
styleUrls: ['./update-user.component.css']
})
export class UpdateUserComponent implements OnInit {

_id: string;
user: User;

constructor(private route: ActivatedRoute, private router: Router,
private userService: UserService) { }

ngOnInit() {
this.user = new User();

this._id = this.route.snapshot.params['_id'];

this.userService.getUser(this._id)
.subscribe(data => {
this.user = data;
}, error => console.log(error));
}

updateUser() {
this.userService.updateUser(this._id, this.user)
.subscribe(data => {
console.log(data);
this.user = new User();
this.gotoList();
}, error => console.log(error));
}

onSubmit() {
this.updateUser();
}

gotoList() {
this.router.navigate(['/users']);
}
}



Update User Template

Path - src/app/update-user/update-user.component.html 
The update-user.component.html shows the updated user HTML form. Add the following code to this file -
<h3>Update User</h3>
<div style="width: 500px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">First Name</label>
<input type="text" class="form-control" id="firstName"
required [(ngModel)]="user.firstName" name="firstName">
</div>

<div class="form-group">
<label for="name">Last Name</label>
<input type="text" class="form-control" id="lastName"
required [(ngModel)]="user.lastName" name="lastName">
</div>

<div class="form-group">
<label for="name">Email Id</label>
<input type="text" class="form-control" id="emailId"
required [(ngModel)]="user.emailId" name="emailId">
</div>

<button type="submit" class="btn btn-success">Submit</button>
</form>
</div>



Create a View User Details Template and Component

User Details Component

Path - src/app/user-details/user-details.component.ts 
The UserDetailsComponent component is used to display a particular user detail. Add the following code to it -
import { User } from '../user';
import { Component, OnInit, Input } from '@angular/core';
import { UserService } from '../user.service';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
selector: 'app-user-details',
templateUrl: './user-details.component.html',
styleUrls: ['./user-details.component.css']
})
export class UserDetailsComponent implements OnInit {

_id: string;
user: User;

constructor(private route: ActivatedRoute,private router: Router,
private userService: UserService) { }

ngOnInit() {
this.user = new User();

this._id = this.route.snapshot.params['_id'];
console.log("gh"+this._id);
this.userService.getUser(this._id)
.subscribe(data => {
console.log(data)
this.user = data;
}, error => console.log(error));
}

list(){
this.router.navigate(['users']);
}
}



User Details  Template

Path - src/app/user-details/user-details.component.html 
The user-details.component.html displays a particular user detail. Add the following code to it -
<h2>User Details</h2>

<hr />
<div *ngIf="user">
<div>
<label><b>First Name: </b></label> {{user.firstName}}
</div>
<div>
<label><b>Last Name: </b></label> {{user.lastName}}
</div>
<div>
<label><b>Email Id: </b></label> {{user.emailId}}
</div>
</div>

<br>
<br>
<button (click)="list()" class="btn btn-primary">User List</button><br>



User Service

Path - src/app/user.service.ts 
The UserService will be used to get the data from the backend by calling  APIs. Update the user.service.ts file inside src/app directory with the following code to it -
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class UserService {

private baseUrl = 'http://localhost:8000/api/v1/users';

constructor(private http: HttpClient) { }

getUser(_id: string): Observable<any> {
return this.http.get(`${this.baseUrl}/${_id}`);
}

createUser(user: Object): Observable<Object> {
return this.http.post(`${this.baseUrl}`, user);
}

updateUser(_id: string, value: any): Observable<Object> {
return this.http.put(`${this.baseUrl}/${_id}`, value);
}

deleteUser(_id: string): Observable<any> {
return this.http.delete(`${this.baseUrl}/${_id}`,
{ responseType: 'text' });
}

getUsersList(): Observable<any> {
return this.http.get(`${this.baseUrl}`);
}
}



App Routing Module

Path: /src/app/app.routing.module.ts 
Routing for the Angular app is configured as an array of Routes, each component is mapped to a path so the Angular Router knows which component to display based on the URL in the browser address bar.
import { UserDetailsComponent } from './user-details/user-details.component';
import { CreateUserComponent } from './create-user/create-user.component';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {UserListComponent } from './user-list/user-list.component';
import { UpdateUserComponent } from './update-user/update-user.component';

const routes: Routes = [
{ path: '', redirectTo: 'user', pathMatch: 'full' },
{ path: 'users', component: UserListComponent },
{ path: 'add', component: CreateUserComponent },
{ path: 'update/:_id', component: UpdateUserComponent },
{ path: 'details/:_id', component: UserDetailsComponent },
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }



App Component

Path: /src/app/app.component.ts 
The app component is the root component of the application, it defines the root tag of the app as with the selector property of the @Component decorator.
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Angular 10 + Node.js + Mongo DB CRUD ';
}



App Component Template

Path: /src/app/app.component.html 
Defines the HTML template associated with the root AppComponent.
<nav class="navbar navbar-expand-sm bg-success navbar-dark">
<!-- Links -->
<ul class="navbar-nav">
<li class="nav-item">
<a routerLink="users" class="nav-link" routerLinkActive="active">
User List</a>
</li>
<li class="nav-item">
<a routerLink="add" class="nav-link" routerLinkActive="active">
Add User</a>
</li>
</ul>
</nav>
<div class="container">
<br>
<h2 style="text-align: center;">{{title}}</h2>
<hr>
<div class="card">
<div class="card-body">
<router-outlet></router-outlet>
</div>
</div>
</div>

<footer class="footer">
<div class="container">
<span>All Rights Reserved 2020 @Knowledge Factory</span>
</div>
</footer>



App Module

Path: /src/app/app.module.ts 
Defines the root module, named AppModule, that tells Angular how to assemble the application. Initially declares only the AppComponent. 
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CreateUserComponent } from './create-user/create-user.component';
import { UserDetailsComponent } from './user-details/user-details.component';
import { UserListComponent } from './user-list/user-list.component';
import { UpdateUserComponent } from './update-user/update-user.component';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent,
CreateUserComponent,
UserDetailsComponent,
UserListComponent,
UpdateUserComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }



index.html

Path: /src/index.html 
The main index.html file is the initial page loaded by the browser that kicks everything off.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Angular10CRUD</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>



Main/Bootstrap File

Path: /src/main.ts
The main file is the entry point used by angular to launch
and bootstrap the application.
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

Local Setup and Run the application

Step 1: Install Mongo DB - Click here


Step 2: Download or clone the source code from GitHub to a local machine - Click here



Backend


Step 3: mvn clean install


Step 4: Run the Spring Boot application - mvn spring-boot:run



Frontend


Step 5: npm install 


Step 6: npm serve

From the browser call the endpoint http://localhost:4200/

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete