I spent a solid chunk of my morning digging through StackOverflow articles, documentation, and Twitter threads to try to find out how to submit a login form with Axios. If you've ever tried to use Axios or a similar library to make requests on a Craft CMS site, you know it's a very hairy experience.
But not anymore! I'm going to show you how to configure Axios to play nice with Craft, and then how to tie that into Vue. Note: the Vue part is optional, most of this article (steps 1-3) will work with any or no framework.
Preface
Before we get into how to accomplish a clean setup, it's nice to understand some of the challenges that we're facing. Here are the two main issues with using Axios with Craft:
- Craft expects your data to be serialized, not JSON: If you attempt to send a JSON object to Craft, it won't accept it.
- Craft expects a CSRF token, so that it can validate the request.
With these issues in mind, let's get started!
Guide
Step 1: Create an Axios instance
Our first step is to install Axios (npm install axios || yarn add axios
), and create an instance of it that we'll use to make requests.
import axios from 'axios'
const axiosInstance = axios.create()
You can now use this Axios instance to make requests. Running axiosInstance.get('/endpoint')
will work as expected!
You may be thinking "why create an instance when you can use Axios directly?" Great question! Creating an instance of Axios will allow us to configure it specifically for Craft, and attach our instance to Vue for use in any component.
Step 2: Add your CSRF token
Generally when making requests to Craft, I see people passing their CSRF token with every request, doing something like this:
const data = { greeting: 'Hello, World!' }
data[window.csrfTokenName] = window.csrfTokenValue
axios.post('/', data)
// note: this code won't actually work, because "data" isn't serialized
That method works fine, but it's clunky to have to attach your CSRF token to each request. Thankfully, there's a solution to this problem!
The Right Way™️
Craft will allow you to pass a X-CSRF-Token
header with your request to bypass sending it along as a query parameter. Let's configure our Axios instance to send that header along automatically with each request, so we don't have to even think about it.
First, we need to output our CSRF token with Twig and put it somewhere that we can access.
{# index.twig #}
<script>
window.csrfToken = "{{ craft.app.request.csrfToken }}"
</script>
Then we can configure our Vue instance to add the X-CSRF-Token
header:
const axiosInstance = axios.create({
headers: {
'X-CSRF-Token': window.csrfToken
}
})
We've resolved one of our two challenges: Passing the CSRF token. Now let's tackle the second!
Step 3: Tell Axios to serialize your data
Before writing this article and trying to figure out a smooth workflow, here's how I would've tackled this problem.
import { stringify } from 'qs'
axios.post('/', stringify({ data: 'things' }))
Again, this works, but it's not ideal.
After some digging, I found a configuration option for Axios called transformRequest
which allows us to transform the data we pass into a post
request before sending it.
Let's update our Axios instance to automatically stringify our data:
import { stringify } from 'qs'
const axiosInstance = axios.create({
headers: {
'X-CSRF-Token': window.csrfToken
},
transformRequest: [
function(data) {
return stringify(data)
},
],
})
Now we have a fully working Axios instance that takes care of passing our CSRF token and converting our data to the format which Craft expects!
Check out the difference between these snippets, and try to tell me that this configuration work isn't worth it!
// before
import { stringify } from 'qs'
const data = { greeting: 'Hello, World!' }
data[window.csrfTokenName] = window.csrfTokenValue
axios.post('/', stringify(data))
// after
axiosInstance.post('/', data);
Step 4: Attach your Axios instance to Vue
This last step makes it even easier to use Axios within our Vue components. We can attach our configured Axios instance to our Vue instance.
const axiosInstance = axios.create(config) // see above for config
Vue.prototype.$axios = axiosInstance
You can then make requests from within methods, lifecycles, etc. in your Vue components like this.$axios.get('/')
. Here's a more real example:
Vue.component('things', {
methods: {
getThings(data) {
this.$axios.post('/', data);
}
}
}
Full Example
Here's the complete Twig/JS code for reference!
{# index.twig #}
<script>
window.csrfToken = "{{ craft.app.request.csrfToken }}"
</script>
// index.js
import axios from 'axios'
import { stringify } from 'qs'
const axiosInstance = axios.create({
headers: {
'X-CSRF-Token': window.csrfToken,
},
transformRequest: [
function(data) {
return stringify(data)
},
],
})
Vue.prototype.$axios = axiosInstance
Top comments (1)
Just Aunt Caroline dropping by to let you know that this is really helpful! Thanks for posting it.