· Nacho Coll · Guides  · 10 min de lectura

Almacenamiento de metadatos NFT: la guía completa de IPFS para creadores de NFT

Guía paso a paso para almacenar metadatos NFT en IPFS. Incluye patrones tokenURI ERC-721 y ejemplos en Python y JavaScript.

Guía paso a paso para almacenar metadatos NFT en IPFS. Incluye patrones tokenURI ERC-721 y ejemplos en Python y JavaScript.

Crear NFTs requiere más que simplemente desplegar un smart contract — necesitas un almacenamiento descentralizado y fiable para tus metadatos y activos. Esta guía completa te lleva paso a paso a través del almacenamiento de metadatos NFT en IPFS empleando las mejores prácticas del sector, con ejemplos de código para desarrolladores de Python y JavaScript.

IPFS Ninja

¿Por qué IPFS para el almacenamiento de metadatos NFT?

El alojamiento web tradicional crea riesgos de centralización para los proyectos NFT. Cuando los metadatos residen en servidores convencionales, los NFT pueden quedar «rotos» si el servicio de hosting cae o cambia las URL. IPFS (InterPlanetary File System) resuelve esto proporcionando:

  • Direccionamiento de contenido inmutable: Cada archivo obtiene un Content Identifier (CID) único que nunca cambia
  • Almacenamiento descentralizado: El contenido existe en múltiples nodos por todo el mundo
  • Verificación criptográfica: La integridad del archivo está garantizada mediante hashing de contenido
  • URL a prueba de futuro: Los enlaces IPFS funcionan indefinidamente, protegiendo el valor a largo plazo

Para una comprensión más profunda de los fundamentos de IPFS, consulta nuestra guía sobre qué es IPFS pinning.

Entendiendo la estructura de metadatos ERC-721

El estándar ERC-721 define cómo deben estructurarse los metadatos NFT. La función tokenURI de tu smart contract devuelve una URL que apunta a metadatos JSON siguiendo este patrón:

{
  "name": "My Amazing NFT #1",
  "description": "A unique digital artwork showcasing...",
  "image": "ipfs://QmYourImageCIDHere",
  "attributes": [
    {
      "trait_type": "Background",
      "value": "Blue"
    },
    {
      "trait_type": "Rarity",
      "value": "Common"
    }
  ],
  "external_url": "https://yourproject.com/token/1"
}

Campos clave de metadatos

  • name: El título del NFT mostrado en monederos y mercados
  • description: Información detallada sobre el NFT
  • image: URL de IPFS al activo visual principal
  • attributes: Propiedades basadas en rasgos para filtrado y cálculos de rareza
  • external_url: Enlace opcional a contenido adicional o al sitio web de tu proyecto

Proceso de almacenamiento de NFT en IPFS paso a paso

Paso 1: Prepara tus activos y metadatos

Antes de subir nada, organiza tus archivos:

  1. Activos principales: Imágenes, vídeos u otro contenido primario
  2. Archivos de metadatos: Archivos JSON que describen cada NFT
  3. Metadatos de colección: Información opcional a nivel de colección

Paso 2: Sube los activos a IPFS

Comienza subiendo tus activos NFT principales (imágenes, vídeos, etc.) para obtener sus IPFS CID. Harás referencia a estos CID en tus archivos JSON de metadatos.

Así puedes subir una imagen utilizando Python:

import requests
import base64
import json

def upload_image_to_ipfs(image_path, api_key):
    """Upload an image file to IPFS and return its CID"""
    
    # Read and encode image
    with open(image_path, 'rb') as f:
        image_data = base64.b64encode(f.read()).decode('utf-8')
    
    # Prepare upload payload
    payload = {
        "content": image_data,
        "description": f"NFT Asset: {image_path}"
    }
    
    headers = {
        "Content-Type": "application/json",
        "X-Api-Key": api_key
    }
    
    # Upload to IPFS.NINJA
    response = requests.post(
        "https://api.ipfs.ninja/upload/new",
        headers=headers,
        json=payload
    )
    
    if response.status_code == 200:
        result = response.json()
        print(f"✅ Image uploaded successfully!")
        print(f"CID: {result['cid']}")
        print(f"IPFS URL: {result['uris']['ipfs']}")
        print(f"Gateway URL: {result['uris']['url']}")
        return result['cid']
    else:
        print(f"❌ Upload failed: {response.text}")
        return None

