Spring Boot +Angular+Consul Gateway

Backend (Spring Boot)

dependencies


				 <!-- Spring Boot Starter for Spring Data JPA -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <!-- MySQL JDBC Driver -->
  <dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
  </dependency>
  
  


application.yml

				

spring:
  application:
    name: client-a

  cloud:
    consul:
      host: localhost
      port: 8500
      config:
        enabled: true
        prefix: config


  config:
    import: "consul:"

  datasource:
    url: jdbc:mysql://localhost:3306/dbclients?createDatabaseIfNotExist=true&useSSL=false&serverTimezone=UTC
    username: root
    password:
    driver-class-name: com.mysql.cj.jdbc.Driver

  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    database-platform: org.hibernate.dialect.MySQLDialect

management:
  endpoints:
    web:
      exposure:
        include: health,info,refresh

server:
  port: 8080

Client Entity


package com.emsi.models;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Column;
import jakarta.persistence.Table;

@Entity
@Table(name = "clients")  // Specify the table name
public class Client {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")  // Optional, but can be useful for clarity or customization
    private Long id;

    @Column(name = "nom", nullable = false, length = 100)  // Specifies non-null and a maximum length of 100 characters
    private String nom;

    @Column(name = "prenom", nullable = false, length = 100)
    private String prenom;

    @Column(name = "tel", length = 15)  // Limits the phone number length to 15 characters
    private String tel;

    @Column(name = "email", nullable = false, unique = true)  // Ensures email is unique and non-null
    private String email;

    @Column(name = "password", nullable = false)
    private String password;

    // Constructor
    public Client() {
    }

    // Getters and Setters

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    public String getPrenom() {
        return prenom;
    }

    public void setPrenom(String prenom) {
        this.prenom = prenom;
    }

    public String getTel() {
        return tel;
    }

    public void setTel(String tel) {
        this.tel = tel;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}


Client Repository

package com.emsi.repositories;

import org.springframework.data.jpa.repository.JpaRepository;

import com.emsi.models.Client;

public interface ClientRepository extends JpaRepository<Client, Long> {
}

Service Layer

package com.emsi.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.emsi.models.Client;
import com.emsi.repositories.ClientRepository;

import java.util.List;

@Service
public class ClientService {

    @Autowired
    private ClientRepository clientRepository;

    public List getAllClients() {
        return clientRepository.findAll();
    }

    public Client getClientById(Long id) {
        return clientRepository.findById(id).orElse(null);
    }

    public Client saveClient(Client client) {
        return clientRepository.save(client);
    }

    public void deleteClient(Long id) {
        clientRepository.deleteById(id);
    }
}

REST Controller

package com.emsi.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import com.emsi.models.Client;
import com.emsi.services.ClientService;

import java.util.List;

@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api/clients")
public class ClientController {

    @Autowired
    private ClientService clientService;

    @GetMapping
    public List getAllClients() {
        return clientService.getAllClients();
    }

    @GetMapping("/{id}")
    public Client getClientById(@PathVariable Long id) {
        return clientService.getClientById(id);
    }

    @PostMapping
    public Client saveClient(@RequestBody Client client) {
        return clientService.saveClient(client);
    }

    @PutMapping("/{id}")
    public Client updateClient(@PathVariable Long id, @RequestBody Client client) {
        client.setId(id);
        return clientService.saveClient(client);
    }

    @DeleteMapping("/{id}")
    public void deleteClient(@PathVariable Long id) {
        clientService.deleteClient(id);
    }
}

Consul Gateway

Create a Spring Boot project named api-gateway.

pom.xml



 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
Configure application.yml for the gateway:
spring:
  application:
    name: api-gateway

