Wer Formulare im Browser verarbeitet, landet schnell bei der Frage: JSON oder FormData? Die kurze Antwort lautet: Für klassische Formulare und Datei-Uploads ist FormData meist der richtige Weg, weil Browser Felder und Dateien direkt im passenden multipart/form-data-Format senden können. Entscheidend ist, die API nicht wie ein normales Objekt zu behandeln und bei fetch() keine kaputten Header zu erzwingen.
Wann FormData sinnvoller ist als JSON
FormData eignet sich immer dann, wenn Formulare Dateien enthalten oder wenn vorhandenes HTML-Form-Markup direkt genutzt werden soll. JSON ist stark für strukturierte API-Payloads, aber für Binärdaten und gemischte Eingaben verursacht es unnötige Umwege.
Ein typischer Fall ist ein Profilformular mit Name, E-Mail und Avatar. Mit JSON müsste die Datei separat behandelt, oft vorher als Blob oder Base64 transformiert und serverseitig wieder zusammengesetzt werden. Mit FormData hängt der Browser Textfelder und Dateien in einer Anfrage zusammen, inklusive Feldnamen und Dateimetadaten.
Auch bei schrittweiser Aufwertung bestehender Seiten ist das praktisch. Wenn bereits ein <form>-Element existiert, kann die API direkt daraus erzeugt werden. Das reduziert eigene Mapping-Logik und passt gut zu Frontend-Code, der bewusst nah an der Plattform bleibt, statt sofort eine Formularbibliothek einzuführen.
Wichtig ist aber die Grenze: Wenn ein Endpoint rein auf JSON ausgelegt ist, sollte nicht ohne Grund auf FormData gewechselt werden. Für verschachtelte Daten, Arrays mit Objekten oder klar versionierte API-Verträge ist JSON oft lesbarer; bei stabilen Request-Verträgen hilft außerdem saubere Vertragsprüfung, damit Frontend und Backend dieselbe Payload-Struktur erwarten.
- Nutze FormData für Datei-Uploads, gemischte Formularfelder und vorhandene HTML-Formulare.
- Nutze JSON für klar strukturierte APIs ohne Dateien und mit stärker verschachtelten Daten.
- Erwarte bei FormData keine automatische Typisierung: Zahlen und Booleans kommen meist als Strings an.
- Plane Feldnamen bewusst, damit Frontend und Backend dieselben Schlüssel verwenden.
Wie wird FormData im Browser korrekt erzeugt?
FormData lässt sich entweder aus einem bestehenden Formular oder per Hand aufbauen. Die sicherste Alltagsregel lautet: Bei echten Formularen zuerst aus dem DOM erzeugen, nur für zusätzliche Werte oder dynamische Felder per append() ergänzen.
Der direkte Weg über das Formular verhindert, dass Felder vergessen werden. Browser übernehmen dabei die name-Attribute der Eingabefelder. Ohne name landet ein Feld nicht in der Payload; das ist einer der häufigsten Gründe, warum serverseitig scheinbar „Daten fehlen“.
const form = document.querySelector('form#profile-form');
form.addEventListener('submit', async (event) => {
event.preventDefault();
const data = new FormData(form);
data.append('source', 'settings-page');
const response = await fetch('/api/profile', {
method: 'POST',
body: data
});
if (!response.ok) {
throw new Error('Profil konnte nicht gespeichert werden');
}
});
Alternativ kann FormData vollständig manuell befüllt werden, etwa wenn Werte nicht aus einem sichtbaren Formular stammen. Dafür gibt es append() zum Hinzufügen und set() zum Ersetzen. Der Unterschied ist wichtig: append() kann mehrere Werte unter demselben Schlüssel sammeln, set() überschreibt vorhandene Einträge.
Gerade bei Checkbox-Gruppen oder Mehrfachauswahl ist dieses Verhalten relevant. Wer FormData wie ein Plain Object behandelt, übersieht schnell, dass ein Schlüssel mehrfach vorkommen darf. Beim Debugging hilft es, Einträge bewusst über for...of auszulesen statt console.log(data) zu vertrauen.
Fetch mit multipart/form-data: Welcher Header ist richtig?
Bei multipart/form-data sollte der Content-Type-Header in der Regel nicht manuell gesetzt werden. Der Browser ergänzt ihn selbst inklusive Boundary, also der technischen Trennmarkierung zwischen den Formularteilen.
Das ist einer der häufigsten Fehler in JavaScript-Code: Es wird 'Content-Type': 'multipart/form-data' gesetzt, weil das logisch klingt. Genau dadurch fehlt aber die korrekte Boundary, und viele Server können die Anfrage nicht mehr sauber parsen. Die richtige Lösung ist einfacher: FormData als body übergeben und den Header weglassen.
const avatarInput = document.querySelector('#avatar');
const data = new FormData();
data.append('displayName', 'Mina');
data.append('avatar', avatarInput.files[0]);
const response = await fetch('/api/account/avatar', {
method: 'POST',
body: data
});
const result = await response.json();
Ein zweiter Stolperstein betrifft Authentifizierung und CSRF-Schutz. Eigene Header wie Authorization dürfen natürlich weiterhin gesetzt werden; nur der Content-Type für multipart sollte unangetastet bleiben. Bei Session-basierten Anwendungen muss außerdem klar sein, ob Cookies mitgesendet werden sollen, etwa über credentials: 'same-origin'; bei Formularen mit Session-Login lohnt sich dafür ein Blick auf Session-Cookies und SameSite, weil Transport und Schutzmechanismen zusammenhängen.
Für fortgeschrittene Frontends ist noch ein Punkt wichtig: Upload-Fortschritt lässt sich mit fetch() nicht so direkt überwachen wie früher oft mit XMLHttpRequest. Wenn eine echte Progress-Anzeige nötig ist, muss die Transportentscheidung bewusst getroffen werden. Für viele Admin-Formulare ist das egal; bei großen Medien-Uploads aber nicht.
Welche Daten kommen im Backend wirklich an?
FormData transportiert Feldwerte meist als Strings und Dateien als separate Upload-Objekte. Backend-Code sollte deshalb nie davon ausgehen, dass Zahlen, Booleans oder Arrays bereits im gewünschten Typ vorliegen.
Das betrifft praktisch jede Sprache. In Node.js mit Express werden Textfelder und Dateien häufig über Middleware wie multer getrennt verarbeitet; in PHP landen Textwerte typischerweise in $_POST und Dateien in $_FILES. Die genaue API hängt vom Stack ab, aber das Grundprinzip bleibt gleich: Feldnamen müssen exakt passen, und Typkonvertierung ist eine eigene Aufgabe.
Gerade Arrays sorgen oft für Missverständnisse. Wenn mehrere Werte unter demselben Schlüssel gesendet werden, müssen Frontend und Backend dieselbe Konvention kennen, etwa mehrfaches append('tags', 'js') oder Namen wie tags[]. Wer hier keine klare Regel festlegt, produziert stillschweigende Datenverluste oder unerwartete Parser-Ergebnisse.
<?php
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit;
}
$name = filter_input(INPUT_POST, 'displayName', FILTER_SANITIZE_SPECIAL_CHARS);
$avatar = $_FILES['avatar'] ?? null;
if (!$name || !$avatar || $avatar['error'] !== UPLOAD_ERR_OK) {
http_response_code(422);
exit;
}
echo json_encode(['name' => $name, 'file' => $avatar['name']]);
Die sichere Empfehlung lautet, Uploads immer zu validieren: Dateigröße prüfen, MIME-Type nicht blind vertrauen, Dateinamen nicht ungefiltert übernehmen und Dateien nie direkt mit Originalnamen im Webroot ablegen. Dasselbe Prinzip gilt auch für normale Eingaben; robuste Serverlogik beginnt bei valider Eingabekontrolle und nicht erst beim Speichern.
Typische Fehler mit FormData und wie sie sich vermeiden lassen
Die meisten Probleme mit Fetch API und FormData sind keine Browser-Bugs, sondern kleine Denkfehler in der Datenmodellierung. Wer die typischen Stolpersteine kennt, spart viel Debugging-Zeit.
Ein häufiger Fehler ist das direkte Umwandeln in JSON, obwohl Dateien enthalten sind. JSON.stringify(new FormData(form)) liefert nicht die erwarteten Felder, weil FormData kein normales Objekt ist. Wenn Werte inspiziert werden sollen, müssen Einträge aktiv iteriert werden.
Ebenso verbreitet ist die Annahme, leere Felder würden verschwinden. Tatsächlich hängt das Verhalten vom Formular und von den konkreten Controls ab; deaktivierte Felder werden zum Beispiel nicht übertragen. Wer Formularzustände dynamisch per JavaScript verändert, sollte deshalb prüfen, ob Felder nur unsichtbar oder wirklich deaktiviert sind.
Auch Dateiuploads mit mehreren Dateien führen oft zu Verwirrung. Ein <input type="file" multiple> liefert mehrere Dateien, die einzeln angehängt oder als entsprechende Feldliste verarbeitet werden müssen. Für den Request selbst bleibt FormData richtig, aber das Mapping im Code muss die Mehrfachwerte berücksichtigen.
- Setze
Content-Typebei FormData nicht manuell. - Vergib für jedes relevante Feld ein korrektes
name-Attribut. - Nutze
append()oderset()bewusst, besonders bei mehrfachen Werten. - Erwarte im Backend Strings und konvertiere Typen explizit.
- Validiere Dateien serverseitig auf Größe, Typ und Upload-Fehler.
- Debugge FormData über Iteration, nicht über Annahmen zu
console.log.
Wie lassen sich FormData-Inhalte sauber prüfen und debuggen?
FormData lässt sich gut debuggen, wenn Einträge explizit gelesen werden. Die Kernaussage ist einfach: Statt die Instanz selbst anzuschauen, sollte der Code Schlüssel und Werte gezielt ausgeben.
Das hilft besonders bei dynamischen Formularen, in denen Felder per JavaScript ergänzt oder umbenannt werden. So fällt sofort auf, ob ein erwarteter Schlüssel fehlt, mehrfach vorkommt oder statt einer Datei nur undefined angehängt wurde. Bei Dateiobjekten ist außerdem sichtbar, ob wirklich ein File-Objekt vorliegt.
const data = new FormData(document.querySelector('#upload-form'));
for (const [key, value] of data.entries()) {
console.log(key, value);
}
console.log(data.get('title'));
console.log(data.getAll('tags'));
Nützlich sind dabei get(), getAll(), has() und entries(). Vor allem getAll() ist wichtig, wenn mehrere Werte unter demselben Namen existieren. Wer an dieser Stelle saubere Kontrolle will, profitiert häufig auch von einem soliden Umgang mit Fehlerpfaden im Frontend, weil Netzwerkfehler, Validierungsfehler und Parserfehler getrennt behandelt werden sollten.
Was ist die beste Praxis für moderne Formulare mit Upload?
Die beste Praxis für moderne Webformulare lautet: HTML-Struktur sauber halten, FormData für Transport nutzen und Validierung auf Client und Server trennen. Dadurch bleibt der Code wartbar, auch wenn später weitere Felder, Upload-Typen oder API-Endpunkte hinzukommen.
Für Fortgeschrittene lohnt sich eine kleine Architekturregel: Der Submit-Handler sollte Daten sammeln und senden, aber keine komplexe Geschäftslogik enthalten. Validierung, Fehlerdarstellung und Response-Verarbeitung bleiben besser in klar getrennten Funktionen. Das passt zu modernem JavaScript, egal ob mit Vanilla JS, React, Vue oder einem servernahen Framework im Backend.
Wenn kein Upload beteiligt ist, kann JSON weiterhin die bessere Wahl sein. Sobald aber Dateien, bestehende Formulare oder mehrere Feldtypen zusammenkommen, ist FormData kein altmodischer Sonderfall, sondern die technisch passende Web-API. Sie nutzt Browser-Standards, spart Konvertierungscode und reduziert Reibung zwischen DOM, Netzwerk und Backend.
FormData ist damit kein Ersatz für sauberes API-Design, sondern ein Werkzeug für den passenden Transportweg. Wer es gezielt einsetzt, bekommt robuste Formular-Requests ohne unnötige Serialisierung, ohne kaputte Upload-Header und ohne Missverständnisse bei der Serververarbeitung.

