POST, PUT, and PATCH Requests with Nuxt 3

Part 8 of 8 in Upgrading Nuxt 2 to Nuxt 3
Dan Pastori avatar
Dan Pastori May 3rd, 2022

Sending data to an API endpoint with Nuxt 3 is not much different than Nuxt 2. You will just have to refactor to use the underlying ohmyfetch wrapper provided by Nuxt 3. Let’s look at how you’d take your Nuxt 2 requests and update them to Nuxt 3.

Before we start our migration process, I wanted to point out one key point. We are sending data to the API in this migration tutorial, not retrieving it. When retrieving data, you will want to use some form of useAsyncData or useFetch with Nuxt 3.

Sending Data to an API with $fetch

In Nuxt 2, you’d either use the $http module or the $axios module. Both of those provided explicit $post, $put, or $patch methods that you could call. Let’s take a look at an example using the $axios module in Nuxt 2:

async function addCompany( data ){
    await this.$axios.$post('/api/v1/companies', data );
    // Handle response...
}

In the method above, we wrapped the $axios plugin in async/await since it returns a promise. We then pass in data to create a new company with the /api/v1/companies endpoint. We send the data as the second parameter to the $axios.$post method.

To convert the example method to the new Nuxt 3 $fetch module, you’d update the method to look like:

async function addCompany( data ){
    await $fetch( '/api/v1/companies', {
        method: 'POST',
        body: data
    } );
}

The syntax looks similar and it performs the same functionality, however there are a few key differences in the second parameter of the $fetch method. First, we explicitly define the HTTP verb we are using to send the data. In the example above, that’s POST, but could be PUT or PATCH with just the change of method name.

Next, we set the body key to the data we passed in to our method. You can pass an assortment of settings to $fetch including interceptors, headers, etc. With ohmyfetch, the response is already JSON encoded and doesn’t require an additional step like with a raw Fetch API request. Next up, let’s discuss uploading files in Nuxt 3.

Uploading files in Nuxt 3

The difference in uploading files with Nuxt 3 compared to what you’d do with Nuxt 2 comes from some of gotchas with the Fetch API.

Before we get started, there are a few differences when sending files to an API compared to sending straight JSON. First, we need to build our response as a Form Data object. This allows us to send data to a server similar to someone submitting a form on a website. We will have to convert our variables and files to this format before sending.

Second, we will have to send the file with a header stating the request is multipart/form-encoded. This is where things start to get weird with the Fetch API. Specifically with axios, we have to add a header to our request saying the Content-Type is multipart/form-encoded. With the Fetch API, we let the browser determine this. That means, we do not add a header. Otherwise, your data won’t submit with the proper boundaries and your API won’t interpret it correctly. Weird issue that might get resolved in an update to the Fetch API itself, but right now that’s how you have to do it.

Finally, and this is more of a Laravel specific API issue due to an underlying Symfony component, when submitting a file for an update (PUT/PATCH), you have to submit the request as a POST request, but add the _method: PUT to the form data.

Let’s take a look at an example with Nuxt 2 and Axios:

<template>
    <div>
        <label>Enter Company Name</label>
        <input type="text" v-model="name"/>

        <label>Select Header Image</label>
        <input type="file" multiple @change="handleFileSelection( $event )"/>
    </div>
</template>

<script>
export default {
    data(){
        return {
            name: ''
            files: []
        }
    },

    methods: {
        handleFileSelection( event ){
            let uploadedFiles = event.target.files;

            for( let i = 0; i < uploadedFiles.length; i++ ){
                this.files.push( uploadedFiles[i] );
            }
        },

        async submit(){
            let formData = new FormData();

            formData.append('name', this.name);

            for( let i = 0; i < this.files.length; i++ ){
                formData.append( 'images['+i+']', this.files[i] );
            }

            await this.$axios.post('/api/v1/companies', formData, {
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            });
        }
    }
}
</script>

Now the same request in Nuxt 3 would look like:

<template>
    <div>
        <label>Enter Company Name</label>
        <input type="text" v-model="name"/>

        <label>Select Header Image</label>
        <input type="file" multiple @change="handleFileSelection( $event )"/>
    </div>
</template>

<script setup>
const files = ref([]);
const name = ref('');

const handleFileSelection = ( event ) => {
    let uploadedFiles = event.target.files;

    for( let i = 0; i < uploadedFiles.length; i++ ){
        files.value.push( uploadedFiles[i] );
    }
}

async function submit(){
    let formData = new FormData();

    formData.append('name', name.value);

    for( let i = 0; i < files.value.length; i ++ ){
        formData.append('images['+i+']', files.value[i] );
    }

    await $fetch( '/api/v1/companies', {
        method: 'POST',
        body: formData
    } );
}
</script>

In both examples, I included the HTML just to show the inputs and the v-models . They will work, but the design isn’t ideal. Besides the differences using the Options API in Vue 2 and the Composition API in Vue 3, the only difference two differences are the Fetch API, and the explicit defining of the headers in Nuxt 2. With Nuxt 2 and $axios, we have to explicitly pass the headers array and let the server know what the content type and encoding are. With Nuxt 3 and the Fetch API, we leave the up to the browser to send correctly.

One of the keys to setting files to a local Vue variable is to watch the @change on the file input and handle the event. You can access the files selected from the FileList provided.

We use this functionality in a variety of different ways in ROAST for uploading files. If you want to see how to handle these requests from a Laravel API or in the context of a full application, check out our book! If you have any questions, feel free to reach out on Twitter (@danpastori) or on our community forum!

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Gold Sponsor SnapShooter Backups

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We’re privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.

What topics are you interested in?
Are you interested in our upcoming book?