POST, PUT, PATCH, DELETE

Intermedio
Angular
Angular
Actualizado: 24/09/2025

Métodos POST, PUT, DELETE

Una vez que dominamos las peticiones GET para obtener datos del servidor, necesitamos aprender los métodos HTTP que nos permiten modificar información. Los métodos POST, PUT, PATCH y DELETE son esenciales para implementar operaciones CRUD completas en nuestras aplicaciones Angular.

Método POST para crear recursos

El método POST se utiliza para crear nuevos recursos en el servidor. A diferencia de GET, POST requiere enviar datos en el cuerpo de la petición:

import { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private http = inject(HttpClient);
  private apiUrl = 'https://jsonplaceholder.typicode.com/users';

  createUser(userData: any) {
    return this.http.post(this.apiUrl, userData);
  }
}

En el componente, utilizamos el servicio para crear un usuario:

import { Component, inject } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-user-form',
  template: `
    <button (click)="createUser()">Crear Usuario</button>
    @if (createdUser) {
      <p>Usuario creado: {{ createdUser.name }}</p>
    }
  `
})
export class UserFormComponent {
  private userService = inject(UserService);
  createdUser: any = null;

  createUser() {
    const newUser = {
      name: 'Juan Pérez',
      email: 'juan@example.com',
      username: 'juanp'
    };

    this.userService.createUser(newUser).subscribe(response => {
      this.createdUser = response;
      console.log('Usuario creado:', response);
    });
  }
}

Configuración de headers personalizados

Para operaciones de escritura, es común necesitar configurar headers específicos, especialmente el Content-Type:

import { HttpHeaders } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private http = inject(HttpClient);
  private apiUrl = 'https://jsonplaceholder.typicode.com/users';

  createUser(userData: any) {
    const headers = new HttpHeaders({
      'Content-Type': 'application/json',
      'Authorization': 'Bearer token123'
    });

    return this.http.post(this.apiUrl, userData, { headers });
  }
}

Método PUT para actualización completa

El método PUT reemplaza completamente un recurso existente. Requiere el ID del recurso y todos los campos del objeto:

updateUser(id: number, userData: any) {
  return this.http.put(`${this.apiUrl}/${id}`, userData);
}

Ejemplo de uso en el componente:

updateUser() {
  const updatedUser = {
    id: 1,
    name: 'Juan Pérez Actualizado',
    email: 'juan.nuevo@example.com',
    username: 'juanp_new',
    phone: '123-456-789'
  };

  this.userService.updateUser(1, updatedUser).subscribe(response => {
    console.log('Usuario actualizado completamente:', response);
  });
}

Método PATCH para actualización parcial

PATCH permite actualizar solo campos específicos de un recurso, sin necesidad de enviar el objeto completo:

patchUser(id: number, partialData: any) {
  return this.http.patch(`${this.apiUrl}/${id}`, partialData);
}

Comparación práctica entre PUT y PATCH:

// PUT: Actualización completa (reemplaza todo el recurso)
updateCompleteUser() {
  const fullUser = {
    id: 1,
    name: 'María González',
    email: 'maria@example.com',
    username: 'mariag',
    phone: '987-654-321',
    website: 'maria.dev'
  };
  
  this.userService.updateUser(1, fullUser).subscribe(response => {
    console.log('Usuario reemplazado:', response);
  });
}

// PATCH: Actualización parcial (solo campos modificados)
updateUserEmail() {
  const partialUpdate = {
    email: 'maria.nueva@example.com'
  };
  
  this.userService.patchUser(1, partialUpdate).subscribe(response => {
    console.log('Solo email actualizado:', response);
  });
}

Método DELETE para eliminar recursos

El método DELETE elimina un recurso específico del servidor:

deleteUser(id: number) {
  return this.http.delete(`${this.apiUrl}/${id}`);
}

Implementación en el componente:

deleteUser(userId: number) {
  this.userService.deleteUser(userId).subscribe(() => {
    console.log('Usuario eliminado exitosamente');
    // Actualizar la lista de usuarios
    this.loadUsers();
  });
}

Manejo de respuestas vacías

Algunos servidores devuelven respuestas vacías (status 204 No Content) para operaciones exitosas, especialmente con DELETE:

deleteUser(id: number) {
  return this.http.delete(`${this.apiUrl}/${id}`, { 
    observe: 'response' 
  });
}

Con observe: 'response', podemos acceder al status code completo:

