Post form data and file in one useFetch call in Nuxt 3 – Nuxt.js

by
Ali Hasan
nuxt.js nuxt3 nuxtjs3

The Problem:

I want to post form data (uncluding strings, numbers, arrays) AND a file to backend using one useFetch() call, in TypeScript.

Specifically, I want to use a library like useFetch to make a request that includes both form data (such as strings, numbers, and arrays) and a file, in a single request. However, I am having trouble finding a way to do this that is compatible with TypeScript and the FormData class.

I have tried adding numbers and arrays to the FormData instance, but I get errors from TypeScript because FormData.append() only accepts strings or Blobs. I have also tried JSONifying numbers and arrays and posting them as strings, but then I get errors on the backend.

I have also tried posting form data as a simple JS Object, but this does not work for files.

Ultimately, I want to be able to post all of the data in a single request, without having to make multiple requests or convert the data on the backend.

The Solutions:

Solution 1: Use `FormData` Efficiently

To post both form data and files using a single `useFetch` call, follow these steps:

  1. Create a FormData instance.

  2. Append the file to the FormData instance using formData.append("file", yourfileorimage).

  3. Append additional form data to the FormData instance using the following loop:

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]);
}
  1. Call useFetch with the following parameters:
  • YOUR-API-URL as the URL
  • PUT as the HTTP method
  • formData as the body
  • { "cache-control": "no-cache" } as the headers

The cache-control header ensures that the browser does not cache the response.

This method allows you to post both form data and files using a single fetch call.

Solution 2: Use `FormData` with JSON Stringification

In this solution, we create a single FormData instance and append the form data and file to it. For arrays and numbers, we JSON stringify them before appending them to the FormData. This ensures that the data is sent correctly and can be parsed by the backend.

<!– begin snippet: js hide: false console: true babel: false –>

<!– language: lang-js –>

&lt;template&gt;
    &lt;div&gt;
        &lt;div&gt;
            &lt;input
                type=&quot;file&quot;
                @change=&quot;fileChange&quot;
            /&gt;
        &lt;/div&gt;
        &lt;button @click.prevent=&quot;sendDataAndUploadFile&quot;&gt;Send Data and Upload file&lt;/button&gt;
    &lt;/div&gt;
&lt;/template&gt;

<script lang="ts" setup>

const uploadedFile = ref&lt;File | null&gt;(null)

function fileChange(file: Event): void {
    const fileData = file.target as HTMLInputElement
    if (fileData.files) {
        uploadedFile.value = fileData.files[0]
    }
}

const BookData = {
    title: &#39;The Adam&#39;,
    author: &#39;John Doe&#39;,
    publisher: &#39;John Doe&#39;,
    ratings: [4, 5, 4.5]
}

async function sendDataAndUploadFile() {
    const formData = new FormData()
    if (uploadedFile.value) {
        formData.append(&#39;cover_image&#39;, uploadedFile.value)
        for (const [key, value] of Object.entries(BookData)) {
            if (Array.isArray(value)) {
                for (const rating of value) {
                    formData.append(`${key}[]`, rating.toString())
                }
            } else {
                formData.append(key, value as string)
            }
        }
    }
    await useFetch(&#39;/api/upload&#39;, {
        method: &#39;POST&#39;,
        body: formData
    })
}

&lt;/script&gt;
&lt;template&gt;
    &lt;div&gt;
        &lt;div&gt;
            &lt;input
                type=&quot;file&quot;
                @change=&quot;fileChange&quot;
            /&gt;
        &lt;/div&gt;
        &lt;button @click.prevent=&quot;sendDataAndUploadFile&quot;&gt;Send Data and Upload file&lt;/button&gt;
    &lt;/div&gt;
&lt;/template&gt;
&lt;style scoped lang=&quot;css&quot;&gt;&lt;/style&gt;

<!– end snippet –>

Q&A

How to pass FormData and File in a single useFetch call?

You must need to pass FormData() into useFetch and append your file/image and another data in FormData()

Is it possible to post form data (strings, numbers, arrays) and a file in one useFetch call, using TypeScript?

Yes, it is possible to post form data and a file in one useFetch call using TypeScript by utilizing the FormData() object.

Video Explanation:

The following video, titled "Nuxt 3 full course build and deploy | #Nuxtjs #vue #nuxt3 - YouTube", provides additional insights and in-depth exploration related to the topics discussed in this post.

Play video

... Fetch Data ⏳ (02:13:58) Auth State ⏳ (02:23:20) 3 Kinds of Middleware ⏳ (02:35:13) Server Routes ⏳ (02:43:32) Deploy to Netlify Social ...