← Blog

Cómo gestionar imágenes en producción con Cloudinary y Node.js

D
Damián Oliva
·14 de octubre de 2024

El problema de las imágenes en producción

Guardar imágenes en el servidor local funciona en desarrollo. En producción es un desastre: si escala horizontalmente tenés imágenes repartidas en múltiples instancias, si el servidor se cae perdés los archivos, y no tenés forma sencilla de hacer transformaciones (resize, webp, etc.).

Cloudinary resuelve todos estos problemas con una API simple. Lo uso en absolutamente todos mis proyectos.

Setup en Node.js

npm install cloudinary multer multer-storage-cloudinary
# .env
CLOUDINARY_CLOUD_NAME=tu_cloud_name
CLOUDINARY_API_KEY=tu_api_key
CLOUDINARY_API_SECRET=tu_api_secret
// services/cloudinary.js
import { v2 as cloudinary } from 'cloudinary';

cloudinary.config({
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
  api_key: process.env.CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET,
});

export default cloudinary;

Upload con Multer

import multer from 'multer';
import { CloudinaryStorage } from 'multer-storage-cloudinary';
import cloudinary from './cloudinary.js';

const storage = new CloudinaryStorage({
  cloudinary,
  params: {
    folder: 'mi-app/uploads',
    allowed_formats: ['jpg', 'jpeg', 'png', 'webp'],
    transformation: [{ width: 1200, crop: 'limit', quality: 'auto' }],
  },
});

export const upload = multer({ storage });

// Uso en ruta:
router.post('/foto', upload.single('image'), async (req, res) => {
  const { secure_url, public_id } = req.file;
  // Guardar en MongoDB
  res.json({ url: secure_url, publicId: public_id });
});

Transformaciones on-the-fly en la URL

Una de las features más poderosas: podés transformar imágenes directamente desde la URL sin procesamiento adicional.

// URL original
https://res.cloudinary.com/mi-cloud/image/upload/v123/foto.jpg

// Thumbnail 200x200
https://res.cloudinary.com/mi-cloud/image/upload/w_200,h_200,c_fill/v123/foto.jpg

// Webp automático, calidad auto, ancho máximo 800
https://res.cloudinary.com/mi-cloud/image/upload/f_auto,q_auto,w_800/v123/foto.jpg

Borrar imágenes al actualizar

export const deleteImage = async (publicId) => {
  if (!publicId) return;
  await cloudinary.uploader.destroy(publicId);
};

// Al actualizar foto de perfil:
if (user.profilePhoto?.publicId) {
  await deleteImage(user.profilePhoto.publicId);
}
const { secure_url, public_id } = req.file;
user.profilePhoto = { url: secure_url, publicId: public_id };
Newsletter

Ideas directamente
a tu bandeja de entrada

Comparto lo que estoy construyendo, lo que aprendo y lo que me parece interesante del mundo tech y startups. Sin spam. Cuando tenga algo que valga la pena.

Sin spam. Unsubscribe en cualquier momento.