  cloud:
    consul:
      discovery:
        enabled: true
        register: true
    gateway:
      routes:
        - id: client-a
          uri: lb://client-a
          predicates:
            - Path=/api/clients/**

  profiles:
    active: consul

server:
  port: 8080


lb://product-service and lb://order-service use Consul to resolve the actual service locations.

main application class.xml


@SpringBootApplication
public class ApiGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
}
In Consul's UI (typically at http://localhost:8500), you should see product-service, order-service, and api-gateway registered.
Access the endpoints via the gateway:
http://localhost:8080/clients → routes to clients-service

Frontend (Angular)

Étapes à suivre

1.Installer Node.js :

Télécharger Node.js

node -v

2.Installer Angular CLI :

Angular CLI est l'outil officiel pour créer et gérer des projets Angular.

npm install -g @angular/cli

Vérifiez que l'installation est correcte :

ng version

3.Créer un nouveau projet Angular :

ng new angularprojectOne --no-standalone --routing

4.Exécuter le projet Angular :

cd angularprojectOne
ng serve

Ouvrez votre navigateur et accédez à http://localhost:4200 pour voir votre application en action.

Client Model :src/app/models/client.ts


export class Client {
  id!: number;
  nom!: string;
  prenom!: string;
  tel!: string;
  email!: string;
  password!: string;
}

Services

src/app/services/client.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Client } from '../models/client';

@Injectable({
  providedIn: 'root'
})
export class ClientService {
  private baseUrl = 'http://localhost:8097/api/clients';

  constructor(private http: HttpClient) {}

  getClients(): Observable<Client[]> {
    return this.http.get<Client[]>(this.baseUrl);
  }

  getClient(id: number): Observable<Client> {
    return this.http.get<Client>(`${this.baseUrl}/${id}`);
  }

  createClient(client: Client): Observable<Client> {
    return this.http.post<Client>(this.baseUrl, client);
  }

  updateClient(id: number, client: Client): Observable<Client> {
    return this.http.put<Client>(`${this.baseUrl}/${id}`, client);
  }

  deleteClient(id: number): Observable<void> {
    return this.http.delete<void>(`${this.baseUrl}/${id}`);
  }
}



src/app/components/client-list/client-list.component.html


<div class="client-list-card"> 
  <h2>Client List</h2>
  <a routerLink="/add-client">Add Client</a>

  <table class="client-table">
    <thead>
      <tr>
        <th>Nom</th>
        <th>Prénom</th>
        <th>Téléphone</th>
        <th>Email</th>
        <th>Password</th>
        <th>Actions</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let client of clients">
        <td>{{ client.nom }}</td>
        <td>{{ client.prenom }}</td>
        <td>{{ client.tel }}</td>
        <td>{{ client.email }}</td>
        <td>{{ client.password }}</td>
        <td>
          <a [routerLink]="['/edit-client', client.id]">Edit</a>
          <button (click)="deleteClient(client.id)">Delete</button>
        </td>
      </tr>
    </tbody>
  </table>
</div>



client-list.component.css

.client-list-card {
    max-width: 800px;
    margin: 20px auto;
    padding: 20px;
    border: 1px solid #ccc;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    background-color: #fff;
  }
  
  /* Heading */
  .client-list-card h2 {
    font-size: 24px;
    margin-bottom: 20px;
  }
  
  /* Button styling */
  .add-client-btn {
    padding: 10px 20px;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 16px;
  }
  
  .add-client-btn:hover {
    background-color: #0056b3;
  }
  
  /* Table styling */
  .client-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 20px;
  }
  
  .client-table th, .client-table td {
    padding: 12px 15px;
    border: 1px solid #ddd;
    text-align: left;
  }
  
  .client-table th {
    background-color: #f2f2f2;
    font-weight: bold;
  }
  
  .client-table tr:nth-child(even) {
    background-color: #f9f9f9;
  }
  
  .client-table tr:hover {
    background-color: #f1f1f1;
  }

src/app/components/client-list/client-list.component.ts

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { ClientService } from '../../services/client.service';
import { Client } from '../../models/client';

@Component({
  selector: 'app-client-list',
  standalone: true,
  imports: [CommonModule, RouterModule],
  templateUrl: './client-list.component.html',
  styleUrls: ['./client-list.component.css']
})
export class ClientListComponent implements OnInit {
  clients: Client[] = [];

  constructor(private clientService: ClientService) {}

  ngOnInit(): void {
    this.loadClients();
  }

  loadClients(): void {
    this.clientService.getClients().subscribe((data: Client[]) => {
      this.clients = data;
    });
  }

  deleteClient(id: number): void {
    if (confirm('Are you sure you want to delete this client?')) {
      this.clientService.deleteClient(id).subscribe(() => {
        this.clients = this.clients.filter(client => client.id !== id);
      });
    }
  }
}



client-add.component.html


