En el dinámico panorama de la tecnología, donde la innovación da forma continuamente a los límites de lo posible, la inteligencia artificial (IA) nunca deja de cautivar nuestra imaginación.

La IA se refiere a la simulación de procesos de inteligencia humana por parte de sistemas informáticos. Estos procesos incluyen tareas como el aprendizaje, el razonamiento, la resolución de problemas, la percepción, la comprensión del lenguaje y la toma de decisiones.

En la actualidad, personas y empresas han desarrollado y entrenado varios modelos de IA para realizar determinadas tareas mejor que los humanos en tiempo real. Entre las innumerables aplicaciones de la IA, un área especialmente interesante es la generación de imágenes con IA.

Lo que Estás Construyendo

Esta guía explica cómo construir una aplicación React que se integre perfectamente con la API OpenAI DALL-E a través de un backend Node.js y genere imágenes fascinantes basadas en indicaciones textuales.

Generador de imágenes AI en acción, produciendo imágenes vívidas y creativas usando DALL-E API
Generador de imágenes de IA en acción, produciendo imágenes vívidas y creativas utilizando la API DALL-E.

Requisitos Previos

Para seguir este proyecto, debes tener:

¿Qué es la API OpenAI DALL-E?

La API OpenAI es una plataforma basada en la nube que permite a los desarrolladores acceder a los modelos de IA preentrenados de OpenAI, como DALL-E y GPT-3 (utilizamos este modelo para construir un clon de ChatGPT con el código de este repositorio Git). Permite a los desarrolladores añadir funciones de IA como el resumen, la traducción, la generación de imágenes y la modificación a sus programas sin necesidad de desarrollar y entrenar sus modelos.

Para utilizar la API de OpenAI, crea una cuenta utilizando tu cuenta de Google o tu correo electrónico en el sitio web de OpenAI y obtén una clave API. Para generar una clave API, haz clic en Personal en la esquina superior derecha del sitio web y, a continuación, selecciona Ver claves API.

El proceso de creación de una clave secreta de la API de OpenAI.
El proceso de creación de una clave secreta de la API de OpenAI.

Haz clic en el botón Crear nueva clave secreta, y guarda la clave en algún sitio. La utilizarás en esta aplicación para interactuar con la API DALL-E de OpenAI.

Configurar el Entorno de Desarrollo

Puedes crear una aplicación React desde cero y desarrollar tu propia interfaz, o puedes coger nuestra plantilla de inicio Git siguiendo estos pasos:

  1. Visita el repositorio GitHub de este proyecto.
  2. Selecciona Usar esta plantilla > Crear un nuevo repositorio para copiar el código de inicio en un repositorio dentro de tu cuenta de GitHub (marca la casilla para incluir todas las ramas).
  3. Extrae el repositorio a tu ordenador local y cambia a la rama starter-files utilizando el comando : git checkout starter-files.
  1. Instala las dependencias necesarias ejecutando el comando npm install.

Una vez completada la instalación, puedes lanzar el proyecto en tu ordenador local con npm run start. Esto hace que el proyecto esté disponible en http://localhost:3000/.

Interfaz de usuario de una aplicación generadora de imágenes AI que muestra el poder de la inteligencia artificial en la creación de imágenes.
Interfaz de usuario de una aplicación generadora de imágenes de IA que muestra el poder de la inteligencia artificial en la creación de imágenes.

Comprender los Archivos del Proyecto

En este proyecto, hemos añadido todas las dependencias necesarias para tu aplicación React. Aquí tienes un resumen de lo que se ha instalado:

  • file-server: Esta biblioteca de utilidades simplifica el proceso de descarga de las imágenes generadas. Está vinculada al botón de descarga, lo que garantiza una experiencia de usuario fluida.
  • uuid: Esta biblioteca asigna una identificación única a cada imagen. Esto evita cualquier posibilidad de que las imágenes compartan el mismo nombre de archivo por defecto, manteniendo el orden y la claridad.
  • react-icons: Integrada en el proyecto, esta biblioteca incorpora iconos sin esfuerzo, mejorando el atractivo visual de tu aplicación.

En el core de tu aplicación React se encuentra la carpeta src. Aquí es donde se aloja el código JavaScript esencial para Webpack. Vamos a entender los archivos y carpetas de la carpeta src:

  • assets: En este directorio encontrarás las imágenes y el cargador gif que se utilizan en todo el proyecto.
  • data: Esta carpeta contiene un archivo index.js que exporta un array de 30 prompts. Estos prompts pueden utilizarse para generar imágenes diversas y aleatorias. Siéntete libre de editarlo.
  • index.css: Aquí es donde se almacenan los estilos utilizados en este proyecto.

Comprender la Carpeta Utils

