POST, PUT & PATCH Requests with VueJS and Axios
Part 3 of 4 in Your Guide to Using an API with VueJS/NuxtJS and AxiosThese requests allow you to manipulate data on your API. If you want a refresher, visit Using Axios to Make API Requests With VueJS where we go over the basics of these requests. Using these requests properly and setting up your API to accept data through these request types ensure that developers know how to interact with your API the right way.
Let’s dive into these requests and how we can use them.
Types of Body Data
When working with these request types, you send data through the body to the server. The two formats we will use to send data to the server is through JSON
and application/x-www-form-urlencoded
.
By default Axios sends any data to the server as JSON
. In most cases, this is fine. However, when you wish to upload a file, you will need to send the data as application/x-www-form-urlencoded
. This is extremely important and cover it in-depth in Your Guide To Uploading Files with VueJS and Axios.
For now, let’s send a basic POST request to the server.
Sending POST Data to Create a Resource
When sending a POST request, you should assume that the server is going to create a new session (authorize a user) or create a new resource. Like we mentioned, this data will be sent as JSON
by default or application/x-www-form-urlencoded
. If you upload files to your server, you have to use a POST request.
Using VueJS
If you followed along in Configuring Axios Globally with VueJS, we set up Axios to be global. This means it should be present in any component. To send a POST request using Axios structure it like this:
axios.post('https://api.com/v1/resource',
{
name: 'name',
date: 'date'
},
{
// Config
}
);
Let’s break down this request example. The first parameter is the URL of the API endpoint we will be sending the data to. Like I mentioned, this should be the URL of an endpoint that creates a resource.
The second parameter is the data you will be sending to the server. Unlike a GET request, the POST request sends the data through the body of the request. In the example above we didn’t change anything, so by default this will be sent as JSON.
If you are uploading a file, as mentioned above, or if your server requires application/x-www-form-urlencoded
data (which some do, especially legacy systems) we need to make a quick change to this request. To do that, we need to transform the data to be FormData
.
To do this, we need to adjust the code to look like this:
let formData = new FormData();
formData.append('name', 'name');
formData.append('date', 'date');
axios.post('https://api.com/v1/resource',
formData,
{
// Config
}
);
What we do here, is we initialize a local variable named formData
and create a new FormData
object. We then append the data that we want to send using the method on the FormData
object named append()
. The first parameter of the append()
method is the key
. This is if you were to write an <input type="text" name="name"/>
element. The second parameter is the value
.
When submitting the form this way, it’s similar to sending the input from an HTML form to the server in the way that it’s encoded. You can append files to the FormData
object which is how we upload files with axios.
Even though it’s similar, we will walk through how to do this with NuxtJS and cover some of the differences.
Using NuxtJS
So remember, NuxtJS has the Axios plugin and prefers the async/await
syntax? Their preferred method looks just slightly different than the straight VueJS format.
Let’s take a look at the same POST request that we touched on first:
async nameOfFunction(){
await this.$axios.$post('https://api.com/v1/resource',
{
name: 'name',
date: 'date'
},
{
// Config
})
}
Usually you’d wrap your Axios requests in NuxtJS in an async
function. This Remember, you can put other functionality after the await
. This would be methods to process or format your data or handle the successful POST request. You can also do this without async/await
within nuxt by just calling:
this.$axios.$post('https://api.com/v1/resource',
{
name: 'name',
date: 'date'
},
{
// Config
})
You will have to handle the request with .then()
at the end if you don’t use async/await
.
When sending a request with FormData()
, it’s exactly the same as VueJS where you have to create your FormData
object and send it as the second parameter. Speaking of Form Data, it can get pretty cumbersome as you add fields to your form. So I created a mixin!
Transforming to Form Data Getting Cumbersome?
I wrote a VueJS mixin for that! As your form grows, you don’t want to keep writing code to send the data to the server when you add a form field. The VueJS mixin is below:
/**
* Simple transformation to form data for uploading files
* using VueJS and Axios.
*
* Assuming that you pass your entire form as the `form`
* paramter. For example:
*
* data(){
* return {
* form : {
* name: 'Dan',
* job: 'Software Developer'
* website: 'https://serversideup.net'
* logo: FileList
* }
* }
* }
*
* this.transfromToFormData( this.form, ['logo'] );
*
* For updating and sending a PUT request add:
*
* this.transformToFormData( this.form, ['logo'], 'PUT' );
*
* When sending a form as form data, you might need to send as
* POST, but pass a _method parameter for 'PUT'.
*
*
*
* @param {object} form The object representation of a form.
* @param {array} fileKeys An array of keys that represent files
* @param {string} method Optional: The method used to send the form such as 'PUT'
*/
transformToFormData( form, fileKeys, method = '' ){
// Initializes the form dat object that we will be appending to
let formData = new FormData();
// Iterates over all elements in the form. Adds them to the
// form data object. If the value is a file, we append the
// file to the form data.
for (let [key, value] of Object.entries(this.form)) {
if( fileKeys.indexOf( key ) > -1 ){
if( value !== '' ){
formData.append( key, value[0] );
}
}else{
// Booleans don't send as a true boolean through form data.
// We send it as a 1 or 0 to make for easier processing on the
// backend
if( typeof value === "boolean" ){
value = value ? 1 : 0;
}
formData.append( key, value );
}
}
// If we have a method we need to send as, we append it here
if( method != '' ){
formData.append( '_method', method );
}
return formData;
},
So there’s a foreshadowing of one of the things we have to do when submitting data through PUT
or PATCH
(used mainly in a PHP/Laravel backend). Can you find it? We will touch on it soon!
Anyways, I have an example above in the comments on how to use this, but let’s walk through it.
The first parameter of this mixin is the form
(see quick tip). This is all of the data you will be sending to the server. Typically this is in JSON object that we will iterate over and transform to form data.
Next up, we have fileKeys
. This is an array that identifies any of the keys in your form
array that are files. We want to handle those in a special manner. Since all v-model
inputs that are of type="file"
are a FileList
, (for more information check out Mozilla’s documentation on this object), we want to grab the first value of the array, which is the individual file.
Finally we have the method
parameter. This was the little “foreshadowing” for PUT
and PATCH
. By default this is an empty string. However, if you choose to pass PUT
or PATCH
you will get the _method
form field attached to your request. With Laravel PHP specifically, this needs to be added when you send data as a FormData
object. Even though you will be wanting to send a PUT
or PATCH
request, you must send the request as POST
but with the _method
set to PUT
or PATCH
Laravel will handle it correctly. We will go through an example in the next section.
The only other piece of the code I’d like to point out is the transformation to a Boolean value. If you have a checkbox that you want to send to the form and the value is true
or false
these will be sent as a String. We went to send these as a 1
or a 0
so we can perform any necessary operations on our backend.
Hopefully this helps! You can implement this mixin in both NuxtJS and VueJS and it will work for your form requests. It does, however, work best when combined with the next “quick tip”.
Quick Tip with Form Components
When working on forms in both NuxtJS and VueJS, a simple structure I’ve been incorporating into my components is setting up the form as an object in the data()
of the component. Then each individual value would be a key on that object. This is especially helpful with large form components that compute data or have other inputs that you don’t want to necessarily send to the server. I saw this structure in the InertiaJS documentation and it made complete sense to me! Let’s take a look.
Say we have a component that has 3 inputs that we want to send to the server, first name, last name, and email address. I’d structure the component like this:
<template>
<div>
<label>First Name</label>
<input type="text" v-model="form.first_name"/>
<label>Last Name</label>
<input type="text" v-model="form.last_name"/>
<label>Email</label>
<input type="text" v-model="form.email"/>
<button v-on:click="submit()">Submit</button>
</div>
</template>
<script>
export default {
data(){
return {
form: {
first_name: '',
last_name: '',
email: ''
}
}
},
methods: {
submit(){
axios.post('https://api.com/v1/user', this.form)
.then(function( response ){
// Handle success
}.bind(this));
}
}
}
</script>
So the big point I’m trying to get across is that anything sent to the server is in the form
object. You can bind any input to the associated object like so: v-model="form.first_name"
. Now why do it this way? Even if you don’t have a big form, you can just pass the whole form directly to your API endpoint by passing this.form
as the second parameter in your Axios request in the submit()
method.
No passing 3 variables and updating it every time you add a new field. You can also easily transform to form data using the mixin above by passing this.form
to the transformToFormData()
mixin.
The other benefit is this allows you to keep some of the UI data outside of the scope of your form so it’s easier to manage. I really like this approach and I feel it leads to much cleaner development!
That’s the core of sending data to the server! Next up, let’s touch on PUT
and POST
.
Sending PUT/PATCH Data to Update a Resource
So there’s not really a lot of difference between PUT
and PATCH
compared to POST
besides the Axios method you use. However, let’s touch on these.
Difference in URLs
In a properly structured RESTful API, you’d create a resource by sending a POST
request to an endpoint with the name of the resource. For example, to create a User
resource, you’d send a POST request to /api/v1/users
or a URL that looks similar.
With a PUT
/PATCH
request, you want to update a specific user. This would mean passing a unique identifier to the server. Once again, in a properly structured RESTful API, this would append to the end of the url. Your API request to update a user would be /api/v1/users/{user}
with {user}
being the unique identifier of the user.
Determine the Method to Use
Remember when we discussed which method to use when updating a resource? If not, here’s a brief reminder, PUT
is used to update an entire resource (send the whole updated resources to the server), and PATCH
when you only have a piece of data to update.
The PUT
method is called in Axios through VueJS like so:
axios.put('https://api.com/v1/resource/{id}',
{
name: 'name',
date: 'date'
},
{
// Config
}
);
and the PATCH
method like this
axios.patch('https://api.com/v1/resource/{id}',
{
name: 'name'
},
{
// Config
}
);
Sending FormData()
is the exact same as through POST
as well. The one MAJOR difference I wanted to point out when sending FormData()
(which we touched a little bit on) is when sending it to a Laravel backend. You must send the _method
set to PUT
or PATCH
and then make the request through POST
. Even though we are making the request through POST
since we have the _method
set to PUT
or PATCH
Laravel will handle that correctly.
Using NuxtJS, you can use the same format as well. Just make sure you use the axios module like so:
this.$axios.$put('https://api.com/v1/resource',
{
name: 'name',
date: 'date'
},
{
// Config
})
Conclusion
Hopefully that helped clear up any confusion on sending data to an API! Next up, we will work on dealing with a lot of API requests and abstracting them into wrappers so you can re-use them easily.
Then we will go through some of the interceptors you can use to handle authentication errors and sending proper headers with requests for authentication.
Finally, we will deal with handling errors from the API request and properly displaying them.
Let me know if you have any questions and reach out in the comment section below or on Twitter (@danpastori).