In der dynamischen Technologielandschaft, in der Innovationen die Grenzen des Möglichen immer weiter verschieben, hört die künstliche Intelligenz (KI) nicht auf, unsere Fantasie zu beflügeln.

Unter KI versteht man die Simulation menschlicher Intelligenzprozesse durch Computersysteme. Zu diesen Prozessen gehören Aufgaben wie Lernen, Denken, Problemlösung, Wahrnehmung, Sprachverständnis und Entscheidungsfindung.

Heute haben Einzelpersonen und Unternehmen verschiedene KI-Modelle entwickelt und trainiert, um bestimmte Aufgaben besser als Menschen in Echtzeit zu erledigen. Unter den unzähligen Anwendungen von KI ist ein besonders faszinierender Bereich die KI-gestützte Bilderzeugung.

Was du bauen wirst

In diesem Leitfaden wird erklärt, wie du eine React-Anwendung erstellst, die sich über ein Node.js-Backend nahtlos in die OpenAI DALL-E API integriert und auf der Grundlage von Textaufforderungen fesselnde Bilder erzeugt.

KI-Bildgenerator in Aktion, der mithilfe der DALL-E API lebendige und kreative Bilder erzeugt
KI-Bildgenerator in Aktion, der mithilfe der DALL-E API lebendige und kreative Bilder erzeugt

Voraussetzungen

Um an diesem Projekt mitzuarbeiten, solltest du Folgendes mitbringen:

Was ist die OpenAI DALL-E API?

Die OpenAI API ist eine Cloud-basierte Plattform, die Entwicklern Zugang zu den vortrainierten KI-Modellen von OpenAI wie DALL-E und GPT-3 gewährt (wir haben dieses Modell verwendet, um einen ChatGPT-Klon mit dem Code in diesem Git-Repository zu erstellen). Damit können Entwickler KI-Funktionen wie Zusammenfassungen, Übersetzungen, Bilderzeugung und Modifikationen in ihre Programme einbauen, ohne ihre Modelle entwickeln und trainieren zu müssen.

Um die OpenAI API zu nutzen, musst du ein Konto mit deinem Google-Konto oder deiner E-Mail-Adresse auf der OpenAI-Website erstellen und einen API-Schlüssel anfordern. Um einen API-Schlüssel zu erhalten, klickst du oben rechts auf der Website auf Personal und wählst dann API-Schlüssel anzeigen.

Der Prozess der Erstellung eines geheimen OpenAI-API-Schlüssels
Der Prozess der Erstellung eines geheimen OpenAI-API-Schlüssels

Klicke auf die Schaltfläche Neuen geheimen Schlüssel erstellen und speichere den Schlüssel irgendwo. Du wirst ihn in dieser Anwendung verwenden, um mit der DALL-E API von OpenAI zu interagieren.

Einrichten der Entwicklungsumgebung

Du kannst eine React-Anwendung von Grund auf neu erstellen und deine eigene Schnittstelle entwickeln oder du kannst dir unsere Git-Starter-Vorlage schnappen, indem du diese Schritte befolgst:

  1. Besuche das GitHub-Repository für dieses Projekt.
  2. Wähle Diese Vorlage verwenden > Neues Repository erstellen, um den Startcode in ein Repository in deinem GitHub-Konto zu kopieren (aktiviere das Kontrollkästchen, um alle Zweige einzuschließen).
  3. Ziehe das Repository auf deinen lokalen Computer und wechsle mit dem Befehl in den starter-files-Zweig: git checkout starter-files.
  1. Installiere die notwendigen Abhängigkeiten, indem du den Befehl npm install ausführst.

Sobald die Installation abgeschlossen ist, kannst du das Projekt auf deinem lokalen Computer mit npm run start starten. Dadurch wird das Projekt unter http://localhost:3000/ verfügbar.

Benutzeroberfläche einer KI-Bildgenerator-Anwendung, die die Leistungsfähigkeit der künstlichen Intelligenz bei der Bilderstellung zeigt
Benutzeroberfläche einer KI-Bildgenerator-Anwendung, die die Leistungsfähigkeit der künstlichen Intelligenz bei der Bilderstellung zeigt

Das Verständnis der Projektdateien