# Example usage
API_KEY = "bws_1234567890abcdef1234567890abcdef"  # Replace with your actual key
image_cid = upload_image_to_ipfs("my-nft-artwork.png", API_KEY)

Paso 3: Crea y sube el JSON de metadatos

Una vez que tengas los CID de tus activos, crea archivos JSON de metadatos y súbelos:

def create_and_upload_metadata(name, description, image_cid, attributes, api_key):
    """Create NFT metadata JSON and upload to IPFS"""
    
    # Create metadata object
    metadata = {
        "name": name,
        "description": description,
        "image": f"ipfs://{image_cid}",
        "attributes": attributes
    }
    
    # Convert to JSON string and encode
    metadata_json = json.dumps(metadata, indent=2)
    metadata_b64 = base64.b64encode(metadata_json.encode('utf-8')).decode('utf-8')
    
    # Upload metadata
    payload = {
        "content": metadata_b64,
        "description": f"NFT Metadata: {name}",
        "metadata": {
            "contentType": "application/json",
            "nftTokenId": name.split('#')[1] if '#' in name else "1"
        }
    }
    
    headers = {
        "Content-Type": "application/json",
        "X-Api-Key": api_key
    }
    
    response = requests.post(
        "https://api.ipfs.ninja/upload/new",
        headers=headers,
        json=payload
    )
    
    if response.status_code == 200:
        result = response.json()
        print(f"✅ Metadata uploaded successfully!")
        print(f"Metadata CID: {result['cid']}")
        return result['cid']
    else:
        print(f"❌ Metadata upload failed: {response.text}")
        return None

# Example usage
attributes = [
    {"trait_type": "Background", "value": "Cosmic Blue"},
    {"trait_type": "Eyes", "value": "Laser"},
    {"trait_type": "Rarity", "value": "Epic"}
]

metadata_cid = create_and_upload_metadata(
    name="Cosmic Warrior #001",
    description="A fierce warrior from the distant galaxies, wielding the power of stars.",
    image_cid=image_cid,
    attributes=attributes,
    api_key=API_KEY
)

Paso 4: Implementación en JavaScript

Para aplicaciones web o proyectos Node.js, aquí tienes el equivalente en JavaScript:

class NFTStorage {
    constructor(apiKey) {
        this.apiKey = apiKey;
        this.baseUrl = 'https://api.ipfs.ninja';
    }
    
    async uploadFile(fileContent, description) {
        const payload = {
            content: fileContent, // base64 encoded
            description: description
        };
        
        const response = await fetch(`${this.baseUrl}/upload/new`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-Api-Key': this.apiKey
            },
            body: JSON.stringify(payload)
        });
        
        if (!response.ok) {
            throw new Error(`Upload failed: ${response.statusText}`);
        }
        
        return await response.json();
    }
    
    async uploadImageFromFile(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = async (e) => {
                try {
                    const base64Content = e.target.result.split(',')[1]; // Remove data:image/...;base64, prefix
                    const result = await this.uploadFile(base64Content, `NFT Image: ${file.name}`);
                    resolve(result.cid);
                } catch (error) {
                    reject(error);
                }
            };
            reader.readAsDataURL(file);
        });
    }
    
    async uploadMetadata(name, description, imageCid, attributes = []) {
        const metadata = {
            name,
            description,
            image: `ipfs://${imageCid}`,
            attributes
        };
        
        const metadataJson = JSON.stringify(metadata, null, 2);
        const base64Metadata = btoa(metadataJson);
        
        const result = await this.uploadFile(base64Metadata, `NFT Metadata: ${name}`);
        return result.cid;
    }
}

// Usage example
const storage = new NFTStorage('bws_1234567890abcdef1234567890abcdef'); // Replace with your key

// Upload process
async function createNFT() {
    try {
        // Assuming you have a file input element
        const fileInput = document.getElementById('nft-image');
        const imageFile = fileInput.files[0];
        
        console.log('Uploading image...');
        const imageCid = await storage.uploadImageFromFile(imageFile);
        console.log(`Image uploaded: ${imageCid}`);
        
        console.log('Uploading metadata...');
        const metadataCid = await storage.uploadMetadata(
            'Galaxy Explorer #042',
            'A mysterious explorer traversing the cosmic void.',
            imageCid,
            [
                { trait_type: 'Class', value: 'Explorer' },
                { trait_type: 'Galaxy', value: 'Andromeda' },
                { trait_type: 'Rarity', value: 'Legendary' }
            ]
        );
        
        console.log(`Metadata uploaded: ${metadataCid}`);
        console.log(`Token URI: ipfs://${metadataCid}`);
        
    } catch (error) {
        console.error('Upload failed:', error);
    }
}