Dentro de esta carpeta, el archivo index.js define dos funciones reutilizables. La primera función aleatoriza la selección de prompts que describen varias imágenes que se pueden generar.

import { randomPrompts } from '../data';

export const getRandomPrompt = () => {
    const randomIndex = Math.floor(Math.random() * randomPrompts.length);
    const randomPrompt = randomPrompts[randomIndex];

    return randomPrompt;
}

La segunda función gestiona la descarga de las imágenes generadas aprovechando la dependencia file-saver. Ambas funciones se han creado para ofrecer modularidad y eficacia, y pueden importarse cómodamente en componentes cuando sea necesario.

import FileSaver from 'file-saver';
import { v4 as uuidv4 } from 'uuid';

export async function downloadImage(photo) {
    const _id = uuidv4();
    FileSaver.saveAs(photo, `download-${_id}.jpg`);
}

En el código anterior, la dependencia uuid da a cada archivo de imagen generado un ID único, para que no tengan el mismo nombre de archivo.

Comprender los Componentes

Son pequeños bloques de código separados para que tu código sea fácil de mantener y comprender. Para este proyecto, se crearon tres componentes: Header.jsx, Footer.jsx y Form.jsx. El componente principal es el componente Form, donde se recibe la entrada y se pasa al archivo App.jsx con la función generateImage añadida como evento onClick al botón Generar imagen.

En el componente Form, se crea un estado para almacenar y actualizar la solicitud. Además, una función te permite hacer clic en un icono aleatorio para generar los prompts aleatorios. Esto es posible gracias a la función handleRandomPrompt, que utiliza la función getRandomPrompt que ya has configurado. Cuando haces clic en el icono, se obtiene un prompt aleatorio y se actualiza el estado con él:

const handleRandomPrompt = () => {
    const randomPrompt = getRandomPrompt();
    setPrompt(randomPrompt)
}

Comprender el Archivo App.jsx

Aquí es donde reside la mayor parte del código. Aquí se reúnen todos los componentes. También hay un área designada para mostrar la imagen generada. Si aún no se ha generado ninguna imagen, se muestra una imagen de marcador de posición (Imagen de vista previa).

Dentro de este archivo, se gestionan dos estados:

  • isGenerating: Mantiene un registro de si se está generando actualmente una imagen. Por defecto, se establece en falso.
  • generatedImage: Este estado almacena información sobre la imagen que se ha generado.

Además, se importa la función de utilidad downloadImage, que te permite activar la descarga de la imagen generada cuando pulsas el botón Descargar:

<button
    className="btn"
    onClick={() => downloadImage(generatedImage.photo)}
>

Ahora que ya conoces los archivos de inicio y has configurado tu proyecto. Empecemos a manejar la lógica de esta aplicación.

Generar Imágenes con la API DALL-E de OpenAI

Para aprovechar las capacidades de la API DALL-E de OpenAI, utilizarás Node.js para establecer un servidor. Dentro de este servidor, crearás una ruta POST. Esta ruta se encargará de recibir el texto del prompt enviado desde tu aplicación React y de utilizarlo para generar una imagen.

Para empezar, instala las dependencias necesarias en el directorio de tu proyecto ejecutando el siguiente comando:

npm i express cors openai

Además, instala las siguientes dependencias como dependencias dev. Estas herramientas te ayudarán a configurar tu servidor Node.js:

npm i -D dotenv nodemon

Las dependencias instaladas se explican a continuación:

  • express: Esta biblioteca ayuda a crear un servidor en Node.js.
  • cors: CORS facilita la comunicación segura entre diferentes dominios.
  • openai: Esta dependencia te permite acceder a la API DALL-E de OpenAI.
  • dotenv: dotenv ayuda a gestionar las variables de entorno.
  • nodemon: nodemon es una herramienta de desarrollo que supervisa los cambios en tus archivos y reinicia automáticamente el servidor.

Una vez realizada la instalación, crea un archivo server.js en el root de tu proyecto. Aquí es donde se almacenará todo el código de tu servidor.

En el archivo server.js, importa las bibliotecas que acabas de instalar y crea una instancia de ellas:

// Import the necessary libraries
const express = require('express');
const cors = require('cors');
require('dotenv').config();
const OpenAI = require('openai');

// Create an instance of the Express application
const app = express();

// Enable Cross-Origin Resource Sharing (CORS)
app.use(cors());

// Configure Express to parse JSON data and set a data limit
app.use(express.json({ limit: '50mb' }));

// Create an instance of the OpenAI class and provide your API key
const openai = new OpenAI({
    apiKey: process.env.OPENAI_API_KEY,
});

// Define a function to start the server
const startServer = async () => {
    app.listen(8080, () => console.log('Server started on port 8080'));
};