In diesem Projekt haben wir alle notwendigen Abhängigkeiten für deine React-Anwendung hinzugefügt. Hier ist eine Übersicht über die installierten Dateien:

  • file-server: Diese Hilfsbibliothek vereinfacht das Herunterladen der erzeugten Bilder. Sie ist mit dem Download-Button verknüpft und sorgt für ein reibungsloses Nutzererlebnis.
  • uuid: Diese Bibliothek weist jedem Bild eine eindeutige Kennung zu. Dadurch wird verhindert, dass Bilder denselben Standard-Dateinamen haben, und die Ordnung und Übersichtlichkeit bleibt erhalten.
  • react-icons: Diese Bibliothek ist in das Projekt integriert und bindet mühelos Icons ein, um die visuelle Attraktivität deiner Anwendung zu verbessern.

Das Herzstück deiner React-Anwendung ist der Ordner src. Hier befindet sich der wesentliche JavaScript-Code für Webpack. Wir wollen die Dateien und Ordner im src-Ordner kennenlernen:

  • assets: In diesem Verzeichnis findest du die Bilder und das Loader-GIF, die im gesamten Projekt verwendet werden.
  • data: Dieser Ordner enthält eine index.js-Datei, die eine Reihe von 30 Prompts exportiert. Diese Prompts können verwendet werden, um verschiedene und zufällige Bilder zu erzeugen. Du kannst sie gerne bearbeiten.
  • index.css: Hier werden die Stile, die in diesem Projekt verwendet werden, gespeichert.

Den Utils-Ordner verstehen

In diesem Ordner definiert die Datei index.js zwei wiederverwendbare Funktionen. Die erste Funktion wählt nach dem Zufallsprinzip die Prompts aus, die verschiedene Bilder beschreiben, die erzeugt werden können.

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

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

    return randomPrompt;
}

