Spring Boot +Angular+Consul Gateway

Backend (Spring Boot)

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 {
}

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.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
Configure application.yml for the gateway:

spring:
  application:
    name: api-gateway
  cloud:
    consul:
      discovery:
        enabled: true
        register: true
  profiles:
    active: consul
server:
  port: 8080

cloud:
  gateway:
    routes:
      - id: product-service
        uri: lb://product-service
        predicates:
          - Path=/products/**
      - id: client-service
        uri: lb://client-service
        predicates:
          - Path=/clients/**
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/products → routes to product-service
http://localhost:8080/clients → routes to clients-service

Frontend (Angular)

Introduction

Angular est une plateforme et un framework de développement pour construire des applications web en utilisant TypeScript. Ce tutoriel vous guidera à travers les étapes nécessaires pour installer et configurer Angular sur votre machine.

Étapes à suivre

1.Installer Node.js :

Angular nécessite Node.js pour fonctionner. Téléchargez et installez la dernière version de Node.js à partir du site officiel :

Télécharger Node.js

Vérifiez que Node.js est bien installé en exécutant la commande suivante dans un terminal :

node -v

2.Installer Angular CLI :

Angular CLI est l'outil officiel pour créer et gérer des projets Angular. Pour l'installer, exécutez la commande suivante :

npm install -g @angular/cli

Vérifiez que l'installation est correcte :

ng version

3.Créer un nouveau projet Angular :

Une fois Angular CLI installé, vous pouvez créer un nouveau projet avec la commande :

ng new angularprojectOne --no-standalone --routing --ssr=false

Suivez les instructions affichées pour configurer votre projet.

4.Exécuter le projet Angular :

Après avoir créé votre projet, entrez dans le dossier du projet :

cd angularprojectOne

Puis exécutez l'application avec la commande suivante :

ng serve

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

6.Structure d'un projet Angular :

Angular utilise une architecture modulaire. Voici une brève description des répertoires principaux :

  • src/ : Contient tout le code source de votre application.
  • app/ : Contient les composants et les services de votre application.
  • assets/ : Contient les fichiers statiques comme les images.

7.Déployer l'application :

Pour générer une version de production de votre application Angular, exécutez :

ng build --prod

Les fichiers de votre application prête à être déployée se trouvent dans le répertoire dist/.

Services


ng generate service services/client

ClientService

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'; // Your Spring Boot API base URL

  constructor(private http: HttpClient) { }

  // Fetch all clients
  getClients(): Observable {
    return this.http.get(this.baseUrl);
  }

  // Fetch a single client by ID
  getClient(id: number): Observable {
    return this.http.get(`${this.baseUrl}/${id}`);
  }

  // Create a new client
  createClient(client: Client): Observable {
    return this.http.post(this.baseUrl, client);
  }

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

  // Delete a client
  deleteClient(id: number): Observable {
    return this.http.delete(`${this.baseUrl}/${id}`);
  }
}


Client Model :models/Clients


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

client-list.component.html


ng generate component components/client-list
<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;
  }

CRUD Operations in client/list.component.ts

import { Component, OnInit } from '@angular/core';

import { Client } from '../../../models/Client';
import { ClientService } from '../../../services/client.service';
@Component({
  selector: 'app-client-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.css']
})
export class ClientListComponent implements OnInit {
  clients: Client[] = [];

  constructor(private clientService: ClientService) {}

  ngOnInit(): void {
    this.clientService.getClients().subscribe(data => {
      this.clients = data;
    });
  }

  deleteClient(id: number): void {
    this.clientService.deleteClient(id).subscribe(() => {
      this.clients = this.clients.filter(client => client.id !== id);
    });
  }
}


ng generate component components/client-add

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 ClientAddComponent {
  client: Client = new Client();

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

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

ng generate component components/client-edit

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 { ClientAddComponent } 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: ClientAddComponent },
  { 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 { ClientAddComponent } from './components/client-add/client-add.component';
import { ClientEditComponent } from './components/client-edit/client-edit.component';

@NgModule({
  declarations: [
    AppComponent,
    ClientListComponent,
    ClientAddComponent,
    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