<div>
    <form (ngSubmit)="addClient()">
     <div>
       <label for="nom">Nom:</label>
       <input type="text" id="nom" [(ngModel)]="client.nom" name="nom" required />
     </div>
     <div>
       <label for="prenom">Prénom:</label>
       <input type="text" id="prenom" [(ngModel)]="client.prenom" name="prenom" required />
     </div>
     <div>
       <label for="tel">Téléphone:</label>
       <input type="text" id="tel" [(ngModel)]="client.tel" name="tel" required />
     </div>
     <div>
       <label for="email">Email:</label>
       <input type="email" id="email" [(ngModel)]="client.email" name="email" required />
     </div>
     <div>
       <label for="password">Password:</label>
       <input type="password" id="password" [(ngModel)]="client.password" name="password" required />
     </div>
     <button type="submit">{{ client.id ? 'Update' : 'Add' }}</button>
   </form>
 </div>
 

client-add.component.ts


import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { Client } from '../../models/Client';
import { ClientService } from '../../services/client.service';
@Component({
  selector: 'app-client-add',
  templateUrl: './client-add.component.html',
  styleUrls: ['./client-add.component.css']
})
export class client-addComponent {
  client: Client = new Client();

  constructor(private clientService: ClientService, private router: Router) {}

  addClient(): void {
    this.clientService.createClient(this.client).subscribe(() => {
      this.router.navigate(['/clients']);
    });
  }
}


client-edit.component.html


<div>
     <form (ngSubmit)=" updateClient()">
      <div>
        <label for="nom">Nom:</label>
        <input type="text" id="nom" [(ngModel)]="client.nom" name="nom" required />
      </div>
      <div>
        <label for="prenom">Prénom:</label>
        <input type="text" id="prenom" [(ngModel)]="client.prenom" name="prenom" required />
      </div>
      <div>
        <label for="tel">Téléphone:</label>
        <input type="text" id="tel" [(ngModel)]="client.tel" name="tel" required />
      </div>
      <div>
        <label for="email">Email:</label>
        <input type="email" id="email" [(ngModel)]="client.email" name="email" required />
      </div>
      <div>
        <label for="password">Password:</label>
        <input type="password" id="password" [(ngModel)]="client.password" name="password" required />
      </div>
      <button type="submit">{{ client.id ? 'Update' : 'Add' }}</button>
    </form>
  </div>
  

client-edit.component.ts


import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ClientService } from '../../services/client.service';
import { Client } from '../../models/Client';

@Component({
  selector: 'app-client-edit',
  templateUrl: './client-edit.component.html',
  styleUrls: ['./client-edit.component.css']
})
export class ClientEditComponent implements OnInit {
  client: Client = new Client();

  constructor(
    private clientService: ClientService,
    private route: ActivatedRoute,
    private router: Router
  ) {}

  ngOnInit(): void {
    const id = this.route.snapshot.params['id'];
    this.clientService.getClient(id).subscribe(data => {
      this.client = data;
    });
  }

  updateClient(): void {
    const id = this.route.snapshot.params['id'];
    this.clientService.updateClient(id, this.client).subscribe(() => {
      this.router.navigate(['/clients']);
    });
  }
}

app-routing.module.ts


import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ClientListComponent } from './components/clients/list/list.component';
import { client-addComponent } from './components/client-add/client-add.component';
import { ClientEditComponent } from './components/client-edit/client-edit.component';

const routes: Routes = [
  { path: '', redirectTo: '/clients', pathMatch: 'full' },
  { path: 'clients', component: ClientListComponent },
  { path: 'add-client', component: client-addComponent },
  { path: 'edit-client/:id', component: ClientEditComponent }
];

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

app.module.ts


import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http'; // Import the HttpClientModule

import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { FormsModule } from '@angular/forms'; 

import { AppComponent } from './app.component';
import { ClientListComponent } from './components/clients/list/list.component';
import { client-addComponent } from './components/client-add/client-add.component';
import { ClientEditComponent } from './components/client-edit/client-edit.component';

@NgModule({
  declarations: [
    AppComponent,
    ClientListComponent,
    client-addComponent,
    ClientEditComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})

export class AppModule { }

Running the Application

Start the Spring Boot backend:

mvn spring-boot:run
Start the Angular frontend:

ng serve
http://localhost:4200