// Call the startServer function to begin listening on the specified port
startServer();

En el código anterior, importa las bibliotecas necesarias. A continuación, establece una instancia de la aplicación Express utilizando const app = express();. Después, habilita CORS. A continuación, se configura Express para procesar los datos JSON entrantes, especificando un límite de tamaño de datos de 50mb.

A continuación, se crea una instancia de la clase OpenAI utilizando tu clave API OpenAI. Crea un archivo .env en el root de tu proyecto y añade tu clave API utilizando la variable OPENAI_API_KEY. Por último, define una función asíncrona startServer y llámala para poner en marcha el servidor.

Ahora ya tienes configurado tu archivo server.js. Vamos a crear una ruta POST que puedas utilizar en tu aplicación React para interactuar con este servidor:

app.post('/api', async (req, res) => {
    try {
        const { prompt } = req.body;
        const response = await openai.images.generate({
            prompt,
            n: 1,
            size: '1024x1024',
            response_format: 'b64_json',
        });
        const image = response.data[0].b64_json;
        res.status(200).json({ photo: image });
    } catch (error) {
        console.error(error);
    }
});

En este código, la ruta se establece en /api, y está diseñada para gestionar las peticiones POST entrantes. Dentro de la función de devolución de llamada de la ruta, recibes los datos enviados desde tu aplicación React utilizando req.body – concretamente el valor prompt.

A continuación, se invoca el método images.generate de la biblioteca OpenAI. Este método toma el prompt proporcionado y genera una imagen como respuesta. Parámetros como n determinan el número de imágenes a generar (aquí, sólo una), size especifica las dimensiones de la imagen, y response_format indica el formato en el que debe proporcionarse la respuesta (b64_json en este caso).

Tras generar la imagen, extraes los datos de la imagen de la respuesta y los almacenas en la variable image. A continuación, envías una respuesta JSON a la aplicación React con los datos de la imagen generada, estableciendo el estado HTTP en 200 (indicando éxito) utilizando res.status(200).json({ photo: image }).

Si se produce algún error durante este proceso, se ejecuta el código dentro del bloque catch, registrando el error en la consola para su depuración.

¡Ahora el servidor está listo! Vamos a especificar el comando que se utilizará para ejecutar nuestro servidor en el archivo package.json objeto scripts:

"scripts": {
  "dev:frontend": "react-scripts start",
  "dev:backend": "nodemon server.js",
  "build": "react-scripts build",
},

Ahora, cuando ejecutes npm run dev:backend, tu servidor se iniciará en http://localhost:8080/, mientras que si ejecutas npm run dev:frontend, tu aplicación React se iniciará en http://localhost:3000/. Asegúrate de que ambas se ejecutan en terminales diferentes.

Hacer Peticiones HTTP desde React al Servidor Node.js

En el archivo App.jsx, crearás una función generateImage que se activa cuando se pulsa el botón Generar Imagen en el componente Form.jsx. Esta función acepta dos parámetros: prompt y setPrompt del componente Form.jsx.

En la función generateImage, haz una petición HTTP POST al servidor Node.js:

const generateImage = async (prompt, setPrompt) => {
    if (prompt) {
        try {
            setIsGenerating(true);
            const response = await fetch(
                'http://localhost:8080/api',
                {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        prompt,
                    }),
                }
            );
            const data = await response.json();
            setGeneratedImage({
                photo: `data:image/jpeg;base64,${data.photo}`,
                altText: prompt,
            });
        } catch (err) {
            alert(err);
        } finally {
            setPrompt('');
            setIsGenerating(false);
        }
    } else {
        alert('Please provide proper prompt');
    }
};

En el código anterior, comprueba si el parámetro prompt tiene un valor y, a continuación, establece el estado isGenerating en true, ya que la operación se está iniciando. Esto hará que el cargador aparezca en la pantalla porque en el archivo App.jsx, tenemos este código controlando la visualización del cargador:

{isGenerating && (
    <div> className="loader-comp">
        <img src={Loader} alt="" className='loader-img' />
    </div>
)}

A continuación, utiliza el método fetch() para hacer una petición POST al servidor utilizando http://localhost:8080/api — esta es la razón por la que instalamos CORS ya que estamos interactuando con una API en otra URL. Utilizamos la solicitud como cuerpo del mensaje. A continuación, extrae la respuesta devuelta por el servidor Node.js y ponla en el estado generatedImage.

Una vez que el estado generatedImage tenga un valor, se mostrará la imagen:

{generatedImage.photo ? (
    <img
        src={generatedImage.photo}
        alt={generatedImage.altText}
        className="imgg ai-img"
    />
) : (
    <img
        src={preview}
        alt="preview"
        className="imgg preview-img"
    />
)}

