Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Post form data and file in one useFetch call in Nuxt 3

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 Blobs). 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.

like image 882
Hazadus Avatar asked Oct 16 '25 16:10

Hazadus


1 Answers

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

like image 175
Kishan Bhensadadiya Avatar answered Oct 18 '25 17:10

Kishan Bhensadadiya