Implementación de tokenURI en tu Smart Contract

Una vez que tus metadatos estén subidos a IPFS, implementa la función tokenURI en tu contrato ERC-721:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyNFTCollection is ERC721, Ownable {
    mapping(uint256 => string) private _tokenURIs;
    string private _baseTokenURI;
    
    constructor(string memory name, string memory symbol) ERC721(name, symbol) {}
    
    function setTokenURI(uint256 tokenId, string memory uri) external onlyOwner {
        require(_exists(tokenId), "Token does not exist");
        _tokenURIs[tokenId] = uri;
    }
    
    function setBaseURI(string memory baseURI) external onlyOwner {
        _baseTokenURI = baseURI;
    }
    
    function tokenURI(uint256 tokenId) public view virtual override returns (string) {
        require(_exists(tokenId), "Token does not exist");
        
        string memory _tokenURI = _tokenURIs[tokenId];
        
        // Return specific token URI if set
        if (bytes(_tokenURI).length > 0) {
            return _tokenURI;
        }
        
        // Fall back to base URI + token ID pattern
        if (bytes(_baseTokenURI).length > 0) {
            return string(abi.encodePacked(_baseTokenURI, tokenId.toString()));
        }
        
        return "";
    }
    
    function mintWithURI(address to, uint256 tokenId, string memory uri) external onlyOwner {
        _mint(to, tokenId);
        _tokenURIs[tokenId] = uri;
    }
}

Operaciones por lotes para colecciones grandes

Para colecciones NFT grandes, las operaciones por lotes ahorran tiempo y costes de gas:

def batch_upload_collection(collection_data, api_key):
    """Upload an entire NFT collection in batches"""
    
    print(f"Starting batch upload of {len(collection_data)} NFTs...")
    results = []
    
    for i, nft_data in enumerate(collection_data):
        print(f"Processing NFT {i+1}/{len(collection_data)}: {nft_data['name']}")
        
        try:
            # Upload image
            image_cid = upload_image_to_ipfs(nft_data['image_path'], api_key)
            
            if image_cid:
                # Upload metadata
                metadata_cid = create_and_upload_metadata(
                    name=nft_data['name'],
                    description=nft_data['description'],
                    image_cid=image_cid,
                    attributes=nft_data['attributes'],
                    api_key=api_key
                )
                
                if metadata_cid:
                    results.append({
                        'token_id': i + 1,
                        'name': nft_data['name'],
                        'image_cid': image_cid,
                        'metadata_cid': metadata_cid,
                        'token_uri': f"ipfs://{metadata_cid}"
                    })
                    
        except Exception as e:
            print(f"❌ Error processing {nft_data['name']}: {e}")
    
    print(f"✅ Batch upload complete! {len(results)} NFTs processed successfully.")
    return results

# Example collection data
collection_data = [
    {
        'name': 'Cosmic Warrior #001',
        'description': 'A fierce warrior from distant galaxies.',
        'image_path': 'images/warrior_001.png',
        'attributes': [
            {'trait_type': 'Class', 'value': 'Warrior'},
            {'trait_type': 'Rarity', 'value': 'Epic'}
        ]
    },
    # Add more NFTs...
]

results = batch_upload_collection(collection_data, API_KEY)

Buenas prácticas para el almacenamiento de metadatos NFT

1. Utiliza nombres de archivo descriptivos

Al subir a IPFS, usa descripciones significativas para ayudar con la organización:

payload = {
    "content": base64_content,
    "description": f"Collection: {collection_name} | Token: {token_id} | Type: {file_type}"
}

2. Implementa una gestión de errores adecuada

Siempre maneja los fallos de subida con elegancia:

import time

def upload_with_retry(upload_function, max_retries=3, delay=2):
    """Upload with exponential backoff retry logic"""
    
    for attempt in range(max_retries):
        try:
            return upload_function()
        except Exception as e:
            if attempt == max_retries - 1:
                raise e
            print(f"Attempt {attempt + 1} failed: {e}. Retrying in {delay} seconds...")
            time.sleep(delay)
            delay *= 2  # Exponential backoff

3. Valida la estructura de metadatos

Asegúrate de que tus metadatos cumplen con los estándares:

def validate_metadata(metadata):
    """Validate NFT metadata structure"""
    required_fields = ['name', 'description', 'image']
    
    for field in required_fields:
        if field not in metadata:
            raise ValueError(f"Missing required field: {field}")
    
    if not metadata['image'].startswith('ipfs://'):
        raise ValueError("Image must be an IPFS URL")
    
    if 'attributes' in metadata:
        for attr in metadata['attributes']:
            if 'trait_type' not in attr or 'value' not in attr:
                raise ValueError("Invalid attribute structure")
    
    return True

Eligiendo el servicio de pinning IPFS adecuado

Al seleccionar un servicio de pinning IPFS para tu proyecto NFT, considera:

  • Fiabilidad: Tiempo de actividad garantizado para almacenamiento a largo plazo
  • Rendimiento: Velocidades de recuperación rápidas en todo el mundo
  • Precio: Coste eficiente para el tamaño de tu colección
  • Funciones: Capacidades de API, analítica y herramientas para desarrolladores

Para una comparación detallada de servicios de pinning, lee nuestra comparación IPFS Ninja vs Pinata y nuestra guía sobre los mejores servicios de pinning IPFS.

Funciones avanzadas: pasarelas personalizadas y analítica

IPFS Ninja ofrece funciones adicionales para proyectos NFT profesionales:

Configuración de pasarela personalizada

Crea pasarelas IPFS con marca para tu colección:

// Access your NFT through a custom gateway
const customGateway = 'https://my-collection.gw.ipfs.ninja';
const nftUrl = `${customGateway}/ipfs/${metadataCid}`;

Analítica de subidas

Monitoriza el uso de almacenamiento NFT y los patrones de acceso mediante la analítica del panel de control, ayudándote a entender el rendimiento de la colección y optimizar los costes de almacenamiento.

Solución de problemas comunes

Los metadatos no se cargan

  • Verifica que las URL IPFS usen el protocolo ipfs://
  • Comprueba que el JSON de metadatos sea válido
  • Asegúrate de que el servicio de pinning mantiene el contenido

Las imágenes no se muestran

  • Confirma que los CID de las imágenes son correctos en los metadatos
  • Prueba las URL de las imágenes en las pasarelas IPFS
  • Verifica que los formatos de archivo de imagen sean compatibles con la web

Errores de estimación de gas

  • Asegúrate de que la función tokenURI devuelve cadenas válidas
  • Comprueba referencias circulares en los metadatos
  • Valida todos los CID IPFS antes del minting

Monitoreo y mantenimiento de tu almacenamiento NFT

Después de desplegar tu colección:

  1. Comprobaciones de salud regulares: Verifica que los metadatos y las imágenes siguen siendo accesibles
  2. Respalda los CID importantes: Mantén registros de todos los identificadores de contenido subidos
  3. Monitoriza la analítica: Realiza un seguimiento de los patrones de acceso y el uso del almacenamiento
  4. Planifica el escalado: Considera actualizar tu servicio de pinning a medida que crece tu colección

Para más detalles sobre la gestión programática de subidas, consulta nuestro tutorial de la API de subida IPFS.

Conclusión

Almacenar metadatos NFT en IPFS asegura que tus activos digitales permanezcan accesibles y valiosos a largo plazo. Siguiendo esta guía, has aprendido a:

  • Estructurar metadatos compatibles con ERC-721
  • Subir activos y metadatos utilizando Python y JavaScript
  • Implementar funciones tokenURI adecuadas
  • Gestionar operaciones por lotes para colecciones grandes
  • Aplicar buenas prácticas para despliegues en producción

La combinación de la arquitectura descentralizada de IPFS y los servicios de pinning fiables crea la base para proyectos NFT exitosos que resisten el paso del tiempo.

¿Listo para empezar con el pinning? Crea una cuenta gratuita — 50 archivos, 1 GB de almacenamiento, 2 GB de ancho de banda/mes. No se requiere tarjeta de crédito.

Volver al Blog

Artículos Relacionados

Ver Todos los Artículos »