Die zweite Funktion kümmert sich um den Download der erzeugten Bilder, indem sie die Abhängigkeit von file-saver nutzt. Beide Funktionen sind modular und effizient aufgebaut und können bei Bedarf bequem in Komponenten importiert werden.

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`);
}

Im obigen Code gibt die uuid-Abhängigkeit jeder erzeugten Bilddatei eine eindeutige ID, damit sie nicht denselben Dateinamen haben.

Zum Verständnis der Komponenten

Das sind kleine Codeblöcke, die voneinander getrennt sind, damit dein Code einfach zu pflegen und zu verstehen ist. Für dieses Projekt wurden drei Komponenten erstellt: Header.jsx, Footer.jsx und Form.jsx. Die Hauptkomponente ist die Komponente Form, in der die Eingaben empfangen und an die Datei App.jsx weitergegeben werden. Die Funktion generateImage wurde als onClick -Ereignis zur Schaltfläche Generate Image hinzugefügt.

In der Formularkomponente wird ein Status erstellt, um die Eingabeaufforderung zu speichern und zu aktualisieren. Außerdem gibt es eine Funktion, mit der du auf ein zufälliges Symbol klicken kannst, um die zufälligen Prompts zu generieren. Dies ist mit der Funktion handleRandomPrompt möglich, die die Funktion getRandomPrompt verwendet, die du bereits eingerichtet hast. Wenn du auf das Symbol klickst, wird ein zufälliger Prompt abgerufen und der Status damit aktualisiert:

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

Die Datei App.jsx verstehen

Hier befindet sich der größte Teil des Codes. Alle Komponenten werden hier zusammengeführt. Es gibt auch einen Bereich, in dem das erzeugte Bild angezeigt wird. Wenn noch kein Bild erstellt wurde, wird ein Platzhalterbild (Vorschaubild) angezeigt.

In dieser Datei werden zwei Zustände verwaltet:

  • isGenerating: Hier wird festgehalten, ob gerade ein Bild erstellt wird. In der Standardeinstellung ist er auf false gesetzt.
  • generatedImage: Dieser Status speichert Informationen über das Bild, das erstellt wurde.

Außerdem wird die Funktion downloadImage importiert, mit der du den Download des erzeugten Bildes auslösen kannst, wenn du auf die Schaltfläche Download klickst:

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

Jetzt, wo du die Startdateien verstanden und dein Projekt eingerichtet hast, kannst du mit der Logik der Anwendung beginnen. Beginnen wir nun mit der Logik dieser Anwendung.

Bilder mit der DALL-E API von OpenAI generieren

Um die Möglichkeiten von OpenAIs DALL-E API zu nutzen, musst du mit Node.js einen Server einrichten. Innerhalb dieses Servers erstellst du eine POST-Route. Diese Route ist dafür zuständig, den von deiner React-Anwendung gesendeten Prompt-Text zu empfangen und daraus ein Bild zu erzeugen.

Um loszulegen, installierst du die notwendigen Abhängigkeiten in deinem Projektverzeichnis, indem du den folgenden Befehl ausführst:

npm i express cors openai

Installiere außerdem die folgenden Abhängigkeiten als Dev-Abhängigkeiten. Diese Tools helfen dir beim Einrichten deines Node.js-Servers:

npm i -D dotenv nodemon

Die installierten Abhängigkeiten werden im Folgenden erklärt:

  • express: Diese Bibliothek hilft beim Erstellen eines Servers in Node.js.
  • cors: CORS ermöglicht eine sichere Kommunikation zwischen verschiedenen Domains.
  • openai: Diese Abhängigkeit ermöglicht dir den Zugriff auf die DALL-E API von OpenAI.
  • dotenv: dotenv hilft dir bei der Verwaltung von Umgebungsvariablen.
  • nodemon: nodemon ist ein Entwicklungswerkzeug, das Änderungen an deinen Dateien überwacht und den Server automatisch neu startet.

Wenn die Installation erfolgreich war, erstelle eine server.js-Datei im Stammverzeichnis deines Projekts. Hier wird dein gesamter Servercode gespeichert.

Importiere in der Datei server.js die Bibliotheken, die du gerade installiert hast, und instanziere sie:

// 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();

Im obigen Code importierst du die notwendigen Bibliotheken. Dann richtest du mit const app = express(); eine Instanz der Express-Anwendung ein. Danach aktivierst du CORS. Als Nächstes wird Express so konfiguriert, dass es eingehende JSON-Daten verarbeitet, wobei eine Datengrößenbeschränkung von 50mb festgelegt wird.

Anschließend wird eine Instanz der OpenAI-Klasse unter Verwendung deines OpenAI-API-Schlüssels erstellt. Erstelle eine .env-Datei im Stammverzeichnis deines Projekts und füge deinen API-Schlüssel über die Variable OPENAI_API_KEY hinzu. Schließlich definierst du eine asynchrone startServer Funktion und rufst sie auf, um den Server in Gang zu setzen.

Jetzt hast du deine server.js-Datei konfiguriert. Erstellen wir nun eine POST-Route, die du in deiner React-Anwendung verwenden kannst, um mit diesem Server zu interagieren:

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);
    }
});

In diesem Code ist die Route auf /api eingestellt und soll eingehende POST-Anfragen bearbeiten. In der Callback-Funktion der Route erhältst du die von deiner React-Anwendung gesendeten Daten über req.body – genauer gesagt über den Wert prompt.

Anschließend wird die Methode images.generate der OpenAI-Bibliothek aufgerufen. Diese Methode nimmt die angegebene Eingabeaufforderung entgegen und erzeugt als Antwort ein Bild. Parameter wie n bestimmen die Anzahl der zu erzeugenden Bilder (hier nur eines), size gibt die Abmessungen des Bildes an und response_format gibt das Format an, in dem die Antwort geliefert werden soll (in diesem Fallb64_json ).

Nachdem du das Bild erzeugt hast, extrahierst du die Bilddaten aus der Antwort und speicherst sie in der Variablen image. Dann schickst du eine JSON-Antwort mit den erzeugten Bilddaten zurück an die React-Anwendung und setzt den HTTP-Status mit res.status(200).json({ photo: image }) auf 200 (was einen Erfolg anzeigt).

Wenn während dieses Prozesses ein Fehler auftritt, wird der Code im catch Block ausgeführt und der Fehler zum Debuggen auf der Konsole protokolliert.

Jetzt ist der Server bereit! Legen wir den Befehl, mit dem unser Server ausgeführt werden soll, in der Datei package.json scripts Objekt fest:

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

Wenn du npm run dev:backend aufrufst, wird dein Server auf http://localhost:8080/ gestartet, und wenn du npm run dev:frontend aufrufst, wird deine React-Anwendung auf http://localhost:3000/ gestartet. Stelle sicher, dass beide in verschiedenen Terminals laufen.

HTTP-Anfragen von React an den Node.js Server stellen

In der Datei App.jsx erstellst du eine Funktion generateImage, die ausgelöst wird, wenn du in der Komponente Form.jsx auf die Schaltfläche Bild generieren klickst. Diese Funktion nimmt zwei Parameter entgegen: prompt und setPrompt aus der Komponente Form.jsx.

In der Funktion generateImage stellst du eine HTTP-POST-Anfrage an den Node.js-Server:

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');
    }
};

Im obigen Code überprüfst du, ob der Parameter prompt einen Wert hat, und setzt dann den Status von isGenerating auf true, da der Vorgang gestartet wird. Dadurch wird der Lader auf dem Bildschirm angezeigt, denn in der Datei App.jsx haben wir diesen Code, der die Anzeige des Laders steuert:

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

Als Nächstes verwenden wir die Methode fetch(), um mit http://localhost:8080/api eine POST-Anfrage an den Server zu stellen – aus diesem Grund haben wir CORS installiert, da wir mit einer API auf einer anderen URL interagieren. Wir verwenden die Eingabeaufforderung als Text der Nachricht. Dann extrahierst du die vom Node.js-Server zurückgegebene Antwort und setzt sie in den Zustand generatedImage.

Sobald der Status generatedImage einen Wert hat, wird das Bild angezeigt:

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

So sieht deine vollständige App.jsx-Datei aus:

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;

Bereitstellen deiner Full-Stack-Anwendung auf Kinsta

Bis jetzt hast du erfolgreich eine React-Anwendung erstellt, die mit Node.js interagiert und somit eine Full-Stack-Anwendung ist. Jetzt können wir diese Anwendung auf Kinsta bereitstellen.

Konfiguriere den Server zunächst so, dass er die statischen Dateien bereitstellt, die während des Build-Prozesses der React-Anwendung erzeugt wurden. Dazu importierst du das Modul path und verwendest es zum Bereitstellen der statischen Dateien:

const path = require('path');

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

Wenn du den Befehl npm run build && npm run dev:backend ausführst, wird deine Full-Stack-React-Anwendung unter http://localhost:8080/ geladen. Das liegt daran, dass die React-Anwendung im Build-Ordner in statische Dateien kompiliert wird. Diese Dateien werden dann als statisches Verzeichnis in deinen Node.js-Server eingebunden. Wenn du deinen Node.js-Server startest, ist die Anwendung also zugänglich.

Bevor du deinen Code bei dem von dir gewählten Git-Anbieter (Bitbucket, GitHub oder GitLab) bereitstellst, musst du die HTTP-Request-URL in deiner App.jsx-Datei ändern. Ändere http://localhost:8080/api in /api, da die URL vorangestellt werden soll.

Schließlich fügst du in deiner package.json-Datei einen Skriptbefehl für den Node.js-Server hinzu, der für die Bereitstellung verwendet werden soll:

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

Als Nächstes pusht du deinen Code zu deinem bevorzugten Git-Provider und stellst dein Repository auf Kinsta bereit, indem du diese Schritte befolgst:

  1. Melde dich in deinem Kinsta-Konto auf dem MyKinsta-Dashboard an.
  2. Wähle in der linken Seitenleiste Anwendung aus und klicke auf die Schaltfläche Anwendung hinzufügen.
  3. Wähle in dem daraufhin angezeigten Modal das Repository aus, das du bereitstellen möchtest. Wenn du mehrere Zweige hast, kannst du den gewünschten Zweig auswählen und einen Namen für deine Anwendung vergeben.
  4. Wähle einen der verfügbaren Rechenzentrumsstandorte.
  5. Füge die OPENAI_API_KEY als Umgebungsvariable hinzu. Kinsta wird automatisch ein Dockerfile für dich einrichten.
  6. Zum Schluss fügst du im Feld für den Startbefehl npm run build && npm run start hinzu. Kinsta installiert die Abhängigkeiten deiner Anwendung aus der package.json und baut sie dann auf und stellt sie bereit.

Zusammenfassung

In dieser Anleitung hast du gelernt, wie du die DALL-E API von OpenAI für die Bilderzeugung nutzen kannst. Außerdem hast du gelernt, wie du mit React und Node.js arbeitest, um eine einfache Full-Stack-Anwendung zu erstellen.

Die Möglichkeiten von KI sind endlos, da täglich neue Modelle eingeführt werden und du erstaunliche Projekte erstellen kannst, die auf dem Anwendungs-Hosting von Kinsta bereitgestellt werden können.

Welches Modell würdest du gerne erforschen und über welches Projekt würden wir gerne als nächstes schreiben? Teile es uns in den Kommentaren unten mit.

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.