A photo goes up the way a browser form sends a file, as multipart/form-data. Countless React Native 36,878 examples append a plain object, form.append('cover', { uri, name, type }), which React Native's own networking reads from disk. BookNest tried exactly that, and the upload failed at once:
E ReactNativeJS: [Error: Uncaught (in promise, id: 0) Error: Unsupported FormDataPart implementation]
The message comes from expo/src/winter/fetch/convertFormData.ts. In SDK 57 the global fetch is Expo 6,418 's WinterCG-style implementation, whose FormData parts must be strings, Blobs, or objects with a bytes() method; the { uri } convention is React Native's alone. expo-file-system's File class provides bytes(), so after npx expo install expo-file-system and a rebuild, the upload became:
import { File } from 'expo-file-system';
// ...
export async function uploadCover(uri: string) {
const form = new FormData();
form.append('cover', new File(uri));
const res = await fetch(`${API}/covers`, { method: 'POST', body: form });
if (!res.ok) throw new Error(`upload failed: ${res.status}`);
return (await res.json()) as { bytes: number };
}The practice API's new POST /covers only counts the bytes it receives. A photo from the camera and the picked back cover arrived as:
multipart/form-data, 17093 bytes 20:35:23 POST /covers 201 multipart/form-data, 55152 bytes 20:39:59 POST /covers 201
Resize before uploading (the picker's quality, or expo-image-manipulator), show progress for large files, and upload straight to object storage with a pre-signed URL rather than through your API server when files are big.