deleteUser(userId: number) {
  this.userService.deleteUser(userId).subscribe(response => {
    if (response.status === 204) {
      console.log('Usuario eliminado (No Content)');
    }
  });
}

Servicio CRUD completo

Aquí tienes un ejemplo completo que implementa todas las operaciones CRUD:

@Injectable({
  providedIn: 'root'
})
export class ProductService {
  private http = inject(HttpClient);
  private apiUrl = 'https://api.ejemplo.com/products';

  // CREATE
  createProduct(product: any) {
    return this.http.post(this.apiUrl, product);
  }

  // READ
  getProducts() {
    return this.http.get(this.apiUrl);
  }

  getProduct(id: number) {
    return this.http.get(`${this.apiUrl}/${id}`);
  }

  // UPDATE (completa)
  updateProduct(id: number, product: any) {
    return this.http.put(`${this.apiUrl}/${id}`, product);
  }

  // UPDATE (parcial)
  patchProduct(id: number, changes: any) {
    return this.http.patch(`${this.apiUrl}/${id}`, changes);
  }

  // DELETE
  deleteProduct(id: number) {
    return this.http.delete(`${this.apiUrl}/${id}`, { 
      observe: 'response' 
    });
  }
}

Los métodos POST, PUT, PATCH y DELETE te permiten construir aplicaciones completas que pueden crear, modificar y eliminar datos en el servidor. La diferencia clave entre PUT y PATCH radica en que PUT reemplaza completamente el recurso, mientras que PATCH solo modifica los campos especificados.

Envío de datos al servidor

Cuando trabajamos con métodos HTTP que modifican datos en el servidor, es fundamental entender cómo preparar y enviar la información de manera correcta. Angular HttpClient ofrece múltiples formas de enviar datos, desde objetos JavaScript simples hasta archivos binarios.

Tipos de datos que podemos enviar

HttpClient acepta diferentes tipos de cuerpo en las peticiones POST, PUT y PATCH:

// Objeto JavaScript (se serializa automáticamente a JSON)
const userData = {
  name: 'Ana García',
  email: 'ana@example.com',
  age: 28
};

// String JSON manual
const jsonString = '{"name":"Ana García","email":"ana@example.com"}';

// FormData para archivos o datos de formulario
const formData = new FormData();
formData.append('name', 'Ana García');
formData.append('email', 'ana@example.com');

Envío de objetos JavaScript

La forma más común es enviar objetos JavaScript que se serializan automáticamente a JSON:

@Injectable({
  providedIn: 'root'
})
export class ProductService {
  private http = inject(HttpClient);
  private apiUrl = 'https://api.tienda.com/products';

  createProduct(productData: any) {
    // HttpClient serializa automáticamente el objeto a JSON
    return this.http.post(this.apiUrl, productData);
  }
}

Ejemplo con datos tipados:

interface Product {
  name: string;
  price: number;
  category: string;
  description?: string;
}

createProduct(product: Product) {
  return this.http.post<Product>(this.apiUrl, product);
}

En el componente:

saveProduct() {
  const newProduct: Product = {
    name: 'Laptop Gaming',
    price: 1299.99,
    category: 'Electronics',
    description: 'Laptop de alto rendimiento'
  };

  this.productService.createProduct(newProduct).subscribe(response => {
    console.log('Producto creado:', response);
  });
}

Configuración del Content-Type

Angular establece automáticamente Content-Type: application/json cuando enviamos objetos JavaScript. Para casos específicos, podemos configurarlo manualmente:

import { HttpHeaders } from '@angular/common/http';

createProduct(product: Product) {
  const headers = new HttpHeaders({
    'Content-Type': 'application/json; charset=utf-8'
  });

  return this.http.post<Product>(this.apiUrl, product, { headers });
}

Envío de FormData

Para formularios con archivos o cuando necesitamos un formato específico, utilizamos FormData:

uploadProductWithImage(productData: any, imageFile: File) {
  const formData = new FormData();
  
  // Agregar campos de texto
  formData.append('name', productData.name);
  formData.append('price', productData.price.toString());
  formData.append('category', productData.category);
  
  // Agregar archivo
  formData.append('image', imageFile, imageFile.name);

  // Con FormData, NO establecer Content-Type manualmente
  return this.http.post(this.apiUrl, formData);
}

Implementación en el componente:

