API REST i Mètodes HTTP¶
Introducció¶
REST (Representational State Transfer) és una arquitectura de disseny per a serveis web que utilitza el protocol HTTP per a la comunicació entre cliente i servidor. Una API REST és la implementació pràctica d'aquesta arquitectura, proporcionant una interfície uniforme i estàndard per accedir a recursos del servidor.
L'API REST es basa en el concepte que tot és un recurso (usuaris, productes, articles, etc.) identificat per una URL única.
Exemples d'URLs REST
http://api.example.com/api/usuarios- Accés a la col·lecció de usuarishttp://api.example.com/api/usuarios/5- Accés al usuari amb ID 5
Per què es fa servir API REST?¶
Avantatges principals¶
Beneficis de REST
- Escalabilitat: Permet suportar un gran nombre de clients i interaccions simultànies
- Simplicitat: Utilitza estàndards ja existents (HTTP) que tota plataforma entén
- Compatibilitat: Funciona en qualsevol client HTTP (navegadors, mòbils, escriptori)
- Separació de responsabilitats: Front-end i back-end es desenvolupen independentment
- Sense estat (Stateless): Cada petició conté tota la informació necessària
- Reutilització: La mateixa API pot ser utilitzada per múltiples clients
- Facilitat de prova: Es pot provar amb eines com Postman, Insomnia, etc.
Mètodes HTTP (CRUD)¶
Els mètodes HTTP defineixen quina operació es vol realitzar sobre un recurs. Els principals són:
GET - Obtenir dades¶
- Propòsit: Recuperar informació d'un recurs
- Idempotent: ✅ Sí
- Envia dades: Via paràmetres a la URL
GET /api/usuarios
GET /api/usuarios/5
GET /api/usuarios?edad=25
HTTP/1.1 200 OK
[
{ "id": 1, "nombre": "Juan", "edad": 30 },
{ "id": 2, "nombre": "María", "edad": 28 }
]
POST - Crear dades¶
- Propòsit: Crear un nou recurs al servidor
- Idempotent: ❌ No
- Envia dades: Al cos de la petició (body)
POST /api/usuarios
Content-Type: application/json
{
"nombre": "Juan",
"edad": 30,
"email": "juan@example.com"
}
HTTP/1.1 201 Created
{
"id": 3,
"nombre": "Juan",
"edad": 30,
"email": "juan@example.com"
}
PUT - Actualitzar dades¶
- Propòsit: Actualitzar completament un recurs existent
- Idempotent: ✅ Sí
- Envia dades: Al cos de la petició (body)
PUT /api/usuarios/5
Content-Type: application/json
{
"nombre": "Juan",
"edad": 31,
"email": "juan.nuevo@example.com"
}
HTTP/1.1 200 OK
{
"id": 5,
"nombre": "Juan",
"edad": 31,
"email": "juan.nuevo@example.com"
}
DELETE - Eliminar dades¶
- Propòsit: Eliminar un recurs del servidor
- Idempotent: ✅ Sí
- Envia dades: No (identificador a la URL)
DELETE /api/usuarios/5
HTTP/1.1 204 No Content
Taula CRUD¶
| Operació | Mètode | Propòsit | Idempotent |
|---|---|---|---|
| CREATE | POST | Crear un nou recurs | ❌ No |
| READ | GET | Llegir/obtenir dades | ✅ Sí |
| UPDATE | PUT | Actualitzar un recurs | ✅ Sí |
| DELETE | DELETE | Eliminar un recurs | ✅ Sí |
Codis de resposta HTTP¶
Els codis de resposta HTTP indiquen l'estat de la petició:
2xx - Èxit
200 OK: La petició s'ha processat correctament201 Created: S'ha creat correctament (POST)204 No Content: Sense contingut (DELETE)
4xx - Error del client
400 Bad Request: La petició és incorrecta401 Unauthorized: No autenticat403 Forbidden: Sense permisos404 Not Found: Recurs no trobat
5xx - Error del servidor
500 Internal Server Error: Error intern del servidor503 Service Unavailable: Servei no disponible
Backend - Spring Boot¶
Crear un controlador REST¶
La anotació @RestController marca una classe com controlador REST. Els mètodes es defineixen amb les anotacions apropiadese según el mètode HTTP:
@RestController
@RequestMapping("/api/usuarios")
public class UsuarioController {
@Autowired
private UsuarioService usuarioService;
// GET - Obtenir tots els usuaris
@GetMapping
public List<Usuario> obtenerTodos() {
return usuarioService.findAll();
}
// GET - Obtenir un usuari per ID
@GetMapping("/{id}")
public Usuario obtenerPorId(@PathVariable Long id) {
return usuarioService.findById(id);
}
// POST - Crear un nou usuari
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Usuario crear(@RequestBody Usuario usuario) {
return usuarioService.save(usuario);
}
// PUT - Actualitzar un usuari
@PutMapping("/{id}")
public Usuario actualizar(@PathVariable Long id,
@RequestBody Usuario usuario) {
usuario.setId(id);
return usuarioService.save(usuario);
}
// DELETE - Eliminar un usuari
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void eliminar(@PathVariable Long id) {
usuarioService.deleteById(id);
}
}
Anotacions Spring Boot¶
Anotacions clau
@RestController: Marca la classe com controlador REST@RequestMapping: Define la ruta base de l'API@GetMapping,@PostMapping,@PutMapping,@DeleteMapping: Defineixen el mètode HTTP@PathVariable: Obté paràmetres de la URL@RequestBody: Obté les dades del cos de la petició@ResponseStatus: Define el codi de resposta HTTP@Autowired: Injecció de dependències
Configurar CORS¶
Per permetre que Angular (port 4200) accedisca a Spring Boot (port 8080), es necessita configurar CORS:
@Configuration
public class CorsConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:4200")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
};
}
}
Nota sobre CORS
CORS (Cross-Origin Resource Sharing) és un mecanisme de seguretat dels navegadors que permet restringir les peticions HTTP des de dominis externs. Sense aquesta configuració, els navegadors bloquejarien les peticions del frontend Angular al backend Spring Boot.
Frontend - Angular¶
Crear un servei¶
El servei utilitza HttpClient per comunicar-se amb el servidor. Els serveis en Angular són classes injectables que encapstulen la lògica de comunicació:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UsuarioService {
private apiUrl = 'http://localhost:8080/api/usuarios';
constructor(private http: HttpClient) { }
// GET - Obtenir tots els usuaris
obtenerTodos(): Observable<Usuario[]> {
return this.http.get<Usuario[]>(this.apiUrl);
}
// GET - Obtenir un usuari per ID
obtenerPorId(id: number): Observable<Usuario> {
return this.http.get<Usuario>(`${this.apiUrl}/${id}`);
}
// POST - Crear un nou usuari
crear(usuario: Usuario): Observable<Usuario> {
return this.http.post<Usuario>(this.apiUrl, usuario);
}
// PUT - Actualitzar un usuari
actualizar(id: number, usuario: Usuario): Observable<Usuario> {
return this.http.put<Usuario>(`${this.apiUrl}/${id}`, usuario);
}
// DELETE - Eliminar un usuari
eliminar(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
}
Configurar HttpClientModule¶
Al arxiu app.module.ts, importar HttpClientModule:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [ AppComponent ],
imports: [
BrowserModule,
HttpClientModule // ← Importar el mòdul
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Utilitzar el servei en un component¶
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-usuarios',
templateUrl: './usuarios.component.html'
})
export class UsuariosComponent implements OnInit {
usuarios: Usuario[] = [];
constructor(private usuarioService: UsuarioService) { }
ngOnInit() {
this.obtenerUsuarios();
}
// Obtenir tots els usuaris
obtenerUsuarios() {
this.usuarioService.obtenerTodos().subscribe(
(datos) => {
this.usuarios = datos;
},
(error) => {
console.error('Error al obtenir usuaris:', error);
}
);
}
// Crear un nou usuari
crearUsuario(nuevoUsuario: Usuario) {
this.usuarioService.crear(nuevoUsuario).subscribe(
(usuario) => {
this.usuarios.push(usuario);
},
(error) => {
console.error('Error al crear usuari:', error);
}
);
}
// Actualitzar un usuari
actualizarUsuario(id: number, usuarioActualizado: Usuario) {
this.usuarioService.actualizar(id, usuarioActualizado).subscribe(
(usuario) => {
const index = this.usuarios.findIndex(u => u.id === id);
if (index !== -1) {
this.usuarios[index] = usuario;
}
}
);
}
// Eliminar un usuari
eliminarUsuario(id: number) {
this.usuarioService.eliminar(id).subscribe(
() => {
this.usuarios = this.usuarios.filter(u => u.id !== id);
}
);
}
}
Observables en Angular¶
Què són els Observables?
Els Observables són una manera en Angular de gestionar operacions asíncrones. Representen un flux de dades que pot arribar en el futur. S'utilitza el mètode subscribe() per a rebre les dades quan estan disponibles.
Cicle de vida d'una petició HTTP¶
1. Component criday el mètode del servei
↓
2. Servei fa la petició HTTP i retorna un Observable
↓
3. Component fa subscribe() al Observable
↓
4. Servidor processa la petició
↓
5. Servidor retorna la resposta
↓
6. El component rep les dades en el callback de subscribe
↓
7. El component actualitza la vista
Flux de comunicació REST¶
sequenceDiagram
participant Client as Cliente (Angular)
participant Server as Servidor (Spring Boot)
Client->>Server: GET /api/usuarios
Server->>Server: Busca dades
Server-->>Client: [dades JSON] 200 OK
Client->>Server: POST /api/usuarios
Note over Client: {dades del recurs}
Server->>Server: Crea el recurs
Server-->>Client: {recurs creat} 201 Created
Client->>Server: PUT /api/usuarios/5
Note over Client: {dades actualitzades}
Server->>Server: Actualitza
Server-->>Client: {recurs actualitzat} 200 OK
Client->>Server: DELETE /api/usuarios/5
Server->>Server: Elimina
Server-->>Client: 204 No Content
Avantatges de la separació client-servidor¶
Beneficis de l'arquitectura separada
- Independència: Front-end i back-end es desenvolupen per separat
- Flexibilitat: Es pot canviar la interfície sense tocar la lògica del servidor
- Reutilització: El mateix servidor pot servir múltiples clients (web, mòbil, escriptori)
- Mantenibilitat: Més fàcil de mantenir i actualitzar
- Escalabilitat: Cada part es pot escalar independentment
Eines útils per a prova¶
Eines per a probar APIs REST
- Postman: Aplicació desktop per provar APIs REST amb interfície visual
- Insomnia: Alternativa a Postman, similar però més lleugera
- Thunder Client: Extensió de VS Code integrada a l'editor
- curl: Utilitat de línia de comandos per fer peticions HTTP
- REST Client: Extensió de VS Code per crear arxius .rest
Checklist de desenvolupament¶
Checklist d'implementació
- Crear els models/entitats (Usuario.java)
- Crear el repository (UsuarioRepository)
- Crear el servei (UsuarioService)
- Crear el controlador REST (@RestController)
- Configurar CORS al backend
- Provar els endpoints amb Postman
- Crear el servei Angular (UsuarioService)
- Importar HttpClientModule al app.module.ts
- Crear els components Angular
- Subscriure's als Observables
- Actualitzar la vista amb les dades
- Provar l'aplicació completa
Conclusió¶
API REST és l'estàndard actual per a la comunicació entre aplicacions web. Amb Spring Boot al backend per exposar els endpoints REST i Angular al frontend per consumir-los mitjançant HttpClient, es pot construir aplicacions web modernes, escalables i mantenibles que segueixen les millors pràctiques de desenvolupament web.
Resum final
- REST utilitza HTTP de manera estàndard i segura
- Els 4 mètodes principals (GET, POST, PUT, DELETE) cobreixen totes les operacions CRUD
- Spring Boot simplifica la creació de APIs REST
- Angular amb HttpClient facilita la comunicació client-servidor
- La separació entre client i servidor proporciona flexibilitat i escalabilitat