Angular Spring Boot Microservices Example
author : Sai K
In this tutorial, we will create a microservices architecture using Spring Boot and Angular. We will
build two
microservices, an API Gateway, a Service Registry, and an Angular client that
calls the
API Gateway. We will use
the latest versions of Spring Boo, Angular, and Spring Cloud.
Prerequisites
Before we start, ensure you have the following:
- Java Development Kit (JDK) installed
- Apache Maven installed
- Node.js and npm installed
- Angular CLI installed (
npm install -g @angular/cli
) - An IDE (such as IntelliJ IDEA, Eclipse, or VS Code) installed
Overview of the Architecture
- Service Registry: Manages service discovery.
- API Gateway: Routes client requests to appropriate microservices.
- Employee Service: Manages employee data.
- Department Service: Manages department data.
- Angular Client: Provides a user interface to interact with the services via the API Gateway.
Step 1: Create the Service Registry
1.1 Create a Spring Boot Project
1.Open Spring Initializr:
- Go to Spring Initializr in your web browser.
2.Configure Project Metadata:
- Project: Maven Project
- Language: Java
- Spring Boot: Select the latest version of Spring Boot 3.2
- Group: com.example
- Artifact: service-registry
- Name: service-registry
- Package Name: com.example.serviceregistry
- Packaging: Jar
- Java Version: 17 (or your preferred version)
- Click Next.
3.ect Dependencies:
- Spring Cloud Discovery
4.Generate the Project:
- Click Generate to download the project zip file.
- Extract the zip file to your desired location
5.Open the Project in Your IDE:
1.2 Update application.yml
Create an application.yml file in the src/main/resources directory and add the following configuration:
server:
port: 8761
spring:
application:
name: service-registry
eureka:
client:
register-with-eureka: false
fetch-registry: false
instance:
hostname: localhost
server:
enable-self-preservation: false
1.3 Enable Eureka Server
Create a class ServiceRegistryApplication and annotate it with @EnableEurekaServer:
package com.example.serviceregistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class ServiceRegistryApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceRegistryApplication.class, args);
}
}
1.4 Run the Service Registry
Run the application by executing the ServiceRegistryApplication class. The Eureka Dashboard should be
accessible at http://localhost:8761.
Step 2: Create the Employee Service
2.1 Create a Spring Boot Project
1. Open Spring Initializr and configure a new project with the following metadata:
- Group: com.example
- Artifact: employee-service
- Name: employee-service
- Package Name: com.example.employeeservice
- Dependencies: Spring Web, Spring Data JPA, H2 Database, Spring Cloud Discovery, Spring
Boot DevTools
2.Generate the Project and open it in your IDE.
2.2 Update application.yml
Create an application.yml file in the src/main/resources directory:
server:
port: 8081
spring:
application:
name: employee-service
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password: password
jpa:
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.H2Dialect
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
2.3 Create Employee Entity
Create an Employee entity class in the com.example.employeeservice.model package:
package com.example.employeeservice.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String department;
// Getters and Setters
}
2.4 Create Employee Repository
Create an EmployeeRepository interface in the com.example.employeeservice.repository package:
package com.example.employeeservice.repository;
import com.example.employeeservice.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EmployeeRepository extends JpaRepository {
}
2.5 Create Employee Controller
Create an EmployeeController class in the com.example.employeeservice.controller package:
package com.example.employeeservice.controller;
import com.example.employeeservice.model.Employee;
import com.example.employeeservice.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/employees")
public class EmployeeController {
private final EmployeeRepository employeeRepository;
@Autowired
public EmployeeController(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
@GetMapping
public List getAllEmployees() {
return employeeRepository.findAll();
}
@PostMapping
public Employee createEmployee(@RequestBody Employee employee) {
return employeeRepository.save(employee);
}
}
2.6 Enable Discovery Client
Annotate the main application class with @EnableDiscoveryClient:
package com.example.employeeservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class EmployeeServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EmployeeServiceApplication.class, args);
}
}
2.7 Run the Employee Service
Run the application by executing the EmployeeServiceApplication class. The service should register
itself with
the Eureka server.
Step 3: Create the Department Service
Repeat the same steps as for the Employee Service, but with the following changes:
3.1 Create a Spring Boot Project
1.Open Spring Initializr and configure a new project with the following metadata:
- Group: com.example
- Artifact: department-service
- Name: department-service
- Package Name: com.example.departmentservice
- Dependencies: Spring Web, Spring Data JPA, H2 Database, Spring Cloud Discovery, Spring Boot DevTools
2.Generate the Project and open it in your IDE.
3.2 Update application.yml
Create an application.yml file in the src/main/resources directory:
server:
port: 8082
spring:
application:
name: department-service
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password: password
jpa:
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.H2Dialect
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
3.3 Create Department Entity
Create a Department entity class in the com.example.departmentservice.model package:
package com.example.departmentservice.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters and Setters
}
3.4 Create Department Repository
Create a DepartmentRepository interface in the com.example.departmentservice.repository package:
package com.example.departmentservice.repository;
import com.example.departmentservice.model.Department;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface DepartmentRepository extends JpaRepository {
}
3.5 Create Department Controller
Create a DepartmentController class in the com.example.departmentservice.controller package:
package com.example.departmentservice.controller;
import com.example.departmentservice.model.Department;
import com.example.departmentservice.repository.DepartmentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/departments")
public class DepartmentController {
private final DepartmentRepository departmentRepository;
@Autowired
public DepartmentController(DepartmentRepository departmentRepository) {
this.departmentRepository = departmentRepository;
}
@GetMapping
public List getAllDepartments() {
return departmentRepository.findAll();
}
@PostMapping
public Department createDepartment(@RequestBody Department department) {
return departmentRepository.save(department);
}
}
3.6 Enable Discovery Client
Annotate the main application class with @EnableDiscoveryClient:
package com.example.departmentservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class DepartmentServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DepartmentServiceApplication.class, args);
}
}
3.7 Run the Department Service
Run the application by executing the DepartmentServiceApplication class. The service should register
itself with
the Eureka server.
Step 4: Create the API Gateway
4.1 Create a Spring Boot Project
1. Open Spring Initializr and configure a new project with the following metadata:
- Group: com.example
- Artifact: api-gateway
- Name: api-gateway
- Package Name: com.example.apigateway
- Dependencies: Spring Cloud Gateway, Spring Cloud Discovery
2.Generate the Project and open it in your IDE.
4.2 Update application.yml
Create an application.yml file in the src/main/resources directory:
server:
port: 8080
spring:
application:
name: api-gateway
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
spring:
cloud:
gateway:
routes:
- id: employee_service
uri: lb://employee-service
predicates:
- Path=/employees/**
- id: department_service
uri: lb://department-service
predicates:
- Path=/departments/**
4.3 Enable Discovery Client
Annotate the main application class with @EnableDiscoveryClient:
package com.example.apigateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
4.4 Run the API Gateway
Run the application by executing the ApiGatewayApplication class. The API Gateway should register itself with the Eureka server.
Step 5: Create the Angular Client
5.1 Create an Angular Project
1.Open a terminal and run the following command to create a new Angular project:
ng new client-app
2.Navigate to the project directory:
cd client-app
5.2 Install Dependencies
Install Bootstrap for styling:
npm install bootstrap
Add Bootstrap to angular.json:
"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
5.3 Create Angular Services and Components
5.3.1 Create API Service
Generate the ApiService:
ng generate service services/api
Edit api.service.ts:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ApiService {
private baseUrl = 'http://localhost:8080';
constructor(private http: HttpClient) { }
getEmployees(): Observable {
return this.http.get(`${this.baseUrl}/employees`);
}
getDepartments(): Observable {
return this.http.get(`${this.baseUrl}/departments`);
}
}
Explanation:
- @Injectable({ providedIn: 'root' }): Marks the service as injectable and available throughout the app.
- HttpClient: Service for making HTTP requests.
- getEmployees(): Sends a GET request to the employee service via the API Gateway.
- getDepartments(): Sends a GET request to the department service via the API Gateway.
5.3.2 Create Components
Generate the components for displaying employees and departments:
ng generate component components/employees
ng generate component components/departments
Edit employees.component.ts:
import { Component, OnInit } from '@angular/core';
import { ApiService } from '../../services/api.service';
@Component({
selector: 'app-employees',
templateUrl: './employees.component.html',
styleUrls: ['./employees.component.css']
})
export class EmployeesComponent implements OnInit {
employees: any[] = [];
constructor(private apiService: ApiService) { }
ngOnInit(): void {
this.apiService.getEmployees().subscribe(data => {
this.employees = data;
});
}
}
Edit employees.component.html:
< div class="container mt-5">
< div class="row">
< div class="col-md-12">
< h2>Employees< /h2>
< table class="table table-striped">
< thead>
< tr>
< th>ID< /th>
< th>Name< /th>
< th>Department< /th>
< /tr>
< /thead>
< tbody>
< tr *ngFor="let employee of employees">
< td>{{ employee.id }}< /td>
< td>{{ employee.name }}< /td>
< td>{{ employee.department }}< /td>
< /tr>
< /tbody>
< /table>
< /div>
< /div>
< /div>
Edit departments.component.ts:
import { Component, OnInit } from '@angular/core';
import { ApiService } from '../../services/api.service';
@Component({
selector: 'app-departments',
templateUrl: './departments.component.html',
styleUrls: ['./departments.component.css']
})
export class DepartmentsComponent implements OnInit {
departments: any[] = [];
constructor(private apiService: ApiService) { }
ngOnInit(): void {
this.apiService.getDepartments().subscribe(data => {
this.departments = data;
});
}
}
Edit departments.component.html:
< div class="container mt-5">
< div class="row">
< div class="col-md-12">
< h2>Departments< /h2>
< table class="table table-striped">
< thead>
< tr>
< th>ID
< th>Name
< /tr>
< /thead>
< tbody>
< tr *ngFor="let department of departments">
< td>{{ department.id }}< /td>
< td>{{ department.name }}< /td>
< /tr>
< /tbody>
< /table>
< /div>
< /div>
< /div>
5.4 Update Angular Routing
Edit app-routing.module.ts:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { EmployeesComponent } from './components/employees/employees.component';
import { DepartmentsComponent } from './components/departments/departments.component';
const routes: Routes = [
{ path: 'employees', component: EmployeesComponent },
{ path: 'departments', component: DepartmentsComponent },
{ path: '', redirectTo: '/employees', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Explanation:
- Defines routes for the employees and departments components.
- Redirects the root path to the employees component.
5.5 Update Angular App Module
Edit app.module.ts:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { EmployeesComponent } from './components/employees/employees.component';
import { DepartmentsComponent } from './components/departments/departments.component';
@NgModule({
declarations: [
AppComponent,
EmployeesComponent,
DepartmentsComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Explanation:
5.6 Run the Angular Application
Open a terminal in the Angular project directory and run the application:
ng serve
Visit http://localhost:4200 in your web browser to see the application.
Handling CORS Issue
CORS (Cross-Origin Resource Sharing) issues occur when a web application running at one origin
(domain)
tries to make requests to a resource at a different origin (domain). Browsers enforce the
same-origin policy for
security reasons, which restricts how resources on one origin can interact
with resources on another origin. If the
server does not explicitly allow requests from the
requesting origin by including appropriate CORS headers, the
browser blocks the request, resulting
in a CORS issue.
To handle CORS (Cross-Origin Resource Sharing) issues in the API Gateway, you need to configure CORS
settings in the application.yml file and create a CORS configuration class in the API Gateway
project. Here's how
you can do it:
Update application.yml to Include CORS Configuration
Add the CORS configuration under spring.cloud.gateway in the application.yml file:
server:
port: 8080
spring:
application:
name: api-gateway
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
cloud:
gateway:
routes:
- id: employee_service
uri: lb://employee-service
predicates:
- Path=/employees/**
- id: department_service
uri: lb://department-service
predicates:
- Path=/departments/**
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
allowedHeaders:
- "*"
Create a CORS Configuration Class
Create a class CorsConfiguration in the com.example.apigateway.config package:
package com.example.apigateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.addAllowedOrigin("*");
corsConfig.addAllowedMethod("*");
corsConfig.addAllowedHeader("*");
corsConfig.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig);
return new CorsWebFilter(source);
}
}
Explanation
- CorsConfiguration class defines the CORS configuration.
- CorsWebFilter bean applies the CORS configuration to all routes.
- CorsWebFilter bean applies the CORS configuration to all routes.
With this configuration, CORS is globally enabled for all routes handled by the API Gateway, allowing
requests
from any origin with any HTTP method and headers. This should resolve any CORS issues when
your Angular
client makes requests to the API Gateway.
Now, your API Gateway will properly handle CORS issues.
Conclusion
In this tutorial, we created a microservices architecture using Spring Boot and Angular. We built two
microservices (Employee Service and Department Service), an API Gateway, a Service Registry, and an
Angular
client that interacts with the microservices through the API Gateway. This setup provides a
scalable and
maintainable architecture for building enterprise-level applications.