I want to post form data (uncluding strings, numbers, arrays) AND a file to backend using one useFetch()
call, in TypeScript.
What I tried
If I add strings and a file to the FormData
instance, all works great.
But if I try to add numbers, or arrays to the FormData
instance, I get errors from TypeScript (because FormData.append()
accepts only strings or Blob
s). I tried to JSONify numbers and arrays and post them as strings, but then I get errors on backend (I don't want to convert anything on backend).
I tried to post form data as simple JS Object - it works, too, but not for a file.
I ended up with the following code, where I decided to post strings/numbers/arrays as JS Object to create new DB record on backend (createNewBook
), then update it using PATCH
method and pass file in FormData
instance (updateBookCover
).
function useApi(
query: Object | undefined = undefined,
method: string = "GET",
token: string | null = null,
formData: FormData | Object | undefined = undefined,
) {
const config = useRuntimeConfig();
const get: typeof useFetch = (url) => {
return useFetch(url, {
params: query,
baseURL: config.public.apiBase + "/api/v1",
key: url.toString(),
method: method as any,
headers: token ? [["Authorization", "Token " + token]] : undefined,
body: formData,
});
};
return { get };
}
export async function createNewBook(book: Book) {
const authStore = useAuthStore();
let authorIds: number[] = [];
// Convert array of `Authors` to array of ids for backend.
book.authors.forEach((a) => authorIds.push(a.id));
const formData = {
title: book.title,
authors: authorIds,
publisher: book.publisher?.id,
year: book.year,
pages: book.pages,
description: book.description,
contents: book.contents,
};
const { get } = useApi(undefined, "POST", authStore.token, formData);
return await get<Book>("/books/create/");
}
export async function updateBookCover(bookId: number, coverImage: File) {
const authStore = useAuthStore();
const formData = new FormData();
formData.append("cover_image", coverImage);
const { get } = useApi(undefined, "PATCH", authStore.token, formData);
return await get<Book>(`/books/${bookId}/`);
}
Full module code - https://github.com/hazadus/drf-nuxt-library/blob/main/frontend/useApi.ts
What I want to achieve
The code above works well, but it would be great to post everything (form data and files) using one useFetch
call.
You must need to pass FormData()
as below
let formData = new FormData();
//append your file or image
formData.append("file", yourfileorimage);
//append another data
const formdata = {
title: book.title,
authors: authorIds,
publisher: book.publisher?.id,
year: book.year,
pages: book.pages,
description: book.description,
contents: book.contents
};
for (const item in formdata) {
formData.append(item, formdata[item]);
}
return await useFetch("YOUR-API-URL", {
method: "PUT",
body: formData,
headers: {"cache-control": "no-cache"},
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With