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

  1. Service Registry: Manages service discovery.

  2. API Gateway: Routes client requests to appropriate microservices.

  3. Employee Service: Manages employee data.

  4. Department Service: Manages department data.

  5. 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:

  • On the Dependencies screen, select:
    • Spring Cloud Discovery
  • Click Next.
  • 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:

  • Open your IDE and import the project as a Maven project.

  • 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:

  • Imports necessary modules for the Angular app.

  • Declares the components used in the app.

  • Sets up the app's root module.

  • 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.


    Related Spring and Spring Boot Tutorials/Guides: