· Nacho Coll · Guides  · 9 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 almacenamiento descentralizado y confiable para tus metadatos y activos. Esta guía completa te lleva paso a paso a través del almacenamiento de metadatos NFT en IPFS usando las mejores prácticas de la industria, con ejemplos de código para desarrolladores Python y JavaScript.

IPFS Ninja

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

El hosting web tradicional crea riesgos de centralización para los proyectos NFT. Cuando los metadatos viven en servidores convencionales, los NFTs pueden quedar “rotos” si el servicio de hosting cae o cambia las URLs. 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 a través de múltiples nodos en todo el mundo
  • Verificación criptográfica: La integridad del archivo está garantizada mediante hashing de contenido
  • URLs 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 wallets y marketplaces
  • description: Información detallada sobre el NFT
  • image: URL IPFS del 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 paso a paso de almacenamiento de NFT en IPFS

Paso 1: Preparar tus activos y metadatos

Antes de subir nada, organiza tus archivos:

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

Paso 2: Subir activos a IPFS

Empieza subiendo tus activos NFT principales (imágenes, videos, etc.) para obtener sus CIDs de IPFS. Referenciarás estos CIDs en tus archivos JSON de metadatos.

Así se sube una imagen usando 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: Crear y subir el JSON de metadatos

Una vez que tengas los CIDs 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í está 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);
    }
}

Implementando 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)

Mejores prácticas para el almacenamiento de metadatos NFT

1. Usa 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 manejo de errores apropiado

Siempre maneja los fallos de subida de forma elegante:

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 los metadatos

Asegúrate de que tus metadatos sigan 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:

  • Confiabilidad: 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 de los mejores servicios de pinning IPFS.

Funciones avanzadas: Gateways personalizados y analítica

IPFS Ninja ofrece funciones adicionales para proyectos NFT profesionales:

Configuración de gateway personalizado

Crea gateways IPFS con tu 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

Monitorea el uso de almacenamiento de tus NFTs y los patrones de acceso a través del dashboard de analítica, ayudándote a entender el rendimiento de la colección y optimizar los costes de almacenamiento.

Solución de problemas comunes

Los metadatos no cargan

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

Las imágenes no se muestran

  • Confirma que los CIDs de imagen son correctos en los metadatos
  • Prueba las URLs de imagen en gateways IPFS
  • Verifica que los formatos de archivo de imagen sean compatibles con web

Errores de estimación de gas

  • Asegúrate de que la función tokenURI devuelva strings válidos
  • Comprueba referencias circulares en los metadatos
  • Valida todos los CIDs IPFS antes de mintear

Monitoreando y manteniendo tu almacenamiento NFT

Después de desplegar tu colección:

  1. Revisiones regulares de salud: Verifica que los metadatos e imágenes siguen siendo accesibles
  2. Respalda los CIDs importantes: Mantén registros de todos los identificadores de contenido subidos
  3. Monitorea la analítica: Rastrea patrones de acceso y uso de almacenamiento
  4. Planifica para escalar: Considera actualizar tu servicio de pinning a medida que tu colección crece

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

Conclusión

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

  • Estructurar metadatos compatibles con ERC-721
  • Subir activos y metadatos usando Python y JavaScript
  • Implementar funciones tokenURI apropiadas
  • Manejar operaciones por lotes para colecciones grandes
  • Aplicar las mejores prácticas para despliegues de producción

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

¿Listo para empezar a pinear? 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 »