POST, PUT, and PATCH Requests with Nuxt 3
Part 8 of 9 in Upgrading Nuxt 2 to Nuxt 3Sending 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!