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
2.Installer Angular CLI :
Angular CLI est l'outil officiel pour créer et gérer des projets Angular.
npm install -g @angular/cliVé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