Este es el aspecto que tendrá tu archivo App.jsx completo:

import { Form, Footer, Header } from './components';
import preview from './assets/preview.png';
import Loader from './assets/loader-3.gif'
import { downloadImage } from './utils';
import { useState } from 'react';

const App = () => {
    const [isGenerating, setIsGenerating] = useState(false);
    const [generatedImage, setGeneratedImage] = useState({
        photo: null,
        altText: null,
    });

    const generateImage = async (prompt, setPrompt) => {
        if (prompt) {
            try {
                setIsGenerating(true);
                const response = await fetch(
                    'http://localhost:8080/api',
                    {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify({
                            prompt,
                        }),
                    }
                );
                const data = await response.json();
                setGeneratedImage({
                    photo: `data:image/jpeg;base64,${data.photo}`,
                    altText: prompt,
                });
            } catch (err) {
                alert(err);
            } finally {
                setPrompt('');
                setIsGenerating(false);
            }
        } else {
            alert('Please provide proper prompt');
        }
    };

    return (
        <div className='container'>
            <Header />
            <main className="flex-container">
                <Form generateImage={generateImage} prompt={prompt} />
                <div className="image-container">
                    {generatedImage.photo ? (
                        <img
                            src={generatedImage.photo}
                            alt={generatedImage.altText}
                            className="imgg ai-img"
                        />
                    ) : (
                        <img
                            src={preview}
                            alt="preview"
                            className="imgg preview-img"
                        />
                    )}
                    {isGenerating && (
                        <div className="loader-comp">
                            <img src={Loader} alt="" className='loader-img' />
                        </div>
                    )}
                    <button
                        className="btn"
                        onClick={() => downloadImage(generatedImage.photo)}
                    >
                        Download
                    </button>
                </div>
            </main>
            <Footer />
        </div>
    );
};

export default App;

Despliega tu Aplicación Full-Stack en Kinsta

Hasta ahora, has construido con éxito una aplicación React que interactúa con Node.js, lo que la convierte en una aplicación full-stack. Ahora vamos a desplegar esta aplicación en Kinsta.

En primer lugar, configura el servidor para que sirva los archivos estáticos generados durante el proceso de construcción de la aplicación React. Esto se consigue importando el módulo path y utilizándolo para servir los archivos estáticos:

const path = require('path');

app.use(express.static(path.resolve(__dirname, './build')));

Cuando ejecutes el comando npm run build && npm run dev:backend, tu aplicación React full stack se cargará en http://localhost:8080/. Esto se debe a que la aplicación React se compila en archivos estáticos dentro de la carpeta build. A continuación, estos archivos se incorporan a tu servidor Node.js como directorio estático. En consecuencia, cuando ejecutes tu servidor Node, la aplicación estará accesible.

Antes de desplegar tu código en el proveedor Git que hayas elegido (Bitbucket, GitHub o GitLab), recuerda modificar la URL de solicitud HTTP en tu archivo App.jsx. Cambia http://localhost:8080/api por /api, ya que se añadirá un prefijo a la URL.

Por último, en tu archivo package.json, añade un comando script para el servidor Node.js que se utilizaría para el despliegue:

"scripts": {
  // …
  "start": "node server.js",
},

A continuación, envía tu código a tu proveedor Git preferido y despliega tu repositorio en Kinsta siguiendo estos pasos:

  1. Accede a tu cuenta Kinsta en el panel MyKinsta.
  2. Selecciona Aplicación en la barra lateral izquierda y haz clic en el botón Añadir Aplicación.
  3. En el modal que aparece, elige el repositorio que quieres desplegar. Si tienes varias ramas, puedes seleccionar la rama deseada y dar un nombre a tu aplicación.
  4. Selecciona una de las ubicaciones de centros de datos disponibles.
  5. Añade el OPENAI_API_KEY como variable de entorno. Kinsta creará un Dockerfile automáticamente por ti.
  6. Por último, en el campo de comando de inicio, añade npm run build && npm run start. Kinsta instalará las dependencias de tu aplicación desde package.json, y luego construirá y desplegará tu aplicación.

Resumen

En esta guía, has aprendido a aprovechar la potencia de la API DALL-E de OpenAI para la generación de imágenes. También has aprendido a trabajar con React y Node.js para construir una aplicación básica full-stack.

Las posibilidades son infinitas con la IA, ya que cada día se introducen nuevos modelos, y puedes crear proyectos increíbles que se pueden desplegar en el Alojamiento de Aplicaciones de Kinsta.

¿Qué modelo te gustaría explorar y sobre qué proyecto te gustaría que escribiéramos próximamente? Compártelo en los comentarios.

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 200 technical articles majorly around JavaScript and it's frameworks.