onFileSelected(event: any) {
  const file = event.target.files[0];
  if (file) {
    const productData = {
      name: 'Smartphone',
      price: 699.99,
      category: 'Electronics'
    };

    this.productService.uploadProductWithImage(productData, file)
      .subscribe(response => {
        console.log('Producto con imagen creado:', response);
      });
  }
}

Serialización personalizada

En ocasiones necesitamos transformar los datos antes de enviarlos:

createUser(userData: any) {
  // Transformar fechas a formato ISO
  const processedData = {
    ...userData,
    birthDate: new Date(userData.birthDate).toISOString(),
    createdAt: new Date().toISOString()
  };

  return this.http.post(this.apiUrl, processedData);
}

Envío de arrays y datos complejos

HttpClient maneja automáticamente estructuras complejas:

createOrder(orderData: any) {
  const order = {
    customerId: 123,
    items: [
      { productId: 1, quantity: 2, price: 29.99 },
      { productId: 3, quantity: 1, price: 15.50 }
    ],
    shippingAddress: {
      street: 'Calle Mayor 123',
      city: 'Madrid',
      postalCode: '28001'
    },
    orderDate: new Date().toISOString()
  };

  return this.http.post(this.apiUrl, order);
}

Configuración de opciones adicionales

Podemos personalizar el comportamiento de la petición con opciones adicionales:

createProduct(product: Product) {
  const options = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    }),
    observe: 'response' as const,
    reportProgress: true
  };

  return this.http.post(this.apiUrl, product, options);
}

Validación de datos antes del envío

Es recomendable validar los datos antes de enviarlos al servidor:

createUser(userData: any) {
  // Validación básica
  if (!userData.email || !userData.name) {
    throw new Error('Email y nombre son obligatorios');
  }

  // Limpiar datos
  const cleanData = {
    name: userData.name.trim(),
    email: userData.email.toLowerCase().trim(),
    phone: userData.phone?.replace(/\s+/g, '') || null
  };

  return this.http.post(this.apiUrl, cleanData);
}

Ejemplo práctico: Formulario de contacto

@Component({
  selector: 'app-contact-form',
  template: `
    <form (submit)="onSubmit($event)">
      <input [(ngModel)]="contactData.name" placeholder="Nombre" required>
      <input [(ngModel)]="contactData.email" type="email" placeholder="Email" required>
      <textarea [(ngModel)]="contactData.message" placeholder="Mensaje"></textarea>
      <input type="file" (change)="onFileSelected($event)" accept="image/*">
      <button type="submit" [disabled]="isSubmitting">
        {{ isSubmitting ? 'Enviando...' : 'Enviar' }}
      </button>
    </form>
  `
})
export class ContactFormComponent {
  private contactService = inject(ContactService);
  
  contactData = {
    name: '',
    email: '',
    message: ''
  };
  
  selectedFile: File | null = null;
  isSubmitting = false;

  onFileSelected(event: any) {
    this.selectedFile = event.target.files[0];
  }

  onSubmit(event: Event) {
    event.preventDefault();
    this.isSubmitting = true;

    if (this.selectedFile) {
      // Enviar con FormData si hay archivo
      this.contactService.submitWithFile(this.contactData, this.selectedFile)
        .subscribe(this.handleResponse.bind(this));
    } else {
      // Enviar solo datos JSON
      this.contactService.submit(this.contactData)
        .subscribe(this.handleResponse.bind(this));
    }
  }

  private handleResponse(response: any) {
    console.log('Mensaje enviado:', response);
    this.isSubmitting = false;
    // Limpiar formulario
    this.contactData = { name: '', email: '', message: '' };
    this.selectedFile = null;
  }
}

El envío de datos en Angular es flexible y se adapta automáticamente al tipo de contenido. La clave está en elegir el formato adecuado según nuestras necesidades: objetos JavaScript para datos simples, FormData para archivos, y configuraciones personalizadas para casos específicos.

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en Angular

Documentación oficial de Angular
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Angular es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Angular

Explora más contenido relacionado con Angular y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

  • Comprender el uso de los métodos HTTP POST, PUT, PATCH y DELETE para operaciones CRUD en Angular.
  • Aprender a enviar datos correctamente al servidor utilizando HttpClient.
  • Diferenciar entre actualización completa (PUT) y parcial (PATCH).
  • Configurar headers personalizados y manejar respuestas vacías.
  • Implementar el envío de datos complejos, incluyendo objetos, arrays y archivos con FormData.