## Sub-Project Documentation The following open source projects have their own dedicated documentation sites with AI-optimized content: - [Docker PHP - Documentation Index](https://serversideup.net/open-source/docker-php/llms.txt): AI-optimized documentation overview for Docker PHP. - [Docker PHP - Full Documentation](https://serversideup.net/open-source/docker-php/llms-full.txt): Complete Docker PHP documentation. - [Spin - Documentation Index](https://serversideup.net/open-source/spin/llms.txt): AI-optimized documentation overview for Spin. - [Spin - Full Documentation](https://serversideup.net/open-source/spin/llms-full.txt): Complete Spin documentation. --- # Accessing Route Parameters in Nuxt 3 Accessing route parameters is an essential for your Nuxt 3 app. Route parameters are the dynamic pieces of your URL that determine what resource or content is loaded. If you are following along in this [migration guide](https://serversideup.net/guides/upgrading-nuxt-2-to-nuxt-3/), you probably have seen them accessed in action. In the last section, we loaded [async data](https://serversideup.net/blog/using-async-data-in-nuxt-3/) from the API to display a company or cafe in ROAST. To load the specific resource, we grabbed a route parameter. Let's touch on some of what it took to migrate ROAST from Nuxt 2 to Nuxt 3 and access the route parameters. ## Step 1: Naming Pages The first step in using route parameters is to set up your naming conventions for your pages correctly. In both Nuxt 2 and Nuxt 3, the way you name your page component reflects the name of the variable. Let's use the individual company URL which would match `https://roastandbrew.coffee/companies/{company}`. The `{company}` would be the dynamic route parameter we will want to access. However, in order to even gain access, we have to set up our page structure correctly. ### Nuxt 2 Page Naming Conventions There are two ways you could do this in Nuxt 2. First, would be to add a page in the pages directory named `/pages/companies/_company.vue`. Any page prefixed with an `_` would be accessible as a route parameter with the name following the `_` (I.E. `company`). The second way you could do this with Nuxt 2 is if you had a directory that started with an `_` and you named a vue page within that directory `index.vue`. The directory would look like: `/pages/companies/_company/index.vue`. This approach is recommended if you have a page to edit a resource. You could throw an `edit.vue` file in the directory and get the following URLs: `https://roastandbrew.coffee/companies/{company}` and `https://roastandbrew.coffee/companies/{company}/edit`. ### Nuxt 3 Page Naming Conventions To migrate these pages to Nuxt 3, the first step is updating the naming conventions. Instead of an underscore, you need to change the name to be bracketed. For example, if we had `/pages/companies/_company.vue` in Nuxt 2, we'd change the name to be `/pages/companies/[company].vue` in Nuxt 3. The same process goes for folder naming conventions. You'd have to update `/pages/companies/_company/index.vue` to be `/pages/companies/[company]/index.vue` in Nuxt 3. Simple update, but important nonetheless. ## Step 2: Accessing Dynamic Route Parameters Now that we have our page layouts named correctly, we can access the dynamic route parameters that we configured. This is useful when you want to load a resource by its identifier on the page. Let's take a look at what we had in Nuxt 2: ```javascript [Accessing the route parameter in Nuxt 2] this.$route.params.company ``` In Nuxt 2, you could access the route parameters by accessing the global `$route` plugin. You'd then access the `company` variable under the `params` key. In Nuxt 3, the same functionality looks like: ```javascript [Accessing the route parameter in Nuxt 3] const route = useRoute(); route.params.company ``` You can access the `route` object once you load it from the `useRoute()` composable function. This must be set up in your `setup()` method or your ` ``` I'm going to do a quick over view of some of the form submission, but for more information, check out: [API Driven Form Submissions with Javascript, Vuex and Laravel - Server Side Up](https://serversideup.net/blog/api-form-submissions-javascript-vuex-laravel/). We need to add a quick template that allow the user to edit their fields. I added the following: ```vue [Manage profile page] ``` This template binds inputs to the appropriate data model for the field we are editing. We also have a quick loading state to show while the profile is loading and a notification when the profile has been updated successfully. For our data we will add the fields we are editing: ```javascript [Data fields we are managing] data(){ return { favorite_coffee: '', flavor_notes: '', profile_visibility: 0, city: '', state: '' } }, ``` There will be a few pieces of computed data we will watch from the Vuex state: ```javascript [Computed data we are watching] watch: { 'userLoadStatus': function(){ if( this.userLoadStatus == 2 ){ this.setFields(); } }, 'userUpdateStatus': function(){ if( this.userUpdateStatus == 2 ){ $("#profile-updated-successfully").show().delay(5000).fadeOut(); } } }, ``` The `userLoadStatus` we watch so we can set the data to what the user is when they have been loaded. The `userUpdateStatus` is watched so we determine when to display the profile updated notification. We also check, when created if the `userLoadStatus` is equal to `2` if it is, then we set the fields because we have enough data: ```javascript [Validate the data is loaded] created(){ if( this.userLoadStatus == 2 ){ this.setFields(); } }, ``` For our computed methods, we grab the `user` the `userLoadStatus` and the `userUpdateStatus`: ```javascript [Computed data for our page] computed: { /* Gets the authenticated user. */ user(){ return this.$store.getters.getUser; }, /* Gets the user load status. */ userLoadStatus(){ return this.$store.getters.getUserLoadStatus(); }, /* Gets the user update status */ userUpdateStatus(){ return this.$store.getters.getUserUpdateStatus; } }, ``` These will help us display the information we need to on the page. For methods, we have the `setFields()` method which sets the data to what the user already has in their profile: ```javascript [Set the fields loaded from the API] setFields(){ this.profile_visibility = this.user.profile_visibility; this.favorite_coffee = this.user.favorite_coffee; this.flavor_notes = this.user.flavor_notes; this.city = this.user.city; this.state = this.user.state; }, ``` We also have an `updateProfile()` method which validates the information we need in our profile and dispatches the `editUser` action, sending all of our data through the API to be saved for the profile: ```javascript [Method to update profile] updateProfile(){ if( this.validateProfile() ){ this.$store.dispatch( 'editUser', { profile_visibility: this.profile_visibility, favorite_coffee: this.favorite_coffee, flavor_notes: this.flavor_notes, city: this.city, state: this.state }); } }, ``` The `validateProfile()` method for now returns `true` because there isn't anything we need to specifically validate. It's great to have in place for future validations to be added. Next, we just have to add a link to the navigation for this page and we have a user profile! ## Step 8: Add Link To Profile Page Now that we have the profile page made, we need to add a link to the page. All we have to do is open up `/resources/assets/js/components/global/Navigation.vue` and add the following code below our avatar: ```html [Link to the profile page with Vue Router] Profile ``` This will add a link to the navigation to the page to edit our profile! ## Conclusion We now have user profiles added to collect some information from our users and expand up to adding friends and other possibilities. Be sure to check out all of the code here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""} Of course, reach out if you have any questions and sign up for our mailing list if you are interested in more about API Driven Development: [Server Side Up General List](https://serversideup.net/subscribe/) # Adding server configurations within your Laravel App Is it a good idea to put your server configurations within Laravel? In this video, we cover the benefits and challenges of putting server configurations within your Laravel application using Docker. # Adopt a Ubiquiti USG to a UniFi Cloud Controller & automate device deployments Learn how to adopt a UniFi Security Gateway to a UniFi Cloud Controller. We'll also set up our UniFi network to automatically deploy other UniFi devices with our network settings. See the entire "Complete Ubiquiti UniFi + Synology Network Build" course here: {rel=""nofollow""} # Advanced Data Fetching with Nuxt 3 Working with ROAST and Bugflow, both having a Nuxt 3 frontend, I've come across a lot of scenarios where I've had to do some more advanced data fetching with Nuxt 3 and the provided composables. I've written a basic article about [using asyncData() in Nuxt 3](https://serversideup.net/blog/using-async-data-in-nuxt-3/). This article will be extending on the previous article and covering some more advanced scenarios like automatic watch sources, infinite scrolling, pagination and tips for dealing with multiple async data sources. Let's get started! ## Nuxt 3 Watch Sources with `useAsyncData()` To be honest, this is my favorite feature of any of the new Nuxt 3 data fetching composables. Watch sources works with `useFetch()` and `useAsyncData()` along with their `lazy` counterparts. Let's set up a use case so we can explain how wild this is. Say you have a page that has a variety of filters, settings, parameters, etc used to query an API. In ROAST this would be like the [search page](https://roastandbrew.coffee/search){rel=""nofollow""} or in [Bugflow](https://bugflow.io){rel=""nofollow""}, our bug listing page. These pages allow the user to set their parameters, filters, etc. and call the API to get data that matches what they are looking for. Every time the user updates one of these filters, you will have to re-query the API. Normally, this would be done by calling a function, or building a reactive query string. However, with watch sources, this is much easier! [Watch sources](https://nuxt.com/docs/api/composables/use-async-data#params){rel=""nofollow""} allow you to "watch reactive sources to auto-refresh". What does that mean? That means when the user changes a parameter you used in your query, the data auto refreshes. It's amazing! Let's look at the following code: ```vue [Example of watch sources] ``` Before we start breaking this apart, I'm using `useAsyncData()` but watch sources work with all the new data fetching composables. In this example we are loading the first set of cafes from the {rel=""nofollow""} endpoint. This is a paginated resource and we can search cafes to get the find the ones we are looking for. So we set up the following filters: ```javascript [Filter set up] const search = ref(''); const page = ref(1); ``` Next, we set up our data fetching request and pass these two parameters in the `params` section: ```javascript [Adding filters to the watch data] const { data: cafes, error } = await useAsyncData( 'cafes', () => $fetch( `/api/v1/cafes`, { // ... other options params: { page: page.value, search: search.value, } } ), { watch: [ page, search ] } ); ``` Looks pretty familiar so far! However, the magic comes in the third parameter to the `useAsyncData()` composable and that's the `watch` key. What this does is allows us to pass an array of reactive sources that will re-query when changed. Let's start with `page` where we want to add simple next and previous pagination. When the user increments or decrements the `page` value, we want to refresh the data source with the next paginated set of data. Instead of calling `refresh` (a method in the composable) or dynamically computing the query string, we can automatically load the new data instantly in Nuxt 3 when the watch source changes. Add the following methods: ```vue [Pagination example] ``` Notice how these methods don't explicitly call a refresh or another method to reload the data? That's because `page` is one of the watch sources defined. All you have to do is increment or decrement the `page` value. This will automatically refresh the data! Super convenient for dynamic data fetching with Nuxt 3 and implementing searches, pagination, or other filters. The `location` variable works the same way. Once it changes, a new request to load the data will be made. However, you will probably want to debounce the input if it's a text search or you will send way too many API requests and blow through the throttling! Here's our final code example: ```vue [Full example of UI and Watch Sources] ``` I really love how the watch sources clean up the code base and make the experience feel so much more optimized and dynamic. Let's touch on another advanced data fetching scenario, infinite scrolling, or "compounding/appending" requests. ## Append Data from `$fetch` in Nuxt 3 Specifically in Bugflow, we ran into a scenario where we wanted a "compounding" or "infinite scroll" type scenario. We had a bug listing screen where the user can see all bugs on a project, newest to oldest. As they scrolled, they had the option to load more. In this scenario we needed to keep appending the data returned from a data fetch with Nuxt 3 in order to show it all on one screen. For this scenario, I recommend using the `$fetch` method that's globally available to directly call the API. Why not a composable? You can, but the composables provided want to replace the data every request. That's how they are designed. We want to append the data. Take a look at the code: ```javascript [Example with appending data (Infinite Scroll)] const page = ref(1); const lastPage = ref(1); const companies = ref([]); const pending = ref(false); onMounted(() => { loadCompanies(); }) const loadMore = () => { if( page.value + 1 <= lastPage.value ){ page.value = page.value + 1; loadCompanies(); } } const loadCompanies = () => { pending.value = true; $fetch(`/api/v1/companies`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { page: page.value } }).then( function( companies ){ appendCompanies( companies.data ); pending.value = false; lastPage.value = companies.last_page; }); } const appendCompanies = ( newCompanies ) => { newCompanies.forEach( ( company ) => { companies.value.push( company ); }); } ``` As you can see, the code is a little bit more verbose than the elegant way you'd typically load data with `useFetch()` or `useAsyncData()`. However, the power is there. Let's start at the top: ```javascript [Variable set up for infinite scroll] const page = ref(1); const lastPage = ref(1); const companies = ref([]); const pending = ref(false); ``` Right away we declare 4 variables, `page`, `lastPage`, `companies`, and `pending`. Since we are loading a paginated resource, we keep track of the `page` (current page we are on) and the `lastPage`(the final page of results for the resource). We also implement our own simple `pending` state while more data loads. If you were using the `useAsyncData()` composable, this would already be available for you. Let's jump to our `loadCompanies()` method: ```javascript [Load companies method] const loadCompanies = () => { pending.value = true; $fetch(`/api/v1/companies`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { page: page.value } }).then( function( companies ){ appendCompanies( companies.data ); pending.value = false; lastPage.value = companies.last_page; }); } ``` What this does is first, set the `pending` value to `true`. This allows us to display a loader or handle other events to show the user the data is loading. Next we call `$fetch` on our endpoint and pass the `page` param. This will grab the current paginated chunk of companies from our API. Most importantly, we listen to the successful return of the promise with `.then()`callback. ```javascript [Callback to append the loaded data] .then( function( companies ){ appendCompanies( companies.data ); pending.value = false; lastPage.value = companies.last_page; }); ``` Upon success, we take the response, append the new companies to the local reactive companies array, set `pending` to `false`, and save the last page so we know when to not load any more. The `appendCompanies()` method is the guts of our "compounding" or "infinite scrolling" takes place: ```javascript [Functionality to append companies] const appendCompanies = ( newCompanies ) => { newCompanies.forEach( ( company ) => { companies.value.push( company ); }); } ``` This simple method takes the new companies, iterates over them, and appends them to the local `companies` array which is reactive. We can then display the reactive companies in our template like: ```html [UI to display all companies] ``` Finally, we have our `loadMore()` method. This method simply increments our page number and calls the `loadCompanies()` method. Unlike in the first section, using a watch source, we aren't using a composable so we have to call the method ourselves. The `loadMore()` method looks like: ```javascript [Load more method] const loadMore = () => { if( page.value + 1 <= lastPage.value ){ page.value = page.value + 1; loadCompanies(); } } ``` For the sake of thoroughness, I also initially call the `loadCompanies()` method with the `onMounted()` hook. You don't have to if you want to pre-populate your page on the server side. Our final implementation should look like: ```vue [Final implementation of the infinite scroll] ``` You can implement this in a component or in a page itself. We implemented it in a table listing on Bugflow. The user initially sees the newest bugs, but can view more as they scroll down. I've also mentioned "infinite" scrolling, but as you can see in the template, I have a button that calls the `loadMore()` method. However, there is a simple VueUse method where you can check if an element, such as an "end of list" element, is visible and then call `loadMore()`. And just like that you have infinite scrolling! Check out [useElementVisibility(](https://vueuse.org/core/useelementvisibility/){rel=""nofollow""}) for more info. ## Helpful Nuxt 3 Data Fetching Hints Here are a few hints that can help you when you make more advanced data fetching requests with Nuxt 3. ### Renaming the Refresh Method in Nuxt 3 As your app grows, you will no doubt hit a time where you will have to do multiple `asyncData()` requests on the same page. Let's look at loading a few companies and cafes on the same page from the ROAST API: ```vue [Standard refresh method] ``` Note: You can also do simultaneous `asyncData()` requests like [I went through here](https://serversideup.net/blog/using-async-data-in-nuxt-3/). Let's just use the above code as an example for now. It will work great right away. But what if you need to actually call the `refresh()` method provided by the composable for each data source. When destructuring `refresh()` from the composable, you will have two methods with the same name. This will not work! To rename the destructured `refresh()` method, simply destructure each as follows: ```vue [Renamed refresh method] ``` Now you can call `refreshCafes()` and `refreshCompanies()` when you need to! ### When to use `refresh()` vs a Watch Source? The simple answer, use `refresh()` when you know data on the server side has changed you need to reload the data on the client side. Use a watch source when the user changes parameters that need to be sent to the server. For example, let's say you have a list of companies. A user deletes a company. This will not change a query parameter, but the data on the server side will change. Run `refresh()` and you will have accurate data. If you want to filter results from the API via a search parameter, set that search parameter as a watch source. When a user changes their query, fresh and accurate data will be re-loaded from the API. Quick note, probably want to use a debounce method from VueUse so you don't query the API on every keystroke or you will hit a throttle limit in no time! ## Conclusion Hope this helps with your advanced data fetching in Nuxt 3! If you have any questions, feel free to hit me up on [Twitter](https://twitter.com/danpastori){rel=""nofollow""} or in our [Discord](https://serversideup.net/discord/)! If you want to see how these pieces fit into the scope of an entire application, [we have a book available](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). With the purchase of the complete package you can see the entire source code behind [ROAST](https://roastandbrew.coffee/){rel=""nofollow""}. # Advanced Meilisearch Queries with Laravel Scout We've touched on a few configurations we can update with Meilisearch to make it more user friendly and powerful. A few that we worked on were setting [sortable](https://serversideup.net/blog/sorting-meilisearch-results-with-laravel-scout-and-eloquent/) and [filterable](https://serversideup.net/blog/filtering-meilisearch-search-results-with-laravel-scout/) attributes on your models. Let's open the gates and show how to use all of the power features of Meilisearch with Laravel Scout! These advanced queries give you full access to the power that Meilisearch provides. You can fine tune your app and make amazing search experiences for your users. Since these are extremely custom to your application, I'll simply show you how to build these advanced queries and you can run wild! ## Prerequisite I'm assuming you have Laravel Scout installed and connected to a Meilisearch instance. Other than that, you are ready to go! ## Why use these advanced queries? When optimizing data for filtering, searching, and sorting, the more optimization the better. Meilisearch provides advanced filtering (when configured, see Filtering with Meilisearch and Laravel Scout), geo queries, etc. Laravel Scout supports some of this fluently, but when you really want to optimize, you will have to interact directly with Meilisearch. One aspect I absolutely love about Laravel is that you have the capability to add these customizations. Yea, it's complicated to understand what you need to achieve sometimes, but Laravel allows you to do that with ease. ## Designing your Advanced Query with Meilisearch Let's say you have a massive customer database with millions of records that's searchable, filterable, order-able, etc. You really want to give the power to the end user to search this data and apply all sorts of filters. Some of these filters could be wild, such as "distance from your headquarters to the customer location" or "customers between the ages of 22 and 43 and have been added in the last 10 days". You want to relate these to an Eloquent model and use Scout natively, so how do you do it? Well, you can pass a call back function to the `search()` method inherited on the model by the `Searchable` trait. Sound like a lot? Let's check it out: ```php [Adding customized filters to Meilisearch Query] $filters = Request::get('filters'); $customers = Customer::search( $term, function( Indexes $meiliSearch, string $query, array $options ) use ( $filters ){ $options['sort'] = [$filters['sort'].':'.$filters['sort_direction']]; $options['filter'] = 'created_at > '.$filter['created_after'].' AND company_id = "'.$filter['company_id'].'"'; return $meiliSearch->search( $query, $options ); } )->get(); ``` Make sure that you add `use MeiliSearch\Endpoints\Indexes;` to the top of your class. This way you can continue to use the advanced features of Meilisearch! ### Breaking Down The Advanced Meilisearch Callback Let's break this down. First, we grab all of the filters configured by the user through the request: ```php [Grab the filters from the request] $filters = Request::get('filters'); ``` Next, we start to build the customer query. Right away, it should look relatively familiar. You call the `search` method on the `Customer` model and pass it a search term. However, the call back function is where things level up a little bit. Let's break that down by itself. The first parameter of the call back function is `Indexes $meilisearch`. This is the actual connection interface to the Meilisearch instance. Here you have direct access through the PHP library to Meilisearch itself. Kind of the key ingredient to doing advanced queries. The second parameter is the `string $query` . The `$query` parameter contains the string the user wants to query (essentially the `$term` variable). You will then pass this along with your overrides to Meilisearch. Finally, you have the `array $options` parameter. This parameter is what contains the options you can pass to Meilisearch to perform advanced queries. Pretty much anything in the [advanced section](https://docs.meilisearch.com/learn/advanced/filtering_and_faceted_search.html){rel=""nofollow""} of the Meilisearch docs can be used here! If you look inside the callback function's body, you see that we set the `sort` key and the `filter` key on the `$options` array. This is where we can construct our complex queries and dynamic searches. Just make sure you have your fields configured to be [sortable](https://serversideup.net/blog/sorting-meilisearch-results-with-laravel-scout-and-eloquent/) and [filterable](https://serversideup.net/blog/filtering-meilisearch-search-results-with-laravel-scout/) within Meilisearch! One thing to note, is the sort key expects an array even if you are just sorting by one attribute. It should look like: ```php [Apply sort filter to Meilisearch] $options['sort'] = [$filters['sort'].':'.$filters['sort_direction']]; ``` Since we are overriding query and injecting our own sorting, advanced filters, etc. we need to return the Meilisearch index and call the `->search()` method. In this method, we need to pass in the `$query` and the `$options` variables. This would be the same if you were using the Meilisearch PHP package itself, outside of Laravel Scout. However, with this approach, we can have the best of both worlds. The power of the PHP package and the fluent approach of Laravel Scout binding to our Eloquent models. Before we move to the next section, we add a `use` statement to the end of the function to ensure our request variables can be accessed from within the callback function. Just a heads up! ### What else can you do? Within this advanced function, you can do complex filters, geo filters, faceted searches, etc. Here are the links to the documentation in Meilisearch for some inspiration: - [Filters & Faceted Search](https://docs.meilisearch.com/learn/advanced/filtering_and_faceted_search.html){rel=""nofollow""} - [Geo Searching](https://docs.meilisearch.com/learn/advanced/geosearch.html){rel=""nofollow""} - [Sorting](https://docs.meilisearch.com/learn/advanced/sorting.html){rel=""nofollow""} A lot of features not supported fluently through Eloquent's query builder, but can be used none the less! This power can really help to ensure your users have an excellent search experience. ## Loading Relationships After Searching One of the other pieces of of functionality I'd like to touch on and give an example of is how to load relationships after a search has been made. This happens after you search your Meilisearch instance, similar to if you are doing an Eloquent query. It's [documented well in the Laravel docs](https://laravel.com/docs/9.x/scout#customizing-the-eloquent-results-query){rel=""nofollow""}, but it took me awhile to grasp it. So maybe another approach will help you as well. Normally, in Eloquent if you'd like to load a relationship you'd add a `->with('invoices')` or something similar to the query builder. You'd have something like this: ```php [Eloquent example using with()] $customers = Customer::where('name', 'LIKE', 'Dan%')->with('invoices')->get(); ``` However, when building a search request to Meilisearch, this won't work. You have to run a `->query()` method and pass it a callback function. As the documentation states, "this callback is invoked after the relevant models have already been retrieved from your application's search engine, the `query`method should not be used for "filtering" results." What that means is don't put a `where` in this function, only use it for adding relationships. So let's take our simple Eloquent query above and convert it to query Meilisearch instead: ```php [Adding with() to a Meilisearch query] $customers = Customer::search('Dan') ->query( function( $query ){ $query->with('invoices'); } )->get(); ``` Now we will search Meilisearch, get all of the results that match "Dan" and load any invoices the customer may have associated with them. ## Conclusion By using some of these advanced features of Meilisearch combined with the fluent nature of Laravel Scout, you can create a super powerful search engine. Your user's experience will benefit dramatically! You can have the best of both worlds with a high speed, full text search powered by Meilisearch. And you can use the beauty of an Eloquent query to relate and return data. Implementing Meilisearch is beneficial in any application, especially with a high availability API. If you want to learn more about building API Driven Applications, [check out our book](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). We are updating it for Laravel 9 and Vue 3! If you have any questions, feel free to reach out on [Twitter](https://twitter.com/danpastori){rel=""nofollow""} or in our [community forum](https://community.serversideup.net){rel=""nofollow""}! # Advanced Vuex 4 Tips So we covered the basics in "[Beginning Vuex 4 with Vue 3](https://serversideup.net/blog/beginning-vuex-4-with-vue-3/)", but if you are building a larger app, you might have already run into some issues. Or maybe you started thinking, there has to be a better way? When you start building state in larger applications, complex forms, or individual pages that could be an app themselves, you will end up wanting to store more and more in the state. There are a ton of advanced Vuex 4 practices that can help make this process even easier. Now that we have gotten our feet wet with Vuex, let's jump into some more of the advanced Vuex 4 use cases and some other awesome features of the state management system! ## Map Helpers The map helpers are some of my favorite features of Vuex. This allows you to essentially short hand access the 4 features of your modules (state, getters, mutations, and actions). Each piece has their own `map` helper. For example, `state` has the `mapState` helper method that you can import. These helpers make managing state easy to do in any component. ### mapState Let's start with the `mapState` helper. To add the `mapState` helper to your component, add the following to your imports: ```javascript [Import mapState helper] import { mapState } from 'vuex'; ``` You now have the `mapState` method available to use within your component. To use this helper method you have to add it to your `computed` properties like this: ```vue [Add computed properties] ``` You now have access directly to your `title` within your component and can access it in the template or in your methods using `this.title`. You will be able to watch for changes locally and you will even be able to use these in your `v-model` assignment. There are a few gotchas with that, so there's a whole different section. ### mapGetters and mapMutations To be honest, I don't use these helpers often since I tend to use `mapState` or treat the piece of state as a model. These two helpers are good to know exist though since any of these map helper functions can come in handy. There are two things to note if you are using either of these helpers. First, you have to include them like `mapState` on top of the component: ```javascript [Import mapGetters helper] import { mapGetters } from 'vuex' ``` and ```javascript [Import mapMutations helper] import { mapMutations } from 'vuex' ``` The next thing to note is if you are using `mapGetters` you use it inside of your `computed` property: ```vue [Add mapGetters to computed property] ``` You also pass an array with the names of the getters you wish to use locally. Then you can call `this.getUser` to access the user. With the `mapMutations` , since these are functions, they map inside of the `methods` property: ```vue [Bring in the mutations to the component] ``` You can then call the method to set the user like this: `this.setUser( user )` and it will perform the mutation to update the user in the store. When we run into using the state as a `v-model` you will see why we don't usually use these helpers much. If you want more information, the Vuex documentation on [mapGetters](https://next.vuex.vuejs.org/guide/getters.html#the-mapgetters-helper){rel=""nofollow""} and [mapMutations](https://next.vuex.vuejs.org/guide/mutations.html#committing-mutations-in-components){rel=""nofollow""} provides a lot more information. ### mapActions To be honest, I use actions sparingly with my Vuex modules. Reason being is that when you map state locally in a component, I usually perform the methods I need within the component instead of the action. With that being said if I need to perform the same method multiple times from multiple components, I will create an action and then map the action locally. In the [Beginning Vuex 4 with Vue 3](https://serversideup.net/blog/beginning-vuex-4-with-vue-3/) I mentioned this with a `next()` method on a playlist. This could be called anywhere from multiple components so creating an action would be helpful. Instead of creating a `next()` method locally in each component and calling `this.next()`: ```javascript [Next method with dispatching to Vuex] export default { methods: { next(){ this.$store.dispatch('nextSong'); } } } ``` We can use `mapActions` helper to do that for us. First, we can import the `mapActions` helper: ```javascript [Import the mapActions helper] import { mapActions } from 'vuex' ``` Next, we can add it to our `methods`: ```vue [Bring in the next method from the Vuex actions] ``` Now we can call `this.next()` as much as we want within our components and we don't have to repeat code! ### Using Multiple Map Helpers We will touch on namespacing in a few sections, but one feature I'd like to touch on before we leave our discussion of map helpers is mapping multiple pieces of state in a component. Say you have a piece of state that is a user and some settings that you want to map in a single component. ```vue [Multiple map helpers] ``` You now can access `this.user` and `this.title` and they both map to their appropriate state. I've definitely run into multiple scenarios where I've had to map multiple pieces of state in a component. Especially when working with a large form where pieces are namespaced. The process is similar for all map helpers, so `actions`, `getters` and `mutations` are the same. Before we get into namespacing and addressing the issues such as possible naming conflicts, let's touch on using Vuex state as a `v-model`. ## Using Vuex State as a v-model So now we know we have all of these amazing helpers available for our use, let's go one step further and use the mapped state as a `v-model` on a form input. Quick tangent. I've built some massive forms that require tons of dynamic computation even before persisting to the database (ie. Entering an address and when every field is validated, grabbing latitude and longitude from Google). When creating these massive forms I ended up with a page or a component that was well over 2000 lines of code. This is unmaintainable. I then divided the form into small, maintainable components. These Vue components were not meant to be re-usable but scoped simply on the form page. That brought us to the next issue. Passing all of the properties for the form down to each component and watching for changes. This became a headache instantly. Luckily, Vuex state is the perfect tool for the job. When dealing with a large form, I actually make a piece of state that is monitored by all the shared components that make up the form. This way I can easily pass data between the components, have small maintainable form pieces, and handle massively complex computations. With that being said, using your mapped state as a `v-model` is extremely important. So with mapped state, you can easily reference the state within components. Maybe this for display or to use in calculation. But there are also times where you want to harness state reactively through a `v-model`. Let's say you have a piece of state that's a `title` and you want to bind it to an input field: ```html [Set up v-model on input] ``` This title is shared across multiple components through state. To use this as a `v-model` you will have to set up your `computed` property on the component to look like: ```vue [Commit and get data from Vuex for our v-model] ``` What you are doing is explicitly defining a two-way binding that allows you to load the title through a `getter` and also call the `setTitle` mutation when the value updates. You can now use your Vuex state within your `v-model` and most importantly, spread out massive forms across multiple components! ## Namespacing Vuex Modules Namespacing Vuex modules is extremely important as your app grows. What namespacing allows you to do is neatly nest modules and access properties within the scope of a name. When you get into larger apps, you could have duplicate state names which would conflict and cause a nightmare to maintain. For example, say you are managing media and you have a piece of state named `title` and a mutation called `setTitle`. Is this title for a movie, book, or song? You can't really decipher that. You also don't want to have a million pieces of top level state where you have getters like `getBookTitle()` and `getVideoTitle()` . Your module will become overwhelmingly large. Namespacing solves this problem. To namespace a Vuex module you have to add the `namespaced` option set to `true` to the top of your module: ```javascript [Example of a namespaced Vuex module] export const book = { namespaced: true, state: () => ({ title: '' }), mutations: { setTitle( state, text ){ state.title = text; } }, getters: { getTitle( state ){ return state.title; } } } ``` Now, if you have two modules that are namespaced you can register them in your store: ```javascript [Two namespaced modules loaded into a Vuex store] import { book } from './Modules/book.js'; import { movie } from './Modules/movie.js'; const store = createStore({ modules: { book, movie } }); ``` When it comes time to access the title of the module you want you can call: ```javascript [Example of a getter with the book module] this.$store.getters['book/getTitle']; ``` or ```javascript [Example of a getter with the movie module] this.$store.getters['movie/getTitle']; ``` And get the title you want! Same goes with mutations and actions. You reference the specific feature through a namespace (similar to a file structure) reference. The namespace is the exported function `movie` is the name of the namespace. You can even do this with the map helpers if you pass the namespace as a first parameter: ```vue [Namespaced computed variable] ``` The first argument is the namespace (in this case `book`) and then you can begin your mapping. You can even map multiple namespaces in the same component: ```vue [Multiple namespaced modules in a component] ``` Now `this.bookTitle` is mapped to the book title state and `this.movieTitle` is mapped to the movie title state. Quick note, you can assign custom names locally and map it to state of a different name (ie. `bookTitle` ≠ `title`). This flexibility comes in handy as your app grows. In the next section we will be getting into some seriously large scale mapping and namespacing. ## Nesting Namespaced Vuex Modules To cap this tutorial off, let's get into nested namespacing with Vuex modules. Let's say we have an ultra large app or a page that has a ton of settings. Your Vuex module is getting un-maintainable (well over 1000 lines of code... it happens). You will need to split it up. To keep structure, the first thing you want to try is namespacing. But sometimes that might not be enough on its own. You can move to nested namespaced Vuex modules! What this means, is say you are dealing with a settings form and each setting belongs to a group. We did this with [AirStudio](https://airstudio.video){rel=""nofollow""} editor. You can nest a namespace to make your modules extremely easy to maintain. What I mean by that is say you have a `settings` module. In your `settings` module you have settings for a `canvas` which has a background color, width, height. You also have settings for a `title` which contains a font, color, size, and weight. These are small settings that are used throughout the massive editor page. If you put them all into a single `editor` namespace, you will end up with an un-maintainable editor module that every time you want to add a setting it becomes a chore to maintain. Also assume there are other modules such as video, user, etc. that live on the same level as the `editor` module. To make these maintainable, you divide up the module into sub modules. For example, I have an `editor` module, that has a sub module of `canvas` and `title` . Now within each of these modules, I store the localized state. Let's take a look. First, I register my `editor` module with the store: ```javascript [Import editor module to store] import { editor } from './Store/editor.js'; const store = createStore({ modules: { editor } }) ``` Notice that I didn't include the `canvas` or `title` sub modules? That's alright, those are coming soon. Next, I create my `editor` module: ```javascript [Import sub module to editor module] import { title } from './editor/title.js'; import { canvas } from './editor/canvas.js'; export const editor = { namespaced: true, modules: { title, canvas } } ``` This is where the magic begins to happen! I then have sub modules which we will show next, that are imported into my `editor` module. I also like to organize these modules in a directory based on their parent modules. In this case, I have an `editor` directory that contains the `title` and `canvas` modules. I then namespace the `editor` module, but then I register the sub modules in the `modules` property. Now I have a nested module that keeps our code nice and clean! So our `title` module can look like this: ```javascript [Title module] export const title = { namespaced: true, state: () => ({ font: '', color: '', size: '', weight: '' }), mutations: { setFont( state, font ){ state.font = font; } }, getters: { getFont( state ){ return state.font; } } } ``` and our `canvas` module: ```javascript [Canvas Module] export const canvas = { namespaced: true, state: () => ({ color: '', width: '', height: '' }), mutations: { setColor( state, color ){ state.color = color; } }, getters: { getColor( state ){ return state.color; } } } ``` It's important to note the `namespaced: true` on top of all of these modules. There are so many benefits to this. First, each module is nice and tidy and easily maintained. Second, they are nested within the main `editor` module so you can scope which properties need to be used. To map a nested module's state, simply extend the namespace to look like: ```javascript [Map nested module state] import { mapState } from 'vuex'; export default { computed: { ...mapState('editor/title', { font: state => state.font } } } ``` The same goes for all of the other mapped helper functions. Now you have a nice clean way to manage the state you need. If you are not mapping state and need to dispatch or commit an action, you can access the nested and namespaced module like this: ```javascript [Dispatch changes to nested module] this.$store.commit('editor/title/setColor', color); ``` I hope this helps shed some light on the power of Vuex and what it can do! It's extremely powerful for organizing shared data across components or pages in an SPA. If you have any questions, by all means reach out, I love discussing Vue and Vuex! # AmplitudeJS Configuration Options Part of what makes AmplitudeJS so powerful is all of the configuration options available. Most of these options have an associated public method that allows you to set or get the state of the option as well. When building a player, you will be initially setting these options when you first run the `Amplitude.init()` method. All of the available options are located in the documentation here: [AmplitudeJS Configuration Options](https://serversideup.net/open-source/amplitudejs/docs/){rel=""nofollow""}. In this tutorial we will be diving into more of the abstract, maybe not so straight forward options that AmplitudeJS provides. ## Setting Playback Speed Sometimes users want to control the speed of playback for a piece of audio. This is especially prevalent with podcasts or audio that can be very long. You might want to speed it up a bit. To do this, you can set the default playback speed for your player to be either `1.0`, `1.5`, or `2.0`. `1.0` is the default speed, `2.0` is twice as fast and `1.5` is right in the middle. To set the default speed, initialize AmplitudeJS with the following key: ```javascript [Initialize AmplitudeJS] Amplitude.init({ songs: [], playback_speed: 1.5 }); ``` You can get the playback speed through a public method with `Amplitude.getPlaybackSpeed()` or allow the user to switch between the options with an interactive element of ``. ## Volume Increment and Decrement AmplitudeJS allows for the user to increment and decrement the volume of their player through an interaction (touch or click) on an element. The exception being iOS of course. When you configure AmplitudeJS, you can set how much the volume should increment or decrement each time the element is clicked. The range the volume can be at is between 1 and 100. By default every time you click a volume up element (``) or volume down element (``) the volume gets adjusted +5 or -5. To change this value, for the respective function, configure AmplitudeJS like this: ```javascript [Set volume increment and decrement] Amplitude.init({ songs: [], volume_increment: 10, volume_decrement: 15 }); ``` The volume will now increment by 10 every time you increase the volume and decrement by 15 every time you decrease the volume. ## Key Bindings This is one of the newer features of AmplitudeJS and one of the more interesting features. You can actually bind functionality to key presses on the keyboard. Any key can be bound to one of the 6 available events: 1. `play_pause` 2. `next` 3. `prev` 4. `stop` 5. `shuffle` 6. `repeat` You will just need to know the key code of the key you want to bind to the event. The simplest way to do that is to visit: [JavaScript Event KeyCodes](http://keycode.info/){rel=""nofollow""}. You can then just press a key and find the code! Say you want to bind `play_pause` button to the `p` key. You would initialize AmplitudeJS like this: ```javascript [Set key bindings] Amplitude.init({ songs: [], "bindings": { 80: 'play_pause' }, }); ``` Now whenever you press the `p` button on the page, AmplitudeJS will toggle the play pause! The functionality is the same and you can combine events to key code in any way you want. It always goes in the same format of: ```javascript [Keybinding layout] "bindings": { {KEY_CODE}: {FUNCTION} } ``` You now have an enhanced UX option for your player! ## Callbacks When you configure AmplitudeJS, you can add an array of callbacks at certain events. These callbacks are methods that you can hook into the functionality of AmplitudeJS and run at certain events. Below is a list of the possible events to choose from: - before\_play - after\_play - before\_stop - after\_stop - time\_update - album\_change - song\_change - time\_updated - playlist\_changed - song\_repeated If you need more information on what each of these callbacks does, check out the docs here: [AmplitudeJS Callbacks](https://serversideup.net/open-source/amplitudejs/docs/){rel=""nofollow""}. Let's say you want to track how many times your users interact with a certain track. Every time they play, you can add a callback to the `before_play` hook that increments a variable (or calls a remote server to keep track). You'd initialize your callbacks like this when you init AmplitudeJS: ```javascript [Set up callback methods] var playCount = 0; Amplitude.init({ "songs": [], "callbacks": { 'before_play': function(){ playCount++; } } }); ``` Of course, these callback can be as complex as needed. You can also call AmplitudeJS public methods inside to get more information about the state of the player. The callback is always configured like: ```javascript [Callback configuration] {callback_key}: function(){ } ``` ## Continue Next This is one of the last weird configurations we will go through. It's the `continue_next` configuration. What this does is when the audio has finished, it determines if AmplitudeJS should continue to the next song or not. Sometimes, you might not want the player to automatically go to the next audio. When the song is finished, you just want the player to stop. This would be used in a scenario where you configure multiple individual players on a page where they aren't all connected. You can set the `continue_next` configuration to be `false` and when the song has ended, the player stops. To do that, set the configuration like this: ```javascript [Continue next example] Amplitude.init({ songs: [], continue_next: false }); ``` So that's an over view of how to configure AmplitudeJS. Any of the configuration variables found in the documentation are handled the same way. You'd set them in the init method. If you have any questions about some of the configuration variables, feel free to head over to our [Discord channel](https://serversideup.net/discord/) and I can lend a hand! # AmplitudeJS for Live Stream HTML5 Audio > **Update 5-25-2017** We've made it even easier for you to style HTML5 audio elements. Amplitude 3 is now available! Download it on [GitHub](https://github.com/serversideup/amplitudejs "Amplitude.js Github"){rel=""nofollow""}. Check out the [Amplitude site for latest documentation](https://521dimensions.com/open-source/amplitudejs){rel=""nofollow""} and a to see the latest features. A unique issue presented itself while reviewing some of the comments in the article: [/blog/style-the-html-5-audio-element/](https://serversideup.net/blog/style-the-html-5-audio-element/). One of the questions was how to use the audio tag for live streams? The issue wasn't necessarily playing the music, but getting the music to stop downloading when the user had the music paused (to save bandwidth) and to play from where the current point of the stream was at rather than starting from where the user paused the stream. With the latest release of AmplitudeJS, you can now manage live stream HTML5 audio sources and give you full control of the UI elements of the audio player. For more background on how to use AmplitudeJS, [here is the original article](https://serversideup.net/blog/customize-html-audio-css-amplitudejs/ "AmplitudeJS"). The hardest part was disconnecting the live stream so it would stop downloading while the user had the stream paused. Thanks to {rel=""nofollow""} for providing some insight on how to disconnect from the source. I provided code to reconnect when the user wishes to play the stream again, which automatically picks up from where the stream is currently at on the server. ## Step 1: Include Amplitude.JS in the head ```html ``` Now we have all of the features to set up for a live stream ## Step 2: Set up your live stream player **WARNING:** Live stream will not work with playlists! It also has an undefined endpoint, so having a track status and ending time would make no sense. We need to add our audio tag: ```html [Add your audio tag] ``` We then need to add our Play/Pause like we would for a single song. ```html [Add a Play/Pause button]
``` NOTE: You can style the all of the elements like you would with a normal AmplitudeJS player ## Step 3: Edit your amplitude\_config variable This is the most important part. You will set AmplitudeJS up to realize that it is a live stream. To do this, add the following Javascript: ```html [Initialize AmplitudeJS] ``` For more information on the AmplitudeJS config variable, see: [Config](https://serversideup.net/blog/customize-html-audio-css-amplitudejs/) What happens in the background is when you click play, it loads up the live stream url and binds it to the audio tag. This will do nothing on the first play because the source is already defined in the audio tag. Your stream will start playing. When you click pause, this is where the magic happens. The stream is disconnected and set to null, canceling the download of the live stream and making sure when the user clicks play again, it picks up where the stream is currently, NOT where the user left off. ### Initial Play ![](https://serversideup.net/blog/amplitudejs-live-stream-html5-audio/initialPlay.png) ### Pause ![](https://serversideup.net/blog/amplitudejs-live-stream-html5-audio/pause.png) ### Resume ![](https://serversideup.net/blog/amplitudejs-live-stream-html5-audio/resume.png) This is what we have for HTML and CSS ```css [Style the player] /* Player Styles */ #player{ width: 334px; margin: auto; box-shadow: 1px 5px 5px #888888; } #player-top{ padding: 10px; height: 55px; background-color: white; } #slash{ color: #3b3b3b; font-size: 12px; font-weight: bold; text-shadow: 1px 1px #ffffff; font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; margin-top: 20px; } #track-info-container{ float: left; font-size: 10px; width: 170px; overflow: hidden; margin-left: 10px; } #time-info-container{ float: left; margin-top: 15px; font-size: 10px; line-height: 19px; } /* Amplitude Element Styles */ #amplitude-play-pause{ width: 58px; height: 59px; cursor: pointer; float: left; } .amplitude-paused{ background-image: url('../images/yellow-play.png'); background-repeat: no-repeat; } .amplitude-playing{ background-image: url('../images/yellow-pause.png'); background-repeat: no-repeat; } #amplitude-now-playing-artist{ font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; font-size: 14px; } #amplitude-now-playing-title{ color: #3b3b3b; font-size: 12px; font-weight: bold; text-shadow: 1px 1px #ffffff; font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; margin-top: 20px; } #amplitude-current-time{ font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; } #amplitude-audio-duration{ font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; } #amplitude-song-slider{ display: inline-block; height: 10px; border-radius: 5px; width: 220px; background-color: rgba(237,237,237,.8); margin-left: 10px; margin-top: 5px; } #amplitude-track-progress{ background-color: #fad52c; height: 10px; border-radius: 5px; width: 0px; } ``` ```html [Full example of a styled player] Amplitude Live
``` This is very beta functionality, so please ask questions, and leave comments for improvement in our [Discord](https://serversideup.net/discord/). # Animista CSS Animations with VueJS Transitions Every once in a while I run across some tools that I can't develop without. VueJS ([Vue.js](https://vuejs.org/){rel=""nofollow""}) is definitely hands down the best front end Javascript framework out there. It's easy to use, extend, and customize to what you need it to do. The front end of this tutorial series is written in VueJS and at [521 Dimensions](https://521dimensions.com){rel=""nofollow""} and we use VueJS daily in our app development process. One of the most awesome feature of Vue is their transitions. It makes animating a process a breeze! For a good front end framework to really show it's power, it has to not only function well, but also look good. This requires knowledge of CSS and really tricky CSS such as animations. Animations add that extra UX flare to a project that make it stand out above the rest. I myself am not too great with CSS. I write it, it works, but some of the features I just feel like I'm missing something. This is especially true when building animations in CSS. That's where the tool of Animista ([Animista](http://animista.net/){rel=""nofollow""}) comes in. It's built by \[Ana Travas]\([Ana Travas (@ana108) | Twitter](https://twitter.com/ana108){rel=""nofollow""} and is a set of on-demand animations. You essentially can test CSS animations, customize them, then download the keyframes and CSS classes needed for your application. It's simply amazing! The best features about VueJS and Animista? They work extremely well together. VueJS has awesome transitions functionality built right into the framework: [Enter/Leave & List Transitions — Vue.js](https://vuejs.org/v2/guide/transitions.html){rel=""nofollow""}. You can take this functionality and extend it easily to use animations from Animista. Let's get started! ## What We Will Build For the sake of this tutorial, we will build a slide out menu similar to what is found on [Roast](https://roastandbrew.coffee/#/cafes){rel=""nofollow""} when the 'Filters' button is clicked. It's a great example and is very easy to implement in your own application. ## Step 1: Build Slide Out Menu Component The first step is to build our slide out menu component. This will be a Vue Component ([Components Basics — Vue.js](https://vuejs.org/v2/guide/components.html){rel=""nofollow""} that slides out from the left side of the screen. The initial component will look like: ```vue [Layout for our initial component] ``` First thing to note, I'm using SASS for this tutorial. You don't have to use SASS you can use straight up CSS if that is what works for your project. In the styles, I have this as a fixed element that maxes out at 550px. I have the width set to 100% so on a mobile device, it goes full screen. Next, I have the component shown off of a data variable named 'showFilters'. This is a boolean set to true or false. If it is true, then the component will be shown, if it is false, then the component will be hidden. We will want this to animate, and I will explain how shortly. Lastly, I have the shown variable updated by an event. I created an event bus per this guide for Vue 2.0: [Migration from Vuex 0.6.x to 1.0 — Vue.js](https://vuejs.org/v2/guide/migration-vuex.html#Store%E2%80%99s-Event-Emitter-removed){rel=""nofollow""}. When shown or hidden, the event bus will fire and we will show or hide the filters. You can do this functionality in a variety of ways, you just need a way to toggle whether or not the filter is shown or hidden. ## Step 2: Download Slide-In-Left animation from Animista.net If you visit [Animista](http://animista.net){rel=""nofollow""} you will see there are TONS of animations. You can animate in so many different ways. In this use case, we just want a simple slide in left animation from our menu. To download that animation, visit: [Animista Slide In Left](http://animista.net/play/entrances/slide-in/slide-in-left){rel=""nofollow""}. Here you can customize duration, delay, timing function, etc. It's so quick to use! Once you have it customized to your heart's content, you can now download the CSS classes and keyframes. To do that, click the 'Generate Code' brackets icon in the top right of the animation window. You will see two chunks of code you need to copy. The first one will be the class. This is highly important as it's what Vue uses to identify the animation to use. In our use case, it should look like: ```css [Slide in Left Animcation] .slide-in-left { -webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; } ``` The next chunk of code is equally important and is key frames for the `slide-in-left` class: ```css [Keyframes for animation] /* ---------------------------------------------- * Generated by Animista on 2018-5-16 13:50:1 * w: http://animista.net, t: @cssanimista * ---------------------------------------------- */ /** * ---------------------------------------- * animation slide-in-left * ---------------------------------------- */ @-webkit-keyframes slide-in-left { 0% { -webkit-transform: translateX(-1000px); transform: translateX(-1000px); opacity: 0; } 100% { -webkit-transform: translateX(0); transform: translateX(0); opacity: 1; } } @keyframes slide-in-left { 0% { -webkit-transform: translateX(-1000px); transform: translateX(-1000px); opacity: 0; } 100% { -webkit-transform: translateX(0); transform: translateX(0); opacity: 1; } } ``` There are a couple things you can do with this code. 1. You could place it all in your Vue Component. This is useful if you have an animation that is ONLY being used within that component. However, most of the time animations are re-usable. 2. If you are using standard CSS, copy both of these chunks of code to your file. You will be able to reference them as needed. 3. If you are using SASS, like in this example, I created an `animations` folder and placed the css and keyframes in a file named: `_slide-in-left.scss`. This gets compiled in my main SASS file and can be reused throughout components. Now that you have your animations, let's make that slide out navigation work! ## Step 3: Wrap Vue Component in `` Tag So to make sure Vue adds the proper animations and reverses animations when needed, you need to wrap your entire Vue component in a `` tag. This allows you to utilize your CSS animation on a component. Now Vue will automatically add the classes necessary to get your component to animate. This is actually a super slick feature of Vue transitions. You can animate entrances and exits and Vue will take care of it for you! For more information on transition statuses check out: [Enter/Leave & List Transitions — Vue.js](https://vuejs.org/v2/guide/transitions.html#Transition-Classes){rel=""nofollow""}. Our component should now look like: ```vue [Animations applied to Vue Component] ``` The big thing to note, besides the entire component in a `` tag is the `name` attribute on the `` tag. This is huge. This references the class that will be used to do the animation. In this case it is set to `slide-in-left`. If you see in Step 2, the CSS class was `slide-in-left`. This is how Vue knows which transition to use. There's really only one more step and we are ready done! ## Step 4: Adjust CSS Classes for Animation to work with VueJS This is really the meat of getting everything working together. I mentioned earlier that Vue adds custom transition statuses to the element to make sure it is firing correctly. To make use of these open the file that contains your animation and find the class. Update your class to be: ```css [Vue class updates to animations] .slide-in-left-enter-active { -webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; } ``` VueJS will add the `enter-active` suffix to your class when you are starting to run the transition. This gets fired when your variable that determines whether or not to show the navigation. Next, after that class add the following class: ```css [Vue leave updates to animations] .slide-in-left-leave-active{ -webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both reverse; animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both reverse; } ``` What this does is the same animation when the navigation is transitioning to hidden. Looks exactly the same EXCEPT for the `reverse` style on the property. This is so the animation runs in reverse when hidden! Slick isn't it? Now that you have these properties defined, and your component wrapped in a transition tag, you are ready to use your animation! Our final CSS should look like: ```css [Final CSS for animation] .slide-in-left-enter-active { -webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; } .slide-in-left-leave-active{ -webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both reverse; animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both reverse; } /* ---------------------------------------------- * Generated by Animista on 2018-4-12 13:38:23 * w: http://animista.net, t: @cssanimista * ---------------------------------------------- */ /** * ---------------------------------------- * animation slide-in-left * ---------------------------------------- */ @-webkit-keyframes slide-in-left { 0% { -webkit-transform: translateX(-1000px); transform: translateX(-1000px); opacity: 0; } 100% { -webkit-transform: translateX(0); transform: translateX(0); opacity: 1; } } @keyframes slide-in-left { 0% { -webkit-transform: translateX(-1000px); transform: translateX(-1000px); opacity: 0; } 100% { -webkit-transform: translateX(0); transform: translateX(0); opacity: 1; } } ``` And our Vue Component should look like: ```vue [Final Vue component with animations] ``` ## Conclusion The combination of VueJS and Animista helps make slick front end development a breeze! You can add multiple animations to a project to be used in a variety of scenarios and just map the animated/transitioning object in a transition tag that references the class name of your transition. Bam! UX transition ready to rock! Just make sure you add the corresponding exit class and suffix of `leave-active` IF that's what you want to use. To check out how I've used a few more transitions, and this transition in Roast, visit: [Roast and Brew Github](https://github.com/serversideup/roastandbrew){rel=""nofollow""}. Of course, ask any questions in the comments below! If you want to learn more and take a deeper dive into API Driven Development, sign up for the mailing list: [API Driven Development Mailing List](https://serversideup.net/subscribe/). We will be launching exclusive intro offer soon! # API Driven Form Submissions with Javascript, Vuex and Laravel We are now at the point where we can begin adding some functionality to our application. In most cases, this requires some sort of data to compute or display. To do that, we need to add a way to add data to our application. Since we are building an application to help coffee enthusiasts find their next cup of coffee, we should begin by allowing *authenticated* users to submit coffee shops to our application. Whenever you are working with a form in the sense of a Single Page Application, everything has to work together from the Laravel API to the Vuex module calling the Javascript route and updating the Vuex data store. There's lots of moving parts, but if you break it down, it's not that bad. ## Step 1: Revisit What We Already Have We already have a route set up in our API to add a cafe that requires the following parameters: - Name - Address - City - State - Zip These are the most basic of parameters for a cafe, we will be adding a lot more later! We also already have a Vue Router route set up for adding a cafe which is `/cafes/new` and a template for adding a cafe in the `/resources/assets/js/pages/NewCafe.vue` file. Since we have been prepared for this, we also have a Javascript API call set up in the `/resources/assets/js/api/cafe.js` file that accepts the 5 parameters to submit to a cafe. We still have a few things we need to do and those are: 1. Add a form to the NewCafe.vue file that can be submitted with the new cafe. 2. Send an action request to our Cafes module that we will be submitting a new cafe. 3. Submit the new cafe through our Javascript API 4. Return either success or failure back to our front end, specifically the Vuex module. 5. Reload the cafes and update our Vuex module. This is the basic flow for adding *ANY* content to our database with an API Driven perspective and a single page application with VueJS/Vuex. Let's get started! ## Step 1: Add a Form to the NewCafe.vue Page First we should open our `/resources/assets/js/pages/NewCafe.vue` file. We have nothing in this page right now, but in the template of the file, we will be adding our form. Our form right away should be very simple and just have some empty text fields that match the data we need to collect for our new cafe: ```html [HTML Layout for our form]
``` You will notice a few things: 1. The grid layout is Zurb Foundation 6.4 XY-Grid and some of the styles are already pre-defined. To read more about the grid layout, check out the docs here: [Forms | Foundation for Sites 6 Docs](https://foundation.zurb.com/sites/docs/forms.html){rel=""nofollow""} 2. I added a page container `
` with a class of `page`. I defined this in the the `/resources/assets/sass/layouts/_page.scss` file and just gave the page a margin top of 25px so we have some breathing room. Following the 7-1 pattern, layouts would be the best place to put this component. We can also re-use this across all of our pages. Now we need to set up our models in our page. First we need to add a `data()` function to our page component and have it return an object like this: ```javascript [Set up data() in Vue Component] data(){ return { } } ``` This will return the variables we will use for our models in our form. We now need to add these variables in our data object: ```javascript [Define our form variables] data(){ return { name: '', address: '', city: '', state: '', zip: '' } } ``` Now we need to add the `v-model` parameter to each of our inputs and bind the data to one of the variables in the object being returned by our component. What we are doing is binding the value of each of our inputs to a piece of data in our page model. Vue takes care of all of the updates when we type into an input. We can also access these variables inside of our component and do actions like pass them to actions to save to the database. For more information visit: [Components — Vue.js](https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function){rel=""nofollow""} for more on data and for the `v-model` visit: [Components — Vue.js](https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events){rel=""nofollow""} Our page should now look like: ```vue [Current state of our Vue Page] ``` We have a very basic form where each input is bound to a piece of data. For fun you can use the Vue inspector in your browser and see each model update as you type. You just have to find the page component and you will see the data you added: ![](https://serversideup.net/blog/api-form-submissions-javascript-vuex-laravel/screenshot-1024x859.jpg) Finally, we will have to add a simple submit button and create a method in our page component to handle a click. This is how we will kick off our form submission. Right after our `zip` field row, I added: ```html [Added a submit button]
``` You will notice a component `v-on:click="submitNewCafe()"`. What this is doing is binding an event handler on our button to a method in our Vue component. We need to add the `submitNewCafe()` method to our methods object like this: ```javascript [Define our method to submit the cafe] methods: { submitNewCafe(){ } } ``` Now whenever a user clicks on the `Add Cafe` button, we will call the method to submit a new cafe. ## Step 2: Send Action To Submit New Cafe We are now ready to submit an action to our Vuex module saying "hey, we want to add this new cafe!" To do that, we will stay in our `NewCafe.vue` file and work with the `submitNewCafe()` method. Since we are calling an action on a Vuex module, we need to dispatch an `addCafe` method which we will implement on our module in the next step. The first parameter of the store dispatch method is the name of the action being dispatched. The second parameter is a JSON object containing the data we want to send in the action. In this case, it will be the value of our inputs. The `submitNewCafe()` method should look like: ```javascript [Method to submit our new cafe] submitNewCafe(){ this.$store.dispatch( 'addCafe', { name: this.name, address: this.address, city: this.city, state: this.state, zip: this.zip }); } ``` If you are new to VueJS, the `this.` references the local component's data value. So what we are doing is dispatching an `addCafe`method that will be received by our cafes Vuex module containing all of the data from our form. ## Step 3: Handle addCafe action in cafes Vuex Module So now we have the action being dispatched, but nothing to catch the action so that's what we are doing now. We will need to open our `/resources/assets/js/modules/cafes.js` file to edit the module. First, we need to add a variable to our state which will keep track of our cafe add status. We should simply add: `cafeAddStatus: 0` at the end of our state which will initialize our variable. Our state should look like: ```javascript [State in our Vuex Module to handle submission] /* Defines the state being monitored for the module. */ state: { cafes: [], cafesLoadStatus: 0, cafe: {}, cafeLoadStatus: 0, cafeAddStatus: 0 }, ``` Now we should add our method to handle the addition of the cafe. Right after we defined our `loadCafe` method, we should add the following method in the actions object: ```javascript [Vuex action to add a cafe] /* Adds a cafe */ addCafe( { commit, state, dispatch }, data ){ } ``` One thing to notice about this method's signature is that we have an extra piece in the decoupled data in the first parameter, it's `dispatch`. What this does is allow us to dispatch actions from within our Vuex module which we will be doing to re-load our Cafes after the cafe has been added. The second parameter, `data`, will be our form data object. If you remember in the tutorial /build-api-requests-javascript/ we already added the API request in our Cafes.js API. We will use this very similar to the way we've made other API requests to load the cafes. In our `addCafe()` method, in our Vuex module, we should add the following code: ```javascript [Full Vuex action to submit a cafe] /* Adds a cafe */ addCafe( { commit, state, dispatch }, data ){ commit( 'setCafeAddedStatus', 1 ); CafeAPI.postAddNewCafe( data.name, data.address, data.city, data.state, data.zip ) .then( function( response ){ commit( 'setCafeAddedStatus', 2 ); dispatch( 'loadCafes' ); }) .catch( function(){ commit( 'setCafeAddedStatus', 3 ); }); } ``` There are a few things to note: 1. We haven't added the `setCafeAddedStatus` mutation yet, we will do that next! 2. When successful, we will be setting the `cafeAddedStatus` to 2 which we can use to display the successful addition of the cafe. 3. When successful, we also dispatch the `loadCafes` action which will reload the cafes containing the one we just added. 4. On failure, we flag the `cafeAddStatus` to 3 which we can use to display an error. A few more things to add our Vuex Module. We need to add the mutation `setCafeAddedStatus` at the end of our mutations object: ```javascript [Mutation that sets the status of adding a cafe] /* Set the cafe add status */ setCafeAddedStatus( state, status ){ state.cafeAddStatus = status; } ``` This will set the `cafeAddStatus` accordingly to what we have as a state in our app. The last thing to add is a getter to get the add state of the cafe: ```javascript [Getter to access the adding of a cafe's status] /* Gets the cafe add status */ getCafeAddStatus( state ){ return state.cafeAddStatus; } ``` We can now use this to show data updates in our app. Our Vuex module is ready to rock. ## Step 4: Review The Laravel Process and Build App In this tutorial: [Add Laravel API End Points - Server Side Up](https://serversideup.net/blog/add-api-end-points-laravel/) we already built the endpoint to add a cafe. which is `POST /api/v1/cafes`.We just completed the front end side that calls the appropriate route. Upon completion, we will have a cafe in the database and this route will return the status. Let's give it a try! Make sure you run your `npm build dev` or `production` if you want! So I added Ruby Coffee Roasters in Nelsonville WI. Side note on Ruby Coffee, if you haven't tried them yet, try them! Small town in central Wisconsin that does an amazing subscription service and sells locally and also in Long Beach, CA . Check em out here: [Ruby Coffee Roasters | Colorful Coffees roasted in Central Wisconsin, USA.](https://rubycoffeeroasters.com/){rel=""nofollow""} ![](https://serversideup.net/blog/api-form-submissions-javascript-vuex-laravel/one-1024x813.jpg) You can see on the response back I got all of the cafes this means that our cafe was added successfully and Vuex called the action to load the cafes again. ![](https://serversideup.net/blog/api-form-submissions-javascript-vuex-laravel/two-1024x273.jpg) I confirmed this by checking out our console and then navigating back to the home page to see it in the list: ![](https://serversideup.net/blog/api-form-submissions-javascript-vuex-laravel/three-1024x814.jpg) This is exciting! We now have a way to add cafes to our application! And even better, everything automatically stays in sync with Vue and Laravel as it's all reactive with Vuex with minimal API requests! ## Conclusion This is the first of many functionality tutorials. It's awesome to see the application start to come together and personally I can't wait to start using it! Any styles or little designs will be mentioned in tutorials and available to be viewed here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""} The next tutorial we will add a few finishing touches to our API Request and then focus on some awesome enhancements for displaying the cafes.. I'm thinking maps could be helpful! # Append Gravatar Attribute to the Laravel Eloquent User Model There are so many useful tricks when it comes with working with Laravel Eloquent. This is one of my favorites. It allows you to simply make a computed attribute on the User model that returns the user's Gravatar URL. Best part? It's only a few lines of code. Let's jump in! ## Step 1: Open Your User Model First, you need to open your user model. In Laravel 8 this should be in the `app/Models` directory. ## Step 2: Append Gravatar Attribute All you need to do now is append the attribute for the computed Gravatar URL: ```php [Eloquent model Gravatar attribute] public function getGravatarAttribute() { return ''.md5( strtolower( trim( $this->email ) ) ); } ``` What this does is uses the Eloquent syntax to create an attribute. When returned, the field `gravatar` will be populated with the URL of the user's gravatar if they have one. Gravatar returns a default icon if the URL doesn't map to a user. This is great so you always have an empty state in place when it comes to implementing this in your app. You can now reference this value from within Laravel and in Blade templates as: ```php [Access the Gravatar on the User Model] $user->gravatar; ``` However, if you are not using Blade templates or loading your User through an API, see below. ## Step 3: (OPTIONAL) Append Gravatar Value to Array The reason this is optional is it only matters if you are loading your User resource through an API or some serialized format. If you are using Laravel blade, you won't be interacting much with your User Model in a serialized form (JSON or Array), so you can use the attribute through: ```php [Access the Gravatar on the User Model] $user->gravatar; ``` However, since it's a computed attribute, you will have to add this to your `$appends` array on your model. When your resource is loaded through an API, the `gravatar` field will appear in the JSON for that user. All you have to do is add the following to your User Model: ```php [Append the gravatar to all JSON models] protected $appends = [ 'gravatar' ]; ``` If the `$appends` array already exists, just add `gravatar`. Now you can access `user.gravatar` when returned in JSON or if you convert your model to an array within your Laravel application. ## Conclusion I love these computed attributes and how simple they are to add useful functionality to your app. The reason I choose to do a computed Gravatar URL instead of saving it to the database is there really isn't any need to save the URL since it's easily computed and keeps our database tidy. It also stays up-to-date if the user changes their email. If you have any thoughts or questions, feel free to reach out in the comment section below! # Automatic Controller Assignment for UniFi DHCP Option 43 on Mikrotik Routers Managing many network devices can be a pain. Especially if you need to simply plug a device in and repeat the steps to configure your the device. Thankfully, Ubiquiti's controllers can be automatically discovered and joined to UniFi using DHCP Option 43. In this article, we'll go through configuring a Mikrotik Router that is running Routerboard 3.x (my version that I am using at this time of the article). ## Step 1: Configure your UniFi controller A few things to remember before getting started: 1. Make sure your UniFi controller is always running (using a cloud key or a computer that is dedicated to serving the network with the UniFi Controller services) 2. Make sure your UniFi controller has a static IP address (this is very important). It can be a local static IP address, or a WAN static IP address. Whatever IP address it is, your devices must be able to access this IP and it cannot change ## Step 2: Configure your DHCP Server in Routerboard After you have your controller up and running, you will want to sign into your Mikrotik router. For simplicity sake, I am using Winbox (available on [Windows \[see download at bottom of this page\]](https://mikrotik.com/download){rel=""nofollow""} or [Mac OS X](http://joshaven.com/resources/tools/winbox-for-mac/){rel=""nofollow""}). - Once logged in, on the left sidebar go to "IP > DHCP Server". - At the top, you will find a tab called "Options". Click the "+" sign to create a new option. This is where things get fun... ## Step 3: Convert your IP address to hexadecimal Don't screw this step up because it is kind of confusing. Take your static IP address (for my example, I am going to use `192.168.1.200`) and convert it to hexadecimal format using the [IP to Hex Converter](https://www.miniwebtool.com/ip-address-to-hex-converter/){rel=""nofollow""} tool. You'll see your result below, but we need to modify it yet: ![IP to Hex Conversion](https://serversideup.net/blog/automatic-controller-assignment-unifi-dhcp-option-43-mikrotik-routers/Artboard.png) We need to take our result (in my case `0xC0A801C8`) and add `0104` after the `x` in the hexadecimal result that was created for us on the IP to Hex Converter tool. **This will give me a final result of:** `0x0104C0A801C8` Why `0104`? This is [how the documentation explains it](https://help.ubnt.com/hc/en-us/articles/204909754-UniFi-Device-Adoption-Methods-for-Remote-UniFi-Controllers#DHCP){rel=""nofollow""}: ![Hex Value Explanation](https://serversideup.net/blog/automatic-controller-assignment-unifi-dhcp-option-43-mikrotik-routers/HexValue.png) Going back to the "New DHCP Option screen, configure your options with the following options: ```bash [DHCP Option Configuration] Name: unifi Code: 43 Value: {{ your "final result" after modifying your hexadecimal IP }} ``` It should look like this once you are complete: ![DHCP Option Configuration](https://serversideup.net/blog/automatic-controller-assignment-unifi-dhcp-option-43-mikrotik-routers/UnifiDHCPOption43-1.png) ## Step 4: Configure your DHCP network to use the "unifi" option Under "IP > DHCP Server" then under the "Networks" tab, you will see a list of all your available DHCP networks. Double click on your network and assign it the `unifi` option that we just created. ![DHCP Network Configuration](https://serversideup.net/blog/automatic-controller-assignment-unifi-dhcp-option-43-mikrotik-routers/UnifiOption.png) ## Step 5: Plug in your *factory default* device Now all you need to do is plug in your **factory default device** and connect it to your network. If you have a device that belonged to a different network or controller, [reset the device first](https://help.ubnt.com/hc/en-us/articles/205143490-UniFi-How-to-Reset-the-UniFi-Access-Point-to-Factory-Defaults){rel=""nofollow""}. When your device is booted and connected to your network, open up your controller. You should see a new device that is awaiting adoption. ![UniFi Controller Interface](https://serversideup.net/blog/automatic-controller-assignment-unifi-dhcp-option-43-mikrotik-routers/UnifiController.png) Click "Adopt" and all your network settings will be applied to that device. This method has saved me countless hours of going on site and makes device upgrades painless. It's so easy, even my grandmother can do this (literally). All you need to do from this point forward is have the device on and plugged into the network and you can manage it anywhere in the world. If you found this article helpful or if you have any questions, drop a comment below! # Basic GET Requests with Fetch API and VueJS The best way to learn something or solve a problem is to break it down into the smallest pieces. While transitioning from Axios to the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch){rel=""nofollow""} and integrating it into my VueJS application, I had to start small. I actually wrote both requests side by side so I could migrate in pieces and ensure the integrity of the app. In this tutorial, we are going to be making some basic `GET` requests using Fetch and comparing those requests to Axios. I tend to learn best when I need to solve a problem and apply new tools to do it. For this course, I'm building a budgeting app (I actually am too as a side project, if it ever gets polished up, we'll release it). This will provide a great example of the different use-cases we need to account for during our migration. Since Fetch is a web browser API, there's no need to configure it [globally](https://serversideup.net/blog/configuring-axios-globally-with-vuejs/)! All methods are made available through the web browser. For this tutorial we are just creating a simple VueJS component. This VueJS component will call the `/api/v1/transactions` endpoint on our API through both the Fetch API and Axios so you can see how they both work. ## Step 1: Create your API Request For loading our transactions, I made 2 methods (one with the Fetch API and the other with Axios) within the same component. I'll show both of the methods and then break down the differences. ### Fetch API GET Request ```javascript [Fetch API Example] methods: { loadFetch(){ fetch( 'https://api.roastandbrew.coffee/api/v1/companies' ) .then( function( response ){ if( response.status != 200 ){ throw response.status; }else{ return response.json(); } }.bind(this)) .then( function( data ){ this.fetchResponse = data; }.bind(this)) .catch( function( error ){ this.fetchError = error; }.bind(this)); } } ``` ### Axios API GET Request ```javascript [Axios API Example] methods: { loadAxios(){ axios.get(' https://api.roastandbrew.coffee/api/v1/companies' ) .then( function( response ){ this.axiosResponse = response.data; }.bind(this)) .catch( function( error ){ this.axiosError = error; }.bind(this)); } } ``` I also had to import the library in to our component: ```javascript [Import the Axios module] import axios from 'axios'; ``` So those are our two requests! From first glance, they are extremely similar! In the next step, we will break down the differences. ## Step 2: Breaking Down the Method Signature So these are very simple GET requests, we will get to the more advanced stuff later on, but starting here we can begin to see the differences between the two requests. So to make the request, the method signature is very similar. Fetch API uses `fetch( END_POINT_URL )` and Axios uses `axios.get( END_POINT_URL )`. Right away you can see that Axios provides a method where you can explicitly call the HTTP Verb of `GET`. You can also do that with: ```javascript [Define the GET method on an Axios Request] axios({ method: 'get', url: END_POINT_URL }) ``` or using their default (which looks just like a Fetch API request): ```javascript [Use the default GET call] axios( END_POINT_URL ) ``` I prefer the `.get()` when using Axios, just because it's easy to read and very explicit. Fetch API doesn't provide that explicit `.get()` method. Nor does it apply the `.post()` method when we get to that later. To specify more options to your request, the method allows for a second parameter which is an object of configuration. We won't touch on that now, but it will look very similar to the Axios method without the explicit `.get()`. ## Step 3: Breaking Down the Method Response This is where the two methods differ a little bit. Both return promises which is nice and both work with `async/await`. However, the `fetch()` API returns a [Response object](https://developer.mozilla.org/en-US/docs/Web/API/Response){rel=""nofollow""} when the request completes successfully. This object has a ton of methods and settings you can work with! Axios returns the response in an object as well and it's accessible through `response.data`. To get the data from the request with the Fetch API you can call a method on the `Response` object. In our example we called `.json()` which returns another promise. However, let's take a step up one block of code and look at: ```javascript [Ensure we have a valid response code] if( response.status != 200 ){ } ``` We first check to see if the response completed successfully. Within the Fetch API, the promise will be resolved even if there was a server side error. Axios allows you to catch the server side error with `.catch()`. For me, I can see this being a hard habit to break, I like the `.catch()` syntax. There's another handy method which is `response.ok` which checks to make sure the status code is a `2XX` status code (perfect for 204 and other API type responses). If we don't get an error in our Fetch API request, we then call `response.json()` and chain another promise to load our data: ```javascript [Access the JSON response returned from the server] response.json().then( function( data ){ this.response = data; }.bind(this)); ``` This is called on the implemented [Body](https://developer.mozilla.org/en-US/docs/Web/API/Body){rel=""nofollow""} interface. There are a variety of other methods you can parse your response body with as well depending on your circumstance. One thing to note about both of these functions is the `.bind(this)` at the end of them. That statement gives the methods scope to our VueJS component so we can set local variables within it. You have to bind twice since there's a child promise returned in the Fetch API when you convert the response to `json()`. ## Conclusion So this is beginning our journey of learning the Fetch API alongside Axios. So far, in my opinion, what I like about the Fetch API is: - No external library - Powerful enough to give you access to the data and settings you need What I don't like about the Fetch API is: - Parsing the data with another promise (`response.json`) feels weird. - Not catching server side errors with `.catch()` will be a hard habit to break. I'm looking forward to doing some data creation with both libraries and eventually authentication (both handling oAuth Tokens and Laravel Sanctum). Also, I'm interested to see how we can make some API wrappers with Fetch API [like we did with Axios](https://serversideup.net/blog/build-an-api-wrapper-with-vuejs-axios/). For the rest of the series, I'll be focusing on the side-by-side comparison with Axios and using them both within VueJS. Matt Netkow over at Ionic wrote a super helpful article about [switching to Fetch as well](https://ionicframework.com/blog/replacing-native-plugins-with-web-apis/){rel=""nofollow""}. If you want to see the actual components that make these Fetch API requests, head [over to our Github repo.](https://github.com/serversideup/fetch-api-vuejs){rel=""nofollow""} The Fetch API could also be used instead of Axios if you are creating an [API driven web and mobile application](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). Feel free to reach out if you have any more questions. On to more advanced requests! # Beginning Vuex 4 with Vue 3 Vuex is one of the most useful plugins in the entire VueJS ecosystem. I honestly include Vuex in every app whether it's an SPA or Monolith. Using Vuex modules allows me to divide up large, dynamic and complex pages or components into maintainable, reusable, scoped components without having to pass a million `props`. I've written a few times about Vuex modules on Server Side Up, with [Using Vuex Modules Inside Components](https://serversideup.net/blog/using-vuex-modules-inside-components/) and [Build a Vuex Module](https://serversideup.net/blog/build-vuex-module/). Both I believe are good examples of what you can do with Vuex, I think it only scratches the surface of what Vuex is capable of. The scope of this tutorial is to introduce Vuex in a generic fashion and explain how you can fit it into your project. Let's dive in! ## What Is Vuex? According to the official docs on {rel=""nofollow""}, "Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion." I like to define and look at Vuex as a single source of truth that allows you to easily share data between components and pages without having to pass every piece of data through a component's props. If you've ever run into a situation where you have a component that has a million props, tons of defaults, and you can't seem to keep it in sync, Vuex is the right option. Before we go too far, with the introduction of the composition API, there's been some discussion on whether or not Vuex is still relevant or not. This is because the composition API makes code reusability even easier and allows you to share pieces of code through multiple components. I believe the composition API will be extremely helpful in replacing mixins and other functions, but I still love the **predictable fashion** and pattern that Vuex provides. ## When to Use a Vuex Store? There are a wide variety of times when it makes sense to use a Vuex data store! I've touched on a few of them already, but this is how I choose. ### When `props` becomes unmaintainable There isn't a quantitative number of props a component must have before switching to Vuex, but there's definitely a time where the component becomes difficult to maintain. If you've worked with components in the past you've probably experienced a point where there should "be a better way" to handle the data. That's when you need to use Vuex. ### Building Massively Complex Forms This is one area where I found Vuex to be extremely helpful. I used to think it was solely for sharing state across an SPA (which it is also very good at). If you are building a form that changes a state based off of user inputs, my guess is props will start getting hairy between your components. Time to check out Vuex! Another turning point for me was I wanted to divide my form into multiple, smaller components. These weren't necessarily re-usable, they were form specific, but my form was well over 2000 lines of code which is un-maintainable. By abstracting my form's state into separate Vuex modules, I was easily able to divide up the form into multiple components and make it much easier to maintain! We will run through an example on how to do this. ### Sharing Data Across an Entire SPA This is the standard example of when to use Vuex and we run through this a lot in our [book](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). Sharing data across an entire single page application is kind of the go-to for Vuex. Typically global app data would be like a user, notifications, etc. Any piece of data that needs to be present on an entire app, a state management system like Vuex makes this a breeze! ### Creating a Page With Multiple Moving Parts Sometimes you have a page that is super complex. When designing the transaction import page for [Financial Freedom](https://github.com/serversideup/financial-freedom){rel=""nofollow""}, I used a Vuex store. This allowed me to easily share data with other VueJS components. Complicated pages are essentially tiny single page applications. I've done this a couple other times in other apps as well where I make a store for a page. ## Installing Vuex 4 To install Vuex 4, run the following command: ```bash [Install Vuex] npm install vuex@next --save ``` At the time of this writing, Vuex 4 was on the `next` branch to give time to update to Vuex 3. Keep an eye out for when the branches are merged. ### Setting up Vuex 4 in an Vue 3 I assume you have a working Vue 3 instance and some sort of root file that gets compiled like `app.js`. Whatever that file is, open it up and add the following line first: ```javascript [Import the createStore method to start with Vuex] import { createStore } from 'vuex' ``` This will allow us to build our store for our app. Next, we need to initialize our data store by adding the following code: ```javascript [Start defining our Vuex Store] const store = createStore({ modules: { } }); ``` We will talk about creating modules next! Finally, let your Vue 3 app know to use the store by appending `.use(store)` to the end of your app definition. This can look like: ```javascript [Ensure your Vue app is set up to work with Vuex] createApp({ }).use(store).mount(el) ``` or like in the Vuex docs: ```javascript [Another method of ensuring Vuex is set up] const app = createApp({}); app.use(store); ``` Perfect! We now have Vuex ready to go in our Vue app! Before we dive into Vuex modules, I want to quickly note that you don't **HAVE** to use Vuex modules and can just make a few pieces of state to map in your components. However, I've honestly never used Vuex this way. I've always had enough state to make modules. Even if it's a few pieces of state, modules really clean up the code base and allow it to grow seamlessly. ## Vuex Modules As I mentioned, everything I do with Vuex is divided into modules. These modules consist of `actions`, `mutations`, `state` and `getters`. I touched on these with examples a little bit in "[Building a Vuex Module](https://serversideup.net/blog/build-vuex-module/)", but wanted to give a more generic approach. Let's break down each one of these pieces from my perspective. The [Vuex docs](https://next.vuex.vuejs.org/){rel=""nofollow""} also explains these well. ### State This is the heart of what Vuex helps with. These are the variables/pieces of data that you'd most likely be passing down as props if you didn't have Vuex. It's also the data that is shared between pages. Pieces of state are modified via `mutations` and retrieved via `getters`. ### Actions In a Vuex module, an action is a standard function usually used to prep data before committing to state through a mutation. I've placed API requests in actions that load data and commit based on success/failure. I've also calculated data in an action before committing to state such as incrementing or keeping track of a value. ### Mutations Mutations are simple methods that simply follow a pattern to modify a piece of state. Say you want to set an active song in a playlist. You'd "commit" a mutation that sets the active song in the playlist. This way each component or method that interacts with your state does so in the proper fashion. Actions also tend to "commit" mutations when they are finished. ### Getters A getter is how you receive a piece of data from state. They are used very heavily in components to make a reference to data. ### Creating a Vuex Module Let's keep track of a few songs in a playlist. To do this, I first start by creating a `store` directory wherever your `src` javascript files are. In that directory, I add a module named `playlist.js`: ```javascript [Example of a Vuex state for a playlist] export const playlist = { state: () => ({ songs: [], activeIndex: 1, activeSong: {}, status: 'paused' }), actions: { nextSong( { commit, state } ){ let nextIndex = state.activeIndex + 1; commit( 'setActiveIndex', nextIndex ); commit( 'setActiveSong', state.songs[ nextIndex ] ); } }, mutations: { setActiveIndex( index ){ state.activeIndex = index; }, setActiveSong( song ){ state.activeSong = song; } }, getters: { getActiveIndex( state ){ return state.activeIndex; }, getActiveSong( state ){ return state.activeSong; } } } ``` So there's a lot to break down but let's go through it. First of all, I didn't create all of the getters and mutations for the state simply because we are using this as an example, and it's all the same process. Let's start by looking at our `state`. In our `state` we initialize a few variables. These will be tracked in our components and will be reactive. Next up, we have our `actions`. If you take a look at the action, we are calling a standard process of `nextSong()`. Vuex injects the `context` variable which contains all of the available functions and state into the method. I decoupled what we need which is the `commit` method and the `state` in the method signature. This standardized action prepares what we need before committing to state. In this action, we find the next index number, then commit the mutation to `activeIndex` and we find the song at the new index and commit the mutation to set the `activeSong`. It's pretty slick to have a standardized way of performing state modifications. After `actions` we have `mutations`. The mutations are simple. They simply accept what is going to be set to a piece of state and set it. However, they are very important since all state needs a process to be modified correctly. Finally, we have `getters` . Getters allow us to retrieve values from our state store. These are used heavily in our components to gain access to the state that we have available. Now that we have a quick overview, let's get to the fun implementation! ## How to Use Vuex So we have our Vuex store registered, our first piece of state, let's get this implemented! The first step we need to take is register our module with our store. To do this, find where we created the Vuex store: ```javascript [Register our Vuex Module] const store = createStore({ modules: { } }) ``` Modify the code to look like: ```javascript [Add our playlist module] import { playlist } from './store/playlist.js'; const store = createStore({ modules: { playlist } }) ``` What we are doing is importing our playlist module and registering it with our code! Now we can perform our actions, mutations, and retrieve the values we need! ### Sharing State Between Components Let's say we want to display the `activeSong` from our state in 2 different components in our app. To do that, let's look at an example component that provides access to our Vuex store: ```javascript [Using our Vuex module in a Vue component] export default { computed: { activeSong(){ return this.$store.getters.getActiveSong(); } } } ``` The first thing to note is that any time you need to reference state in a component, it's in the `computed` property. You can name this property whatever you want (though I think it'd be hard to keep track if the names get crazy), it's inside of the property is what matters. Inside of the property, we return a reference to `this.$store.getters.getActiveSong()`. Right away you can see the `this.$store` reference. We registered the `$store` globally within our VueJS app so now we have it available everywhere! Next, we call the `getters.getActiveSong()` which returns the value of the `getActiveSong()` getter that we created. That's all we need to do! We can reference these values through their getters in any component we choose in our app! You may start to see that this could get a little hairy if you have a ton of state. I'll discuss how to handle that with `namespacing` in another tutorial. ## Dispatching an Action The final thing we should touch on is how to dispatch an action to set our state. Let's say we have a button that fires the `next()` method in our component. Implement the method like this: ```javascript [Dispatch an action from a component] export default { methods: { next(){ this.$store.dispatch('nextSong'); } } } ``` What this method does is simply `dispatch` an action with the string name of the action. In our case it's the `nextSong` action. Now our Vuex state will go to the next song in our `songs` array! You can also pass data to your action for pre-processing as well. The second parameter in the action is the data being passed. Say you want to either provide an index to skip to, or go incrementally. You could have an action that looks like: ```javascript [Define a Vuex action] actions: { nextSong( { commit, state }, index ){ let nextIndex = null; if( index ){ nextIndex = index; }else{ nextIndex = state.activeIndex + 1; } commit( 'setActiveIndex', nextIndex ); commit( 'setActiveSong', state.songs[ nextIndex ] ); } }, ``` Now you can incorporate some passed data into the process before you commit. ### Nested Vuex State One final thing I'd like to touch on in this tutorial is that you can nest Vuex state. I first saw this in an Axios example with [InertiaJS](https://inertiajs.com/){rel=""nofollow""} where they'd build a `form` object and submit just the `form` to the API. I thought it was brilliant! It really cleans up the axios requests and scopes the data nicely. With that being said, let's say we had a form to request a song. The data of the form spans multiple components and we store the form in the Vuex state: ```javascript [An example of nested Vuex state] export const playlist = { state: () => ({ form: { name: '', artist: '', album: '' } }), actions: { setName( { commit }, data ){ commit( 'setName', data ); } }, mutations: { setName( state, name ){ state.form.name = name; } }, getters: { getForm( state ){ return state.form } } } ``` You can actually provide nested objects in the state! So when it comes time to submit the form to the API all you have to do is submit: `this.form` with `form` being the name of the computed property that references the state: ```javascript [Setting up your Vue component to use a Vuex store] export default { computed: { form(){ return this.$store.getters.getForm(); } }, methods: { requestSong(){ axios.post( '/api/v1/requests', this.form ); } } } ``` I find this syntax really nice for scoping a form so you can easily add fields if needed without having to adjust the `axios` request. Hopefully that helps a little bit and gives a better understanding of the power of Vuex. In the next tutorial, I'll go into some advanced Vuex methods such as mapping state, actions, using Vuex state as a v-model, namespacing and nested namespacing. Until then, let me know if you have any questions! # Best practices for planning for UniFi Video See the entire "Complete Ubiquiti UniFi + Synology Network Build" course here: {rel=""nofollow""} # Browser Extension Messaging One of the most confusing parts of developing your first browser extension is how to make each part communicate efficiently. Reading the docs doesn’t help. There’s ports and one time message calls, confusion on how to handle responses whether they are synchronous or asynchronous, and it’s super difficult to target certain parts of your extension. That’s where the [webext-bridge](https://serversideup.net/open-source/webext-bridge/) package comes in. The `webext-bridge` package allows you to easily communicate between different parts of your extension. Every piece is handled cleanly and efficiently. Most importantly, you can target where you are sending the message so you don’t have check if the part of the extension receiving the message should be receiving the message. Let’s run though how to use the `webext-bridge` package. ## Installing the `webext-bridge` package First, we will need to add the `webext-bridge` package to our browser extension. To do that, run the following command: ```bash [Install the webext-bridge package] yarn add webext-bridge ``` That’s it! Now we can begin to use the package within our extension. ## Including the right module Let’s say we have 3 pieces to our extension. We have a background script, content script, and popup. Each piece of the extension needs to communicate with one another efficiently. For the sake of simplicity in this example, we’ll say our popup script lives in the `popup.js` file, our background script in `background.js`, and our content script in `content.js`. The most important part of using the `webext-bridge` package is to import the right module in the right place when setting up your communication. Let’s say we want to handle communication in the content script: ```javascript [Handle communication from the content script] import { sendMessage, onMessage } from "webext-bridge/content-script" ``` Notice how we import the `webext-bridge/content-script` module? That module is scoped to the content script. So when we target a message to the content script, we ensure it’s going to the right place and getting picked up by the script itself. The same goes for the popup and the background script. To handle communication in either of those areas, you’d add the following code respectively: ```javascript [Handle communication from the popup] import { sendMessage, onMessage } from "webext-bridge/popup" ``` or ```javascript [Handle communication from the background page] import { sendMessage, onMessage } from "webext-bridge/background" ``` Once you have the right module imported in your code, you are ready to pass messages! ## Sending Messages Now that we have our modules included, let’s send some messages! This can happen on a user interaction, a timed event, or anywhere within your extension you need to communicate. If you notice, when you import a module, you are importing two methods, `sendMessage()` and `onMessage()`. Let’s look at the `sendMessage()` method first. The `sendMessage()` method is, you guessed it, how you send a message. It accepts 3 parameters no matter where you include it: - `messageId` - ID of the message that we are sending. I usually set this to an uppercase enum or string value so it's easy to listen for and makes sense. - `data` - JSON data to send along with the message. Used for processing. - `target` - The target we are sending the message to. In our example, there are 3 targets we will be using: `background`, `content-script`, and `popup`. We will also show how to target the content script at a specific tab. Let’s say we want to send a message from our popup to the background. Our full code would look like this: ```javascript [Send message from popup to background] import { sendMessage, onMessage } from "webext-bridge/popup"; const response = await sendMessage("MESSAGE_ID", { // your data }, "background"); ``` The two big things to note are the `MESSAGE_ID` which will identify the message where we intend to receive it and handle the functionality, and the `background` target. The `background` target identifies the background script as the receiving end of the message. Right now, we are halfway through the process. Let’s look at the `onMessage()` handler and finish up this message passing. ## Receiving Messages So we have a message being passed to our background script, but how do we handle it? That’s where the `onMessage()` handler comes in. The \` # Build an API Wrapper with VueJS & Axios Creating an API wrapper using VueJS & Axios makes your API interfacing code extremely fluid, modular, and maintainable. Before we get started, those using NuxtJS should skip to the next tutorial. This will tutorial will ONLY work with VueJS and not within NuxtJS. With that being said, so far we've installed [Axios and got it to work with VueJS](https://serversideup.net/blog/using-axios-to-make-api-requests-with-vuejs/) and configured [Axios to work globally](https://serversideup.net/blog/configuring-axios-globally-with-vuejs/). We've also went through a few more [complex requests like POST, PUT, & PATCH](https://serversideup.net/blog/post-put-patch-requests-with-vuejs-and-axios/). In this tutorial we will abstract all of our API requests into wrapper modules. Before we get started, let's begin with why. ## Why Should We Do This? Simply put, for code maintainability and re-usability. What we will be doing is taking all of our API requests for a specific resource and wrapping them in a module. If you need to make a request to an API endpoint using this module with a Vuex action, you can. If you need to make an API request in a component, you can do that as well. This process will work with your own API or a 3rd Party API. However, the best part is, if you need to change a request in any way (like adding a header, upgrading API versions, etc), you update it once. This will update the request through your entire app! It's really convenient and easy to use. In the next tutorial I'll show you how to do this with NuxtJS if that's what you are using. Let's get started! ## Step 1: Build a Front End API Directory The way I approach development is to set up "buckets" (which are just folders) to house code and pseudo-structure before I start developing. Before I add any API wrapping modules, I always create an `/api` directory at the root of my app. In this directory I will place all of my modules. If you have a very complex API, feel free to add sub-directories to this as well. The more code organization, the better! Let's add our first module. ## Step 2: Add Your First Module Let's say we have an API focused around music. This API has endpoints that allow us to manage the songs resource. We can view all songs (`GET`), create a song (`POST`), update a song (`PUT`), view a song (`GET`) and delete a song (`DELETE`). There are a variety of places in our application where we interact with this resource, so we want to build a module. The first step is to add the file: `/api/songs.js`. In this file, add the following code: ```javascript [Template for our API request module] export default { index( params ){ }, show( id ){ }, update( id, data ){ }, create( data ){ }, delete( id ){ } } ``` If you've worked with Laravel in the past, this naming convention may look familiar. If you haven't, you might be wondering what's up with the names of these methods. Laravel uses a naming scheme for resource endpoints that I really enjoy implementing on both back and front end. If you want to read about it, check out their [documentation on Resource Controllers](https://laravel.com/docs/7.x/controllers#resource-controllers){rel=""nofollow""}. You don't have to use Laravel as your backend and you really don't even have to use this naming format. These are just the methods that we will be implementing for each resource regardless of first party or third party. Each method will return an Axios request which is a promise. This way we can efficiently handle the request in context. ### The `index` Endpoint When following a resource naming scheme, the `index` endpoint will load all of a specific resource. It's a `GET` request that usually accepts parameters so you can filter/order your response. You don't really want to load all of the resource when you call this route. Especially in larger systems where this could literally be over a million items, so these filters usually come in handy or maybe even be required. Let's take a look at our music app example. Say we want to load all of the songs, we will have to implement the `index()` method. To do that, add the following code: ```javascript [Example of calling an index endpoint] index( params ){ return axios.get( 'https://music.com/api/v1/songs', { params: params }) }, ``` *Remember, we set up [Axios to be global!](https://serversideup.net/blog/configuring-axios-globally-with-vuejs/)* This method accepts a single parameter which should be a JSON object containing any filters, or ordering data that you wish to send. This variable allows you to pass filters to your resource module in the form of a JSON object that Axios will turn into a query string for your API. Remember, sending a [GET request with Axios](https://serversideup.net/blog/using-axios-to-make-api-requests-with-vuejs/), the second parameter of the Axios GET request is the configuration. This will build a nice query string to append to your request. Because of these filters, this tends to be the most complicated endpoint to create. Let's say you wanted to search for an artist and order by newest releases. You'd pass a JSON object like this: ```json [Example search request] { "artist": "Red Hot Chili Peppers", "order_by": "release_date", "order_direction": "DESC" } ``` Your query string will then be formatted like: `?artist=Red%20Hot%20Chili%20Peppers&order_by=release_date&order_direction=DESC` Let's build out the rest of our endpoints for our module and then we can show the power of this module within VueJS. ### The `show` Endpoint The `show()` method is very similar to the `index()` method since it is a `GET` request. However, the `show()` method should only return a **single** resource. In our example, this is a song. To implement this method simply add the following code: ```javascript [Example of calling a show() endpoint] show( id ){ return axios.get( 'https://music.com/api/v1/songs/'+id ); }, ``` Since we are limiting what this endpoint is returning to a single resource, we don't pass a `params` variable like we did to the `index()` method. The variable `id` allows us to load an individual resource based on it's unique identifier. ### The `create` Endpoint This endpoint will handle the creation of a resource on the API, thus using the `POST` method. As [discussed in the last section](https://serversideup.net/blog/post-put-patch-requests-with-vuejs-and-axios/), there are two ways to send data to a server with Axios, through JSON and through Form Data. Our API wrapper will account for this by accepting whatever version you throw at it. Let's add the endpoint like this: ```javascript [Example of calling a create() endpoint] create( data ){ return axios.post( 'https://music.com/api/v1/songs', data ); }, ``` The parameter `data` will contain either a JSON object or a FormData object depending on if you need to send files. Either one works! When we implement this module in Step 3 & 4 you can see how fluid this will be to submit data to a server. ### The `update` Endpoint Similar to the `create` endpoint, the `update` endpoint sends data to the API. However, since we are updating a resource, we need an additional parameter of `id` to identify the specific resource we are updating. Depending on the [use case,](https://serversideup.net/blog/post-put-patch-requests-with-vuejs-and-axios/) you should use `PUT` or `PATCH` for the method. Let's say we are using `PUT`. Our wrapper method should look like this: ```javascript [Example of calling an update() endpoint] update( id, data ){ return axios.put( 'https://music.com/api/v1/songs/'+id, data ); }, ``` We build the URL to update the specific song defined by our `id` parameter. Similar to the `create` endpoint, the `data` parameter can be either JSON or FormData. HOWEVER, if it is FormData, you will have to modify your method in certain circumstances (like with Laravel), to look like this: ```javascript [Using PUT with FormData] update( id, data ){ data._method = 'PUT'; return axios.post( 'https://music.com/api/v1/songs/'+id, data ); }, ``` I know Laravel specifically requires you to `POST` any FormData to the server, but if you add `_method` and set it to `PUT` you can keep your resource controllers standardized and can respond to the request. ### The `delete` Endpoint This is the last endpoint we will be implementing. It simply deletes a resource from the system. The only parameter it accepts is the `id` of the resource we wish to delete: ```javascript [Example of calling a DELETE endpoint] delete( id ){ return axios.delete( 'https://music.com/api/v1/songs/' + id ) } ``` There we go! This is what our final API Wrapper should look like: ```javascript [Final API Wrapper] export default { index( params ){ return axios.get( 'https://music.com/api/v1/songs', { params: params }) }, show( id ){ return axios.get( 'https://music.com/api/v1/songs/'+id ); }, update( id, data ){ return axios.put( 'https://music.com/api/v1/songs/'+id, data ); }, create( data ){ return axios.post( 'https://music.com/api/v1/songs', data ); }, delete( id ){ return axios.delete( 'https://music.com/api/v1/songs/' + id ) } } ``` Now, let's get to the part where our hard work pays off. Using the module within a VueJS component and Vuex module! You will be able to use these methods anywhere you feel is necessary making the code standardized and extremely re-usable! ## Step 3: Using Your API Wrapper in a VueJS Component Now it's time to reap the rewards of our hard work! We can re-use our module in any component or page within our VueJS App! The API Wrapper comes in handy whenever you need to interface with an API. Sometimes, this could be in a parent page (like a layout) as well where you need to load data globally. All you need to do inside of your component is include the API module you created. Let's say we have a page that loads all of the songs from our music app by the `Red Hot Chili Peppers`. We would have a component that looks like this: ```vue [Using our API Wrapper in a Vue component] ``` All we needed to do is `import` our API module within our component and then call the method in our \` # Build Out API Requests in Javascript In the last tutorial we added a few API Endpoints in Laravel [Add Laravel API Endpoints](https://serversideup.net/blog/add-api-end-points-laravel/). Now it's time to build out API requests in javascript to access these routes. Since we have everything ready to rock and roll with our VueJS configuration and our routes, it should be pretty smooth to add these requests and we can store the data. We get to start using Vuex modules as well! ## Step 1: Configure the config.js file The config.js file I find extremely important for development in multiple environments. I put all of my JS environment specific information in this file. Right now we will only have one variable that varies per environment and that is the api\_url. On my development machine, I have a development domain called roast.dev and in production in the source code that is roastandbrew\.coffee. When I build my app I want node to call each API route correctly based on my environment. So in my `/resources/assets/config.js` I make a simple switch statement that builds out the `api_url` depending on which environment I'm building my app for, then I export the config. My config.js file looks like: ```javascript [Example config file] /* Defines the API route we are using. */ var api_url = ''; switch( process.env.NODE_ENV ){ case 'development': api_url = 'https://roast.dev/api/v1'; break; case 'production': api_url = 'https://roastandbrew.coffee/api/v1'; break; } export const ROAST_CONFIG = { API_URL: api_url, } ``` I can then export out any of the variables I feel fit as an object and use them in the rest of the front end of my app, like the next section where we define the API routes. ## Step 2: Add cafe.js file I like to mimic our API structure with our resources in Javascript. This allows us to group our API routes by resource. First what we will need to do is make the cafe.js file in the `/resources/assets/js/api` directory. Next we need to import the `ROAST_CONFIG` from the last step so we can access the API\_URL to make our requests. To do that, add the following code to the top of your `cafe.js` file: ```javascript [Import the config file into our API wrapper] /* Imports the Roast API URL from the config. */ import { ROAST_CONFIG } from '../config.js'; ``` Now we have our base API URL to make our requests to. Next we should export a default module so we can use our API requests elsewhere in our application. Our `cafe.js` file should look like: ```javascript [Set up our basic API wrapper] /* Imports the Roast API URL from the config. */ import { ROAST_CONFIG } from '../config.js'; export default { } ``` ## Step 3: Add Request For Getting Cafes We now need to add a method for getting all of the cafes. This method will access the `/api/v1/cafes` route on the Laravel side of our application. For all of our front end requests, we will be using the axios library that we installed when we configured our application: [GitHub - axios/axios: Promise based HTTP client for the browser and node.js](https://github.com/axios/axios){rel=""nofollow""}. We have this set up in this tutorial: /configuring-js-sass-single-page-app/ and will automatically add the proper header to access the application's API routes. To add the request to access the cafes, add the following code inside your module: ```javascript [Create our getCafes() method] export default { /* GET /api/v1/cafes */ getCafes: function(){ return axios.get( ROAST_CONFIG.API_URL + '/cafes' ); }, } ``` What this does is returns an axios GET request to the proper API route. In Vuex we will call this API request as an action and save the data to a module that we will build. ## Step 4: Add Request For Getting Single Cafeh Similar to the last request, we will return an axios GET request to the `/api/v1/cafes/{cafeID}` route that we will call through Vuex. The difference is in this request we have a parameter for the cafeID which will be used to reference the cafe we are loading. To add this request add the following line of code below the last request: ```javascript [Load an individual cafe] /* GET /api/v1/cafes/{cafeID} */ getCafe: function( cafeID ){ return axios.get( ROAST_CONFIG.API_URL + '/cafes/' + cafeID ); }, ``` This will return the specific cafe we are loading up. ## Step 5: Add Request For Adding A Cafe The mindset is the same we are building an axios request that we are returning that we can call anywhere in the app. This time it's a POST request though and requires a few more parameters. However once again we are going to be calling this through a Vuex action which we will make in the next tutorial. To add the POST request, add the following below the request for a single cafe: ```javascript [Add the request to create a new cafe] /* POST /api/v1/cafes */ postAddNewCafe: function( name, address, city, state, zip ){ return axios.post( ROAST_CONFIG.API_URL + '/cafes', { name: name, address: address, city: city, state: state, zip: zip } ); } ``` This method's parameters match what will get passed to our `/api/v1/cafes` route to add a new cafe. When we send our Vuex action's payload we will extract the data and call this method adding an action. ## Wrapping Up So we have all of our actions added in javascript to the `/resources/assets/js/api/cafe.js` file and it should look like: ```javascript [Our entire API request module] /* Imports the Roast API URL from the config. */ import { ROAST_CONFIG } from '../config.js'; export default { /* GET /api/v1/cafes */ getCafes: function(){ return axios.get( ROAST_CONFIG.API_URL + '/cafes' ); }, /* GET /api/v1/cafes/{cafeID} */ getCafe: function( cafeID ){ return axios.get( ROAST_CONFIG.API_URL + '/cafes/' + cafeID ); }, /* POST /api/v1/cafes */ postAddNewCafe: function( name, address, city, state, zip ){ return axios.post( ROAST_CONFIG.API_URL + '/cafes', { name: name, address: address, city: city, state: state, zip: zip } ); } } ``` We are returning axios requests so we can listen and act upon the promises within the Vuex action. Sounds really complex, but in the next tutorial you will begin to see how this all comes together very elegantly. This will keep track of all of the data we need in the front end of our app and make loading all of this data on demand a breeze. Since we are returning the axios requests and calling them in a Vuex action, we can handle errors and handle successes. I know this is a lot of set up, but trust me, once you have it configured correctly everything will start to fall into place. Think of it this way. You can build a house with a foundation, but the second you add stuff to it you will quickly see it needs to be redone. Or you can build a solid cement foundation and adding features to your house is easily supported. Very similar in coding. We have all of this configuration, but once we get the base of it done, we can really fly together and make a beautiful SPA. You will really see the benefits of doing it this way if you ever wanted a hybrid mobile application. The goal of this tutorial series is to get there with cordova. You literally can copy the code you have, create a new entry point file and within about 1-2 hours have a fully functioning mobile application. # Build a Vuex Module In the last tutorial [Build Out API Requests in Javascript](https://serversideup.net/blog/build-api-requests-javascript/) , we went through building methods to call our Laravel API routes with Javascript. We built the `/resources/assets/js/api/cafe.js` file which contains the front end requests to our backend API that we built here: [Add Laravel API End Points - Server Side Up](https://serversideup.net/blog/add-api-end-points-laravel/). Now we are at the point where we need to store the data we get from the API so we can use it in our Single Page Application. That's where Vuex Modules come in! According to the Vuex documentation [What is Vuex? · Vuex](https://vuex.vuejs.org/en/intro.html){rel=""nofollow""} "Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion." What this translates to is it's a single point of data that can be reused across multiple components and multiple pages. Why would you want this? As you build larger, more complex single page applications, you end up using data in multiple places. For example, you have a user that's logged into your app. Instead of passing that user in as a parameter to each component that uses that user's first name and last name, you can store it in a Vuex module and access that data whenever is needed. We will be using that example and many more vuex modules in Roast. Vuex is also extremely helpful in tracking the state of the data in your application. If you are using the dev tools, you can see the data each module holds and how to access it. Vuex can be a little difficult to understand at first as it's a different way to store data in your application. The documentation written about Vuex is extremely thorough and helpful: [What is Vuex? · Vuex](https://vuex.vuejs.org/en/intro.html){rel=""nofollow""} . Not all applications will require a Vuex data store, but if you were like me, and you are writing a larger single page application, you begin to think "there has to be a better way to handle all of this data". You are right, that's Vuex. ## Step 1: Understanding mutations, getters, actions, modules, and store. The first thing we have to do is lay out our data plan for what we are trying to build. Right now we have a backend API for cafes in Roast and a front end set of API methods to access these routes. Our fist module we will build will be a cafes module. This module will handle all of the data relating to what we get back from the API for cafes. A module is almost like a a basic object with certain methods used to access and set the data. Next up, we have mutations. Mutations are ways to manipulate the data. Every piece of data in a module should have mutations there to manipulate it. You will *NOT* be manipulating the data directly. The goal of Vuex is to have a single funnel for all components to access the state of the data and keep it in sync. You will call an action to mutate the data. If this doesn't make sense, the code examples in this tutorial will help and this part of the Vuex documentation [What is Vuex? · Vuex](https://vuex.vuejs.org/en/intro.html){rel=""nofollow""}. The section "What is a "State Management Pattern"? has some super helpful images that explain the flow of Vuex. Getters are how you access the current state of the data. You will be using these in the components you create to access the single point of data for your application. All of the modules live in what's called a data store. This is like the entry point for your data structure. Each module like cafe will be part of a larger data store. Lets get started and I'll explain more as we go! ## Step 2: Configure your `/resources/assets/js/store.js` So we already created an initial `store.js` file when we set up our application to work with VueJS. We will need to open this file and a little code so we have an initial store ready to rock and roll. At the top of your file, import Vue and Vuex so we can start building out our store: ```javascript [Initialize our store] /* Imports Vue and Vuex */ import Vue from 'vue' import Vuex from 'vuex' ``` Next, we will instruct Vue to use Vuex as a data store. This will extend our Vue instance with the methods needed to utilize the Vuex data store. Add this line of code right after you imported Vue and Vuex: ```javascript [Add Vuex to Vue] /* Initializes Vuex on Vue. */ Vue.use( Vuex ) ``` Lastly, we will export a new Vuex data store from our store.js file. This is so we can apply it to our Vue instance and have all of the modules accessible within components and routes. Add this to the end of the file: ```javascript [Export our data store] /* Exports our data store. */ export default new Vuex.Store({ modules: { } }); ``` We now have a very basic data store configured. This will make it easy for us to expand with our modules. ## Step 3: Add es6-promise polyfill to store for IE Of course IE 11 doesn't support promises and requires extra configuration. We will need to add the polyfill to our store so Vuex will work with IE. So open up a terminal, navigate to your development directory and run the following command: ```bash [Add polyfill for IE] npm install es6-promise —save-dev ``` This will add the es6-promise polypill to your package.json file for your application. On the top of the `/resources/assets/js/store.js` file add the following line of code: ```javascript [Require the polyfill in our store] /* Adds the promise polyfill for IE 11 */ require('es6-promise').polyfill(); ``` Our store.js file should look like: ```javascript [store.js] /* |------------------------------------------------------------------------------- | VUEX store.js |------------------------------------------------------------------------------- | Builds the data store from all of the modules for the Roast app. */ /* Adds the promise polyfill for IE 11 */ require('es6-promise').polyfill(); /* Imports Vue and Vuex */ import Vue from 'vue' import Vuex from 'vuex' /* Initializes Vuex on Vue. */ Vue.use( Vuex ) /* Exports our data store. */ export default new Vuex.Store({ modules: { } }); ``` We will be adding modules shortly, but it's nice to get this all set up so we have a place to put our modules. ## Step 4: Add the Data Store to Vue Now that we have our data store built, we will need to add it to Vue. Our Vue instance that we are using resides in `/resources/assets/js/app.js` so open that file. Below our import for our router add the following line of code: `import store from './store.js'` This includes our store that we created. Next we have to extend our Vue instance with that store. So right under the router, make sure include the store we just imported. The updated code should look like: ```javascript [Import our store into our app.js] new Vue({ router, store }).$mount('#app') ``` Our `app.js` file should now look like: ```javascript [app.js] window._ = require('lodash'); try { window.$ = window.jQuery = require('jquery'); require('foundation-sites'); } catch (e) {} /** * We'll load the axios HTTP library which allows us to easily issue requests * to our Laravel back-end. This library automatically handles sending the * CSRF token as a header based on the value of the "XSRF" token cookie. */ window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; /** * Next we will register the CSRF Token as a common header with Axios so that * all outgoing HTTP requests automatically have it attached. This is just * a simple convenience so we don't have to attach every token manually. */ let token = document.head.querySelector('meta[name="csrf-token"]'); if (token) { window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; } else { console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); } import Vue from 'vue'; import router from './routes.js' import store from './store.js' new Vue({ router, store }).$mount('#app') ``` We now are using the entire VueJS ecosystem in our application! Let's add some modules and put this work horse to use! ## Step 5: Add cafes.js Vuex module The first part is to create a new file in your `/resources/assets/js/modules/` directory named `cafes.js`. This will be where we manage all of the data for our cafes. We can then use all of this data through out our app. This is where you will start to see some of the benefits of load time with a Single Page App. You will load the cafes once, store it our Vuex module and only re-load when needed. It will be available for use on any page or any component in our app! Right now, the `cafes.js` file should be empty, the next step is configuring the file. ## Step 6: Configure the State We are diving heavily into Vuex now! With our `/resources/assets/js/modules/cafes.js` file open, first import the CafeAPI from our API directory that we built in: /build-api-requests-javascript/. We will be using these methods in our actions to load the data. Our file should look like this: ```javascript [Example of our cafes module] /* |------------------------------------------------------------------------------- | VUEX modules/cafes.js |------------------------------------------------------------------------------- | The Vuex data store for the cafes */ import CafeAPI from '../api/cafe.js'; ``` Now we will export a constant which will be our cafes module. Underneath the imported CafeAPI add the following code: ```javascript [Export our cafes store] export const cafes = { } ``` This is our module that we will be adding to our data store. We will import this into the store later on, for now we are just stubbing out our module. Next we will add the 4 aspects of a Vuex module (state, actions, mutations, getters). First we will add an empty object for the state like so: ```javascript [Add state to our cafes] export const cafes = { state: { } } ``` The state is all of the data we want to track. In the cafes module I can think of 2 different pieces of data that we would want: An array of cafes, and an object that stores a single cafe. This matches our API that returns all of the cafes and an individual cafe. We will initialize these two pieces of data like this: ```javascript [Initialize 2 pieces of state for our store] export const cafes = { state: { cafes: [], cafe: {} } ``` As a rule of thumb, one issue I always ran into was displaying a loading state. In a Single Page App, loading states are essential. The HTML/CSS and other page features will load usually before the data giving a bad UX to users who are waiting for the data to load. For each piece of data we track in the state, I add a corresponding variable to the state which keeps track of the load status. This way I can read this variable to determine whether to display the loading state or not. With VueJS being reactive, when the data is loaded, the variable is updated, the components using the variable will be updated as well and show the screen accordingly. I'd add the following to the state: ```javascript [Add our loading statuses] export const cafes = { state: { cafes: [], cafesLoadStatus: 0, cafe: {}, cafeLoadStatus: 0 } } ``` I usually follow a method like this: - status = 0 -> No loading has begun - status = 1 -> Loading has started - status = 2 -> Loading completed successfully - status = 3 -> Loading completed unsuccessfully This way we can display the appropriate information when needed depending on what loaded. ## Step 7: Configure the Actions I briefly went over what actions were, but now it's time to implement them. Actions are what is called on the module to mutate the state. In this case, we will call an action that makes a request to the API and commits a mutation. The mutations we will implement in the next step. First we will add an empty object that will contain our action methods: ```javascript [Define actions within our module] export const cafes = { state: { cafes: [], cafesLoadStatus: 0, cafe: {}, cafeLoadStatus: 0 }, actions: { } } ``` In our actions object we will add the methods to load our cafes and individual cafe. Our module should look like: ```javascript [Template out our individual actions] export const cafes = { state: { cafes: [], cafesLoadStatus: 0, cafe: {}, cafeLoadStatus: 0 }, actions: { loadCafes( { commit } ){ }, loadCafe( { commit }, data ){ } } } ``` Two things to note. 1. Each method contains a destructured argument called `commit`. This is passed in by Vuex and allows us to commit mutations for our store. There are other destructured arguments you can pass in as well. To read more about Argument Destructuring read: [GitHub - lukehoban/es6features: Overview of ECMAScript 6 features](https://github.com/lukehoban/es6features#destructuring){rel=""nofollow""} 2. The `loadCafe` action contains a second argument named `data`. This is an object we will pass to the method that contains the ID of the cafe we are loading. We are limited to an extra argument so you can pass in an object for more variables. Now to implement these methods, we will do the following: ```javascript [Implement our methods within our store] export const cafes = { state: { cafes: [], cafesLoadStatus: 0, cafe: {}, cafeLoadStatus: 0 }, actions: { loadCafes( { commit } ){ commit( 'setCafesLoadStatus', 1 ); CafeAPI.getCafes() .then( function( response ){ commit( 'setCafes', response.data ); commit( 'setCafesLoadStatus', 2 ); }) .catch( function(){ commit( 'setCafes', [] ); commit( 'setCafesLoadStatus', 3 ); }); }, loadCafe( { commit }, data ){ commit( 'setCafeLoadStatus', 1 ); CafeAPI.getCafe( data.id ) .then( function( response ){ commit( 'setCafe', response.data ); commit( 'setCafeLoadStatus', 2 ); }) .catch( function(){ commit( 'setCafe', {} ); commit( 'setCafeLoadStatus', 3 ); }); } }, } } ``` First thing to note is the commit function. This is committing a mutation. We will be setting up these mutations in the next step. However, just a reminder, each piece of data in the state should have a mutation. In both methods, we commit the load status for the piece of the state we are using. Next we make an API call to load the specific piece of information that we want to load. These API calls are defined in our `/resources/assets/js/api/cafe.js` file. Since we are returning axios ([GitHub - axios/axios: Promise based HTTP client for the browser and node.js](https://github.com/axios/axios){rel=""nofollow""}) calls, we can then bind into the `then` and `catch` chained promises. `then` method gets called when the method is returned successfully, so we will commit a mutation with the data returned. The `catch` method gets called when the data has been loaded unsuccessfully. We then will mutate the data accordingly like set the load statuses to failure and clear out the cafes or the state since we don't want failed data. The `response` variable passed into each of the method allows us to access the response data and headers from the request. ## Step 8: Configure the Mutations The mutations are how your data gets updated. Each module has state, which requires a mutation to update. The flow is like this: 1. The user calls an action 2. The action loads/computes data 3. The action commits a mutation 4. The state gets updated 5. The getter (next step) reactively returns the state back to the component. 6. The component gets updated. There are lots of steps but trust me, compared to implementing something like this in jQuery or vanilla JS, Vuex makes this a breeze. We have our state built, our actions made, now it's time to implement the mutations. We saw mutations being called when we configured our actions, time to make them functional. If you look at the uniqueness of the commit methods in our actions, there are 4 unique mutations needed: 1. setCafesLoadStatus 2. setCafes 3. setCafeLoadStatus 4. setCafe To start, let's add these methods to our mutations object: ```javascript [Add mutations to our module] mutations: { setCafesLoadStatus( state, status ){ }, setCafes( state, cafes ){ }, setCafeLoadStatus( state, status ){ }, setCafe( state, cafe ){ } }, ``` So all mutations do is set the state. This way they can be tracked and it's consistent whenever the state needs to be updated. The first argument for the mutation is the state. This is the local module state NOT the global state. So the state we configured in step 6 is accessible. The second parameter is the data we need to update the state to. Our mutations should look like: ```javascript [Wire up our mutations] mutations: { setCafesLoadStatus( state, status ){ state.cafesLoadStatus = status; }, setCafes( state, cafes ){ state.cafes = cafes; }, setCafeLoadStatus( state, status ){ state.cafeLoadStatus = status; }, setCafe( state, cafe ){ state.cafe = cafe; } }, ``` In each mutation, we set the local module's state data to what is being passed in. Really this is all each mutation does, mutates the state data. Next, we will configure the getters and our first Vuex module will be ready to go! ## Step 9: Configure the Getters So we have state data we want to track, actions to retrieve the data from the API and mutations to set the data. Now it's time to retrieve the data from the modules. We do that with getters. We need to define an object that will contain all of our getter functions. If you are used to some OOP design principals, mutations are like setters, getters are... getters, they retrieve the data. Our getters object should be added to our cafes component like: ```javascript [Define the getters key on our module] getters: { } ``` Next we will need a method for each of our state variables that will return the state data we are looking for: ```javascript [Build out our getters in our store] getters: { getCafesLoadStatus( state ){ return state.cafesLoadStatus; }, getCafes( state ){ return state.cafes; }, getCafeLoadStatus( state ){ return state.cafeLoadStatus; }, getCafe( state ){ return state.cafe; } } ``` Each method takes the local module state as the parameter and each method returns the corresponding data in the state we wish to retrieve. That's all we need to do for our getters! We can now utilize all of this data within our components. Our module is now complete for this tutorial and should look like this: ```javascript [Complete Vuex module] /* |------------------------------------------------------------------------------- | VUEX modules/cafes.js |------------------------------------------------------------------------------- | The Vuex data store for the cafes */ import CafeAPI from '../api/cafe.js'; export const cafes = { /* Defines the state being monitored for the module. */ state: { cafes: [], cafesLoadStatus: 0, cafe: {}, cafeLoadStatus: 0 }, /* Defines the actions used to retrieve the data. */ actions: { /* Loads the cafes from the API */ loadCafes( { commit } ){ commit( 'setCafesLoadStatus', 1 ); CafeAPI.getCafes() .then( function( response ){ commit( 'setCafes', response.data ); commit( 'setCafesLoadStatus', 2 ); }) .catch( function(){ commit( 'setCafes', [] ); commit( 'setCafesLoadStatus', 3 ); }); }, /* Loads an individual cafe from the API */ loadCafe( { commit }, data ){ commit( 'setCafeLoadStatus', 1 ); CafeAPI.getCafe( data.id ) .then( function( response ){ commit( 'setCafe', response.data ); commit( 'setCafeLoadStatus', 2 ); }) .catch( function(){ commit( 'setCafe', {} ); commit( 'setCafeLoadStatus', 3 ); }); } }, /* Defines the mutations used */ mutations: { /* Sets the cafes load status */ setCafesLoadStatus( state, status ){ state.cafesLoadStatus = status; }, /* Sets the cafes */ setCafes( state, cafes ){ state.cafes = cafes; }, /* Set the cafe load status */ setCafeLoadStatus( state, status ){ state.cafeLoadStatus = status; }, /* Set the cafe */ setCafe( state, cafe ){ state.cafe = cafe; } }, /* Defines the getters used by the module */ getters: { /* Returns the cafes load status. */ getCafesLoadStatus( state ){ return state.cafesLoadStatus; }, /* Returns the cafes. */ getCafes( state ){ return state.cafes; }, /* Returns the cafes load status */ getCafeLoadStatus( state ){ return state.cafeLoadStatus; }, /* Returns the cafe */ getCafe( state ){ return state.cafe; } } } ``` ## Step 10: Add the Module to the Store Last but not least, we need to tell our Vuex data store to use the cafe module. To do this we need to open our `/resources/assets/js/store.js` file first. Right below telling Vue to use Vuex, add the following: ```javascript [Add our cafes module to the Vuex store] /* Imports all of the modules used in the application to build the data store. */ import { cafes } from './modules/cafes.js' ``` This imports our cafes module, we are now ready to register it with the store. To do that, simply add the cafes to the Vuex.Store({}) modules object. Our `store.js` file should look like: ```javascript [store.js] /* |------------------------------------------------------------------------------- | VUEX store.js |------------------------------------------------------------------------------- | Builds the data store from all of the modules for the Roast app. */ /* Adds the promise polyfill for IE 11 */ require('es6-promise').polyfill(); /* Imports Vue and Vuex */ import Vue from 'vue' import Vuex from 'vuex' /* Initializes Vuex on Vue. */ Vue.use( Vuex ) /* Imports all of the modules used in the application to build the data store. */ import { cafes } from './modules/cafes.js' /* Exports our data store. */ export default new Vuex.Store({ modules: { cafes } }); ``` ## Wrapping Up In this tutorial we created a Vuex store and configured a module for our cafes. To see this in the application, visit your development environment and open the developer tools. Switch to the Vue tab (assuming you are using a supported browser) and it should look something like this: ![](https://serversideup.net/blog/build-vuex-module/Screen-Shot-2017-10-11-at-3.32.23-PM-1024x326.png) You can see the state, the module and the getters within. The left side will show all of the mutations run and even allow you to time travel back and forth through your state. The entire Vue ecosystem is beautiful and the developer tools is the icing on the cake! The next tutorial will include a little bit of styling and some functionality on the front end of the app! Make sure to check out the code base here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""} # Building a Queue with Vue 3 and Vuex 4 This is a post I've been wanting to write for some time. We had to implement a client side queue in two of our apps recently using Vue 3 and Vuex 4. Now why would you want a client side queue? Well with so much more power given to the web browsers, there are actually times where you might want to perform a long running task in the browser. For example, with [FFMPEG WASM](https://ffmpegwasm.netlify.app/){rel=""nofollow""} you can actually encode videos directly the browser itself, WITHOUT touching a server! For these scenarios, building a queue is the perfect system to make this work. Since these are still kind of "fringe" scenarios, I wasn't going to go to the trouble right away by making an officially supported package. However, using Vuex with the reactivity of Vue 3, you can make a simple queue system fairly simply. Let's get started! ## Prerequisites I'm going to assume you have Vue 3 and Vuex 4 installed as well. We will step through the configuration of these, but I won't go through everything on how you need to install them. ## Base Concepts The idea of a queue is deferred processing. Essentially meaning, you can place jobs on a queue, they process in order, and you can continue working. When using a queue in Laravel for example, you might want to have the user send a request to an endpoint and return immediately, queuing up the job for later. This allows the user to use your site, get efficient responses, and still have resource intensive processes take place in the background. This will be similar with our client side queue where we want long running processes to run in the background while the user continues to use the app. When you are using Vuex 4, you are already coming from the mindset that you need to have some "system level state". What that means is that you need access to data throughout all pages and components. This usually happens within a Single Page Application (NuxtJS or Vue 3) or an InertiaJS app. Any app that doesn't make a hard refresh between navigation. Or, if you have like one massive page that does a lot of features. An example would be an in browser video editor where it lives on one page and you want the user to export and continue working. What we will be implementing in this tutorial is a simple queue in Vuex 4. With that queue you can push jobs, see the status, be alerted when the job finishes and view job history. This is great for those long running client side processes. If you want to jump right to the code, [check out the repo](https://github.com/serversideup/vue-3-vuex-4-queue){rel=""nofollow""}. Otherwise, let's get started! ## Step 1: Register your store in your Vue 3 app This is the first step to using Vuex 4 to begin with, registering your store. There are many places you can do this, and will need to find where you configure your app's set up. If you installed your app using the `vue-cli`, this will be in your `src/main.js`. Ours looks like: ```javascript [main.js] import { createApp } from 'vue' import { createStore } from 'vuex' import AppLayout from './App.vue' /** * Vuex Queue Set Up */ import { queue } from './modules/queue.js'; const store = createStore({ modules: { queue } }); const app = createApp(AppLayout); app.use(store); app.mount('#app'); ``` There's a few things I'd like to point out. First, we need to import the `createStore` function from `vuex`. This allows us to build our store and register it with the app. Second, I already divided up our queue into it's own module (`import { queue } from './modules/queue.js';`). This is stored in the `/modules` directory. I think this is the most beautiful feature of Vuex. The fact you can break up state into small maintainable modules. The queue will have the full gamut of Vuex module functionality, so breaking it up right away allows us to easily manage the queue. Next, if you installed through the `vue-cli` or have a similar structure, save what is returned from the `createApp()` function to a `const` variable. The `vue-cli` doesn't assign this function's return to anything. We need to do that, because in the next step we need to tell our app to use the store we created: ```javascript [Make sure our store is registered] app.use(store) ``` Finally, make sure you mount the element using `app.mount('#app')` or whatever the ID of your primary element is. If you installed using `vue-cli` this happens right after the `createApp()` method is called. We need to ensure the app uses the store before we mount the element. You are now ready to start and build out your queue module in Vuex! ## Step 2: Build your Queue Vuex 4 Module As I mentioned in the last step, we created a Queue module. This module can live anywhere you want, but I recommend putting it inside a `/modules` directory. This way everything is easy to maintain and easy to find. The first step is to add `queue.js` to `/modules` and give it some scaffolding for all of our Vuex functionality. If you are unfamiliar with Vuex, I'd take the time to [read the docs](https://vuex.vuejs.org/guide/state.html){rel=""nofollow""} before going further. We will be touching on all of the core components. Our `queue.js` module should look like: ```javascript [Define our queue module] export const queue = { namespaced: true, state: () => ({ }), actions: { }, mutations: { }, getters: { } } ``` A quick note, I'd recommend namespacing so none of your modules interfere. Everything will be accessible from the `queue/` namespace within our components. Let's start building out our module. **State** Let's start with the state. In your `queue.js` module, add the following to your `state`: ```javascript [Define the state for our queue] state: () => ({ pending: [], completed: [], active: {} }), ``` This is the guts of our queue. The pending state will be all of the jobs in the queue. As the queue runs, it will move the first job into active so we can see which job is currently being processed. When the job is finished, the active job will move to the completed array and the next job will begin if it's available. **Actions** Now we are going to start getting into the weeds. The actions on your queue control how the queue works and moves jobs from pending, to active, to completed. There will be two actions on this queue, but they are fairly dense. Remember, actions can contain logic, but mutations only adjust state within Vuex. Let's take a look: ```javascript [The core functionality of our queue] actions: { addJob({ commit, state, dispatch }, job ){ commit( 'addPendingJob', job ); if( Object.keys( state.active ).length == 0 ){ dispatch('startNextJob'); } }, startNextJob({ commit, state }){ if( Object.keys( state.active ).length > 0 ){ commit( 'addCompletedJob', state.active ); } if( state.pending.length > 0 ){ commit( 'setActiveJob', state.pending[0] ); commit( 'popCurrentJob' ); }else{ commit( 'setActiveJob', {} ); } } }, ``` The first action is `addJob()` which uses the decoupled `commit` , `state` , and `dispatch` parameters as the first local parameter. The second parameter is `job`. We need the `commit` function so we can perform mutations on our queue which we will talk about next. Then we need the `state` parameter to check if we need to start the next job which we will talk about next. The `dispatch` parameter dispatches the method to start the next job if we have nothing active. Finally, the `job` is the job object we will add to the queue. I'll show you the format of that when we dispatch our first queue job. Right away, we commit the job to pending jobs. We want to make sure that we add it to the queue to keep the order of operations correct. Next, we check to see if we have any active job. If we don't have an active job, we immediately start the job by dispatching the action `startNextJob` . The second action, `startNextJob` is doesn't take any special parameters, just the decoupled `commit` method, and `state`. First, we check to see if there is an active job. If there is, and we are starting the next job, we add the job to the completed jobs array. Next, we check to see if there is another pending job. If there is another pending job, we set the active job to the next job and then "pop" the current job, meaning we remove the job we just set from `pending` to `active`. If there's no next job, we just set the active job to an empty object. Those are the only two core actions you need to use when building your queue! Next up, we will go through the mutations and getters, then on to dispatching our first jobs! **Mutations** There are 4 simple mutations in this queue. The power of using Vue 3 with Vuex 4 is the reactivity. A lot of the hard stuff is solved right out of the box! Let's add the following 4 mutations to our queue: ```javascript [Define the mutations used by our module] mutations: { addPendingJob( state, job ){ state.pending.push( job ); }, setActiveJob( state, job ){ state.active = job; }, popCurrentJob( state ){ state.pending.shift(); }, addCompletedJob( state, job ){ state.completed.push( job ); } }, ``` Alright, so when we set up state, we set up the `pending` and `completed` variables to be arrays. With that, we have a few built in methods that we can use to manipulate these in away to make them act like queue. First, we have the `addPendingJob` mutation. This uses the `.push()` method to push a job on to our `pending` jobs array. Second, we have the `setActiveJob`. This mutation just accepts a job that comes from the `startNextJob()` action and sets it to the active job in our queue. Next, we have the `popCurrentJob()` mutation. Once again, we can use a built in array function to remove the first job in the array, working how a queue data structure would work. We use the `.shift()` which "pops" the first job from the queue. Finally, we have the `addCompletedJob()` mutation. This uses the `.push()` method again, except we push a job, the previous active job, onto the `completed` array that's monitored in our queue. With those 4 mutations, we have a pretty powerful queue handler right there! Finally, let's add the getters and we can move to the next steps! **Getters** There are 3 getters to match the 3 pieces of data in our Vuex store. They are: ```javascript [Define the getters that access queue data] getters: { PENDING( state ){ return state.pending; }, ACTIVE( state ){ return state.active; }, COMPLETED( state ){ return state.completed; } } ``` Each getter works in the Vuex fashion of returning a piece of state. This way we can monitor the queue status from anywhere within our application. Our entire queue module should look like this: ```javascript [Our entire queue Vuex module] export const queue = { namespaced: true, state: () => ({ pending: [], completed: [], active: {} }), actions: { addJob({ commit, state, dispatch }, job ){ commit( 'addPendingJob', job ); if( Object.keys( state.active ).length == 0 ){ dispatch('startNextJob'); } }, startNextJob({ commit, state }){ if( Object.keys( state.active ).length > 0 ){ commit( 'addCompletedJob', state.active ); } if( state.pending.length > 0 ){ commit( 'setActiveJob', state.pending[0] ); commit( 'popCurrentJob' ); }else{ commit( 'setActiveJob', {} ); } } }, mutations: { addPendingJob( state, job ){ state.pending.push( job ); }, setActiveJob( state, job ){ state.active = job; }, popCurrentJob( state ){ state.pending.shift(); }, addCompletedJob( state, job ){ state.completed.push( job ); } }, getters: { PENDING( state ){ return state.pending; }, ACTIVE( state ){ return state.active; }, COMPLETED( state ){ return state.completed; } } } ``` Now that we have our module completed, let's put it to work and dispatch some jobs. ### Step 3: Dispatching Queue Jobs Dispatching a job interacts directly with the `addJob()` action on our queue. That action will handle the movement of our job throughout our queue. Before we dispatch the `addJob` action, let's talk about the structure of our job. The job will be a simple JSON object with two required parameters, an `id` and a `handler()` function. The ID will be used so you can visualize whatever is on the queue throughout your app. The `handler()` bundles the functionality you need to process your job. Before we dive into the `handler()`, I want to point out, you can add whatever you want to the job object that you need to display or use within your app. This object will be present in the `pending`, `active`, and `completed` sections of your queue. Let's take a look at a sample job we can dispatch from anywhere within our app: ```javascript [Dispatch a queue job from a component] this.$store.dispatch('queue/addJob', { id: 'your-job-id', handler: function( ){ // Add functionality and data here // Complete the job. Can also be called from // a promise like when making an API request this.$store.dispatch('queue/startNextJob'); }.bind(this) }); ``` What this does is dispatch a job onto the queue using the `addJob` action. Remember, we namespaced our queue so we have to call `queue/addJob` to actually add the job. The **major** point to note is that our handler method has to have `.bind(this)` at the end. This way we can call the `this.$store.dispatch('queue/startNextJob')` method. This signals to our queue that the active job is completed and we can work on the next job. Within the body of the `handler()` method, you can add whatever you need to process your job. You can include data, API requests, etc. Whenever you are done, just dispatch the `startNextJob` action and the queue will go to the next job! With that being said, we need to add 1 more component within our app, and that's the component to control the queue. ### Step 4: Controlling the Queue Now this component can be very customized. It could even be a mixin that you include on the app level, or through a composition function. For this example, we are just going to throw it in a headless component and include it at the root level of our app. This component simply reads in from the queue and facilitates the processing of the jobs. This is what the component should look like: ```javascript [Control the queue within a component] import { mapState } from 'vuex'; export default { computed: { ...mapState('queue', { pending: 'pending', active: 'active', completed: 'completed' }) }, watch: { active(){ this.processJob(); } }, methods: { processJob(){ if( this.active.handler ){ this.active.handler(); } } } } ``` Since we are operating with `vuex`, we import the `mapState` method from `vuex`. This allows us to view the state right within our component. In the `computed` section, we mapped the `vuex` state locally so we can watch for reactive changes. Once again, we namespaced our `queue`, so we add the namespace as our first parameter. Next, we have the glue that holds this all together, the `watch` method that watches our `active` job. Due to the reactive nature of Vue, whenever there is a change, we can monitor that change. So whenever there is a new active job, we can process it! That's exactly what happens here: ```javascript [Watch the active job] watch: { active(){ this.processJob(); } }, ``` When the active job changes, we call the method to process the job. Next up, we define our only method of `processJob()`. It's super straight forward. If there's a handler on the active job, we call the handler! That decouples the functionality into the job itself so you can have flexible jobs! It also checks to see if the handler is present. This is because if we finish our queue, we reset the active job to an empty object. The reset of the job will trigger the `processJob()` since the active job changed. We don't want an error since the handler will not be defined. That completes the full loop of the queue with Vue (nice rhyme, took me this long to realize that)! Next we will touch on how to display this status anywhere in your app and some optional steps. ### Step 5: Viewing Queue Status So the three pieces of state can be mapped anywhere within your app. All you have to do is make sure the `mapState` method is imported `import { mapState } from 'vuex';` and you map the state locally. From there, you can loop over the pending and completed jobs and/or display the active job and reference keys on that object. For example, you could show pending jobs like this: ```html [Display pending jobs] ``` You can also show the active job, and some data like this: ```html [Show active job]
Processing Job {{ active.id }}
``` Those are very simple examples, but that should show a small example of how you can expand in the future! ## Optional Steps There are a few optional features you can add to enhance the functionality of your queue. ### Clear Pending Jobs If you have a lot of tasks and realize you made a mistake, you might need to clear the pending jobs. Adding an action you can call to clear the pending jobs is extremely useful. All you have to do is add the following action: ```javascript [Clear pending jobs] clearPendingJobs({ commit }){ commit( 'setPendingJobs', [] ); } ``` With the mutation: ```javascript [Set pending jobs] setPendingJobs( state, jobs ){ state.pending = jobs; } ``` Now when you call: ```javascript [Dispatch even to clear pending jobs] this.$store.dispatch('queue/clearPendingJobs'); ``` Your pending jobs will be cleared! ### Clear Completed Jobs Similar to pending jobs, but more likely to occur, is the clearing of the completed jobs. After awhile, these may become irrelevant. Let's add an action that clears these jobs: ```javascript [Clear completed jobs] clearCompletedJobs({ commit }){ commit( 'setCompletedJobs', [] ); } ``` Then add the following mutation: ```javascript [Set completed jobs mutation] setCompletedJobs( state, jobs ){ state.completed = jobs; } ``` Now when you call: ```javascript [Action used to clear completed jobs] this.$store.dispatch('queue/clearCompletedJobs'); ``` All of your completed jobs will be cleared. ### Remove Individual Job This is also possible with the queue system. And super useful if you mess up a job and don't want to re-add everything to make it work. To do this you will need to add the following action: ```javascript [Cancel job action] cancelJob({ commit }, index ){ commit( 'removeJob', index ); } ``` This action takes the index of the job we want to cancel. Next, we need to add the following mutation: ```javascript [Remove an individual job from the state] removeJob( state, index ){ state.pending.splice( index, 1 ); } ``` Once again, we can use another array function, `splice()` provided by Javascript to make this efficient. The first parameter in `splice()` is the index we wish to remove, while the second parameter is how many elements we wish to remove. We only want to remove 1 element at an index, so that's all we need. Now when we want to remove an individual job, we can run the following: ```javascript [Action used to cancel the actibe job] this.$store.dispatch('queue/cancelJob', index ); ``` And our job has been removed! Those are just a few of the other helpful queue functions we can add. ## Conclusion Hopefully this helped show you some of the possibilities with Vuex 4 and Vue 3 to create dynamic systems! If you have any requests or other ideas, let me know. I might mess around with Pinia in the future to see how that works as well. If you want to see the final product, check out the repo. It's very simple, but should demonstrate some of the potential! If you want to learn more about Single Page Application structuring, [check out our book](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). We are in the process of updating it to Vue 3 & Nuxt 3! We cover a lot more information regarding larger Single Page Applications as well as building an API. Any questions, feel free to reach out on [Twitter](https://twitter.com/danpastori){rel=""nofollow""} or on our [community forum](https://community.serversideup.net){rel=""nofollow""}! # Building a Single Song Player So, let's start with a very simple player, a single song player. For this tutorial, I will be building the example player here: [amplitudejs/examples/single-song at master · 521dimensions/amplitudejs · GitHub](https://github.com/521dimensions/amplitudejs/tree/master/examples/single-song){rel=""nofollow""}. All the code is available to download but I'll be walking through the basics as an introduction on how to use AmplitudeJS and play a single song in your browser. ## Pre-Requisites Before we start building our player, we need to have a few assets in order: 1. The newest version of AmplitudeJS here: [GitHub - 521dimensions/amplitudejs: Amplitude.js is the HTML5 Audio Player for the modern era. No dependencies required.](https://github.com/521dimensions/amplitudejs){rel=""nofollow""} 2. A song you want to play (preferably in an MP3 Format) 3. An organized directory you can open in a web browser. You shouldn't need a web server since there's no server side code. If you have a design for your player already, you can follow along with the basics, otherwise feel free to grab the files from the example player as that's what we will be using to step through. ## What We Will Touch On - Getting AmplitudeJS initialized - Play/Pause Button - Song Meta Data - Song Progress Bar - Time Meta Data ## Getting Started With any web development, I like to lay out my folder structure right away so as I build I have places for all of my assets. For this player, I'd open your working directory and make the following files and folders: - `/index.html` - `/css` - `/js` - `/img` If you are doing SASS compiling or any other JS compiling, you might want to add a `/resources` directory and add all of your components in there. In the example player we DO use SASS, but that's outside the scope of this tutorial. We will be walking through the mindset of using AmplitudeJS for building a single song player. As for JS, if you are following along with the example player, I have foundation and jQuery included. AmplitudeJS is not dependent on any 3rd party library so these are not required for any player. I use foundation for just page layout and jQuery for any animation functions. ## Structuring Your Player To start off, set up your `index.html` file like this: ```html [Basic HTML structure for the player] AmplitudeJS Single Song Example
``` A few things to notice here, we included foundation: [The most advanced responsive front-end framework in the world. | Foundation](https://foundation.zurb.com/){rel=""nofollow""} like I mentioned, this is only for layout and helps design the player. I also included a Google Font which makes the player look good. You can include any other styles or fonts if needed. Next, I add a player container which is simply a `
` element that we will be structuring to make our player. ```html [Player container element] ...
... ``` This is not an AmplitudeJS element it's simply a container for the player that we are building. Inside of this element will contain a few AmplitudeJS specific elements. ## Adding Album Art This is our first encounter with an AmplitudeJS specific element. We will be adding our album art image to the player. To do this, right inside of the `id="single-song-player"` element, add the following: ```html [Album art element with AmplitudeJS attributes] ``` What this does is it will show the album art for whatever song is being played. There are two attributes. The first one `amplitude-song-info="cover_art_url"` defines the meta data we should use for the element. In this case, whatever is defined in our song object (discussed later) for `cover_art_url` will be set to the `src` attribute of the image. The next attribute, `amplitude-main-song-info="true"` states that this will show the `cover_art_url` for whatever song is playing. Our player element should now look like this: ```html [Player container with album art] ...
... ``` ## Adding Song Progress A unique element in AmplitudeJS is the song played progress bar. It visualizes how far along the song being played. In a later tutorial I will dive in on some of the specifics and how to style the element. For now, add the following HTML to your player right below the image: ```html [Song progress bar element]
``` You will notice on the progress bar the class of `amplitude-song-played-progress`. This alerts AmplitudeJS that this progress element will display the current progress played in the song. We also have the attribute `amplitude-main-song-played-progress="true"`. This means that whatever song being played this will show the progress for that song. It is the main area to find that information. ## Adding Time Information Below the progress bar, we will add a few time meta data fields. This will give a number read out of how long the song is and how far into the song we are. As discussed in [Working With AmplitudeJS Song Object Metadata](https://serversideup.net/blog/working-with-amplitudejs-song-object-metadata/), there are lots of different time meta data fields. For this player, we are only going to use: - Current Minutes - Current Seconds - Duration Minutes - Duration Seconds You can use whatever you want, I like scoping each into their own span just for full control, but you can use a time stamp or whatever you want. Add the following HTML below the progress bar: ```html [Time metadata elements]
: :
``` The time container that wraps everything is not an AmplitudeJS specific element. It's solely a structural element. As you notice, AmplitudeJS fits right in with structural elements and only binds the information needed to the elements specified. In the code, there are the following classes: \* `amplitude-current-minutes` \* `amplitude-current-seconds` \* `amplitude-duration-minutes` \* `amplitude-duration-seconds` Each of these elements has an attribute `amplitude-main-[WHATEVER-ELEMENT]` (ex. `amplitude-main-current-seconds`). This means that they will display the appropriate data for whatever song is playing since they are main elements. When we dive into playlists and multiple songs per page, we will notice that these are linked to a playlist or a song and will display data for the specific song or the specific playlist. For this example there is only one song, so it needs to be the main data. As with everything we've added, you can apply any styles you want through CSS. The next section, we will add some control elements, these have a few styling guidelines. ## Adding Play Pause Element The Play/Pause button is probably the most important element in AmplitudeJS. It does exactly what it's name states, it either plays or pauses the current song depending on the state of the song. It's a toggle button so the individual element will act differently depending on the state of the player. Add the following HTML below your time elements: ```html [Play/Pause button element]
``` The control container is just a structural element, and is not an Amplitude specific element. We want to focus on the element `amplitude-play-pause`. You can apply a class to ANY element you want and when the user touches or clicks the element, it will toggle play or pause. Before we get into the styling details, I'd like to point out, this is an element with the attribute `amplitude-main-play-pause="true"`. This means it will play or pause on a global level. Whatever song is active whether it's a playlist, individual song, or the only song will get toggled depending on the state of the player. Now, when Amplitude initializes (see Initializing AmplitudeJS), there will be an additional class that gets added to this element called `amplitude-paused`. This is the current state of the player on initialization. When the button is clicked, the class will be removed and the class `amplitude-playing` will be added. These classes allow the developer to style the element based on the state of the player. AmplitudeJS will NEVER add HTML to the page. It will only adjust classes to represent the state of the player. Now a common use case is to use an image to represent the play or pause state of the player. Do not put an image inside of the element!! Instead set it in the CSS as the `background-image` attribute like this: ```css [CSS styling for play/pause button] div.amplitude-play-pause { width: 74px; height: 74px; cursor: pointer; float: left; margin-left: 10px; } div.amplitude-play-pause.amplitude-paused { background: url('../img/play.svg'); background-size: cover; } div.amplitude-play-pause.amplitude-playing { background: url('../img/pause.svg'); background-size: cover; } ``` The reason we won't put the image inside of the element is AmplitudeJS binds a click/touch handler to the element. Javascript might detect the click on the inner element and it won't handle correctly. You can do whatever you want in the CSS and add the style accordingly. ## Adding Now Playing Meta Data Before we initialize AmplitudeJS, let's add the last HTML we need to display the now playing meta data. Below the play/pause button add the following HTML; ```html [Now playing metadata elements]
``` Once again, meta container is not an AmplitudeJS element, but the rest of them are. We are showing the song name and artist. As discussed in [Working With AmplitudeJS Song Object Metadata](https://serversideup.net/blog/working-with-amplitudejs-song-object-metadata/), these elements get populated with the text from the song meta data. Whatever is in the `amplitude-song-info` attribute relating to the song object is inserted in the element. These are also main elements as noted by `amplitude-main-song-info="true"`. This means whatever song is playing, the data gets added to the element. These can be scoped per song, per playlist, or global. So now we have our HTML laid out for AmplitudeJS to bind to! All CSS can be found here: [amplitudejs/examples/single-song at master · 521dimensions/amplitudejs · GitHub](https://github.com/521dimensions/amplitudejs/tree/master/examples/single-song){rel=""nofollow""} along with the code that we stepped through. Next up is initializing AmplitudeJS! ## Initializing AmplitudeJS Now that we have our HTML set up, it's time to activate it! This is done with the `Amplitude.init()` method: [Initializing AmplitudeJS](https://serversideup.net/open-source/amplitudejs/docs/){rel=""nofollow""}. The `Amplitude.init()` method initializes AmplitudeJS with a variety of different configuration options, callbacks, songs, playlists, etc. The most important being the songs. Now you don't HAVE to even add a song to AmplitudeJS on configuration, you can dynamically load them if needed. However, in this case, we we will be initializing with one song. The `Amplitude.init()` method takes a JSON object as it's only parameter in there, you can define everything you need. Let's add the following code to the bottom of the `index.html` file: ```javascript [AmplitudeJS initialization code] ``` A few things to note. First, this method runs when it's called. It does not wait for a ready function or anything else. That means it should be called at the end of your document or wrapped inside a document.ready() method! If you call this method before you have your HTML, it won't bind to any elements and it won't do anything! Now, you can call `Amplitude.bindNewElements()` if your display changes and you need to add more AmplitudeJS elements. We will discuss that in a later tutorial. Next, the songs are in an array. This is important to note because a lot of AmplitudeJS functions reference a song index. The only song we have in the array right now has an index of 0. This is programming so the first object starts at 0 and goes up. When creating playlists, these indexes will be used to create the playlist. You can see in the song object, there's a lot of meta data. The only required piece is the `url`. This points to the song that will be played by AmplitudeJS. That's it! By calling `init()` AmplitudeJS has bound all of the click/touch handlers to the elements and has loaded a song. The code from GitHub includes a few other pieces of JS that we will dive more in depth on in later tutorials. You can do cool things like bind key events to control AmplitudeJS on the keyboard and other click handlers. Let me know if you have any questions or want me to explain something differently in the comment section below! # Building a Page Layout for Vue Router One thing that comes in real handy with any app is the ability to add layouts to your application. Laravel does that with the Blade templating system: [Blade Templates - Laravel - The PHP Framework For Web Artisans](https://laravel.com/docs/5.6/blade){rel=""nofollow""}. However, since we are doing a Single Page Application, doing that with Vue Router is a little bit of a trick. The way I like to do it is create a root level page that contains the Vue components present on every page, like Header and Footer. The other advantage of using a layout, is you can load all of the Vuex data you need once and it will be present on all of the child pages, which in this case, is our entire app. ## Step 1: Restructure To Nested Routes In the tutorial where we set up our `/assets/js/routes.js` file: /configuring-vue-router-single-page-app/ we didn't include any nested routes. The nested feature of Vue Router is the trick that I use to make a layout. For more information on nested routes, check out: [Nested Routes · vue-router](https://router.vuejs.org/en/essentials/nested-routes.html){rel=""nofollow""} First we will build our top level route which will be our Layout. To do this, add the following route object to your routes file: ```javascript [Top level route configuration] { path: '/', name: 'layout', component: Vue.component( 'Home', require( './pages/Layout.vue' ) ), } ``` We will be adding the Layout.vue in the next step. Now we will add a `children` key that will contain the array of routes that are nested routes. We should have a routes.js file that looks like: ```javascript [routes.js] /* |------------------------------------------------------------------------------- | routes.js |------------------------------------------------------------------------------- | Contains all of the routes for the application */ /* Imports Vue and VueRouter to extend with the routes. */ import Vue from 'vue' import VueRouter from 'vue-router' /* Extends Vue to use Vue Router */ Vue.use( VueRouter ) /* Makes a new VueRouter that we will use to run all of the routes for the app. */ export default new VueRouter({ routes: [ { path: '/', name: 'layout', component: Vue.component( 'Layout', require( './pages/Layout.vue' ) ), children: [] } ] }); ``` Now we will take the existing routes and add them to the children array to get a file that looks like: ```javascript [routes.js] /* |------------------------------------------------------------------------------- | routes.js |------------------------------------------------------------------------------- | Contains all of the routes for the application */ /* Imports Vue and VueRouter to extend with the routes. */ import Vue from 'vue' import VueRouter from 'vue-router' /* Extends Vue to use Vue Router */ Vue.use( VueRouter ) /* Makes a new VueRouter that we will use to run all of the routes for the app. */ export default new VueRouter({ routes: [ { path: '/', name: 'layout', component: Vue.component( 'Layout', require( './pages/Layout.vue' ) ), children: [ { path: 'home', name: 'home', component: Vue.component( 'Home', require( './pages/Home.vue' ) ) }, { path: 'cafes', name: 'cafes', component: Vue.component( 'Cafes', require( './pages/Cafes.vue' ) ), }, { path: 'cafes/new', name: 'newcafe', component: Vue.component( 'NewCafe', require( './pages/NewCafe.vue' ) ) }, { path: 'cafes/:id', name: 'cafe', component: Vue.component( 'Cafe', require( './pages/Cafe.vue' ) ) } ] } ] }); ``` Now we will add our Layout.vue page. ## Step 2: Add Layout.vue In the `/resources/assets/js/pages` directory add a file called `Layout.vue`. This will be our Layout file where we can add our Navigation component discussed in our last tutorial. Stub out the component like this: ```vue [Layout.vue] ``` If you check out `` that component is built into Vue Router which renders the child component inside of the parent component. It's like a place holder for the nested component. We now have our layout, so let's import our `Navigation.vue` component so you can see the power of the layout. We just need to add the following line before we export the default component: ```javascript [Navigation component import] import Navigation from '../components/global/Navigation.vue'; ``` That will include our Navigation component, we then need to tell our page to use it by adding it to the `components` object on the page: ```html [Adding Navigation to components] ``` Finally, we add the component to our template before the `` component so our layout looks like this: ```vue [Layout.vue] ``` What this means is now the Navigation component will appear on all pages since every page is a sub page of the layout. Kinda slick eh? Let's load the data we need on every page. To do that, since we have Vuex ready to rock and roll: [Build a Vuex Module](https://serversideup.net/blog/build-vuex-module/), we simply bind to the created() hook and load some data! This case, we know we want a user in the navigation component so we can display a profile picture, as well as loading the cafes. When we load it on the parent component, and since we are using Vuex, the user will be available to ALL routes. I didn't write a tutorial about writing everything we need to load the user, but if you followed along with this series, it's follows along the same steps as adding a cafe module, routes, models, etc. Of course all of the code can be found here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""} . Our Layout.vue component should look like: ```vue [Layout.vue] ``` When created, the layout loads the cafes and user that's currently logged into the app. ## Step 3: Adjust the Authentication Controller Redirect This is the last step of adding a Layout to the app. Since now the top level link is our layout, we have to direct users to our `/home` route upon login. To do this, open up the `/app/Http/Controllers/Web/AuthenticationController.php` and find the `getSocialCallback()` method. At the end of the method adjust the redirect from ```php [Original redirect code] return redirect('/'); ``` to ```php [Updated redirect code] return redirect('/#/home'); ``` Now your users will be redirected to the home page on login. ## Conclusion Now that you have your layout created, you can add any sort of elements you want to appear on each page. I've added an AppFooter.vue as another example as a component that should be on every page. You can find it in the repository: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""}. There are other ways you can pull this off if needed. One would be if you don't want to access Vuex data, then you could add header and footer into your `app.blade.php` file and you can load the Vue data on a `
` element between the header and footer. I just prefer to do it this way since we can load global data and access Vuex information. # Caching API Endpoints with Laravel As your API begins to grow, you will want to start looking into some performance optimizations. Laravel provides a [variety of different options](https://laravel.com/docs/9.x/cache){rel=""nofollow""} when it comes to caching endpoints, such as Redis, Memcached, DynamoDB, and even simpler file caches. By default, Laravel is set to cache in a file. Since Laravel is facade driven, basic caches don't matter from a programming perspective. The method to actually cache data is the same and will use what you have configured for your environment. However, you will get better performance using a high-availability service like Redis or Memcached as your app grows. ## Why Cache API Endpoints? Speed and performance. That's why you'd want to cache an API endpoint. Say you have a database with thousands or millions of records. Every time you hit an endpoint to load data, you are querying the database to load the records you need. Throw a bunch of users on your system and you will get massive performance issues. If you cache pre-calculated data and update the data in your cache when it changes, you will have a much better performance! You can get infinitely complex with how much you cache, but try to take the low hanging fruit first. By that, I mean say you have an endpoint that rarely gets updated, but has a lot of requests and a lot of data computation. Those are the perfect places to begin where you will see the biggest performance increase. For this tutorial, I'll be going through a real life example of an app I'm working on called Amplitude Hosting that works with [AmplitudeJS](https://github.com/serversideup/amplitudejs){rel=""nofollow""}. Amplitude Hosting allows you to create a config file to load into your Amplitude player through a URL. The endpoint can get complicated to compute, but doesn't change very often. We want to load it through a URL structured like `/api/v1/configs/{key}`. Our end game being to return a JSON object with our config pre-computed and up-to-date resource from the cache. All without touching our database. Let's get started! ## Caching a Resource Endpoint The first step is to save your resource to a cache, otherwise you will have nothing to return! Let's pretend we just persisted our Amplitude Config to the database. After you persist your resource to the database, then it's time to cache it. As I mentioned previously, we will be using the `Illuminate\Support\Facades\Cache` facade provided by Laravel to work with our cache. This facade will work with whatever caching system we have in place. So in our controller or service, include the following in your use statements: ```php [Import Cache Facade] use Illuminate\Support\Facades\Cache; ``` I like to keep my controllers small, so I created a service that saves our config. Right now, it looks like: ```php [Initial Config Service Class] data = $data; } public function persist() { $config = $this->saveToDatabase(); return $config; } private function saveToDatabase() { // Save config to database } } ``` Let's add our cache functionality now: ```php [Config Service with Cache Implementation] data = $data; } public function persist() { $config = $this->saveToDatabase(); $this->persistInCache( $config ); return $config; } private function saveToDatabase() { // Save config to database } private function persistInCache( $config ) { Cache::forever('config_'.$config->unique_id, $config); } } ``` Two things to note. First, we persist to the cache after the resource has been saved to the database. This way we can return the resource exactly how it would appear from our database, except without the query. Second, in our `persistInCache()` method, we call the `Cache::forever()` method on the `Cache` facade. The `Cache::forever()` method accepts 2 parameters. The first parameter is the unique id of the key within the cache. In this case it's `config_{unique_config_id}`. This way we know how to reference the key when returning from the cache. The second parameter is the `$config` itself. This is the data we will return from the cache. Since we used the `Cache::forever()` method, the cache will remain until it's explicitly deleted. This works perfect for our use case. There are actually a few methods you can call depending on your use case. `Cache::put()` allows you to define a `key` and a `value` but also accepts a third parameter of `expiration_time` . The `expiration_time` can be either the amount of seconds the value should live in the cache or the `DateTime` instance of when the cache should expire. The other option is `Cache::add()` which is the same as `Cache::put()`except only adds if the value does not exist. You can read more about the [methods here](https://laravel.com/docs/9.x/cache#storing-items-in-the-cache){rel=""nofollow""}. Now that we have our Config stored in the cache, let's return the value! ## Returning Data from a Cached Endpoint with Laravel The simplest way to return a value from the cache is to use the `Cache::get()` method. The `Cache::get()` method accepts the key of your cached item and returns the value. So in our case, on our `/api/v1/configs/{key}` endpoint, we would return the value at `Cache::get('config_{key}')`. Our service to load the config from the cache could look like this: ```php [Basic Cache Loading Service] key = $key; } public function load() { return Cache::get('config_'.$this->key); } } ``` The service accepts the key of the config and loads it dynamically from the cache without touching our database (we did not update the typehint for the `$config` to load from the `$key` variable or it would have hit our database). This is great, but there are instances where your cache is out of sync like after a reboot, or you need to rebuild and the value may not exist. So I like to return my cached values like this: ```php [Advanced Cache Loading with Fallback] key = $key; } public function load() { if( Cache::has('config_'.$this->key) ){ return Cache::get('config_'.$this->key); }else{ return Cache::rememberForever('config_'.$this->key, function () { $config = Config::where('unique_id', '=', $this->key) ->first(); return $config; }); } } } ``` It looks slightly more complex, but it's actually fairly simple and elegant, let's dive in! First, we check to see if the `Cache::has()` a config with the key specified. If it does, we simply return the config at that key. Second, if there is no config at the key, we run the `Cache::rememberForever()` method, passing the key and the config function. This is an EXTREMELY helpful method. The first parameter should look familiar since it's the key that we want to cache the value at. The second parameter is a callback function that loads the value. In this case, it's our Config resource. After we load the value, set the value at the key and return what was loaded from the database. Yes, it's a database call, but it only happens once so any sub-sequent requests will be cached appropriately! This way you cache is never out of sync! Unless, you update the value of the config, in that case, see the next section. ## Updating a Cached Endpoint with Laravel In our scenario, configs aren't updated often, but they are updated. When they are updated, we need to "re-prime" the cache with the latest and greatest data so it's ready to go. It's pretty simple. After you persist the updates to the database you can prime the cache with the same method as saving the cache. It will just overwrite the value: ```php [Cache Update Example] Cache::forever('config_'.$this->config->unique_id, $this->config ); ``` Now when we call our endpoint, `/api/v1/configs/{key}`, we will get the latest version of the config without calling from the database. This will speed up responses dramatically. ## Deleting a Cached Endpoint in Laravel Say you changed your structure and no longer want to save a cached config. Or the user deletes the config and you don't want it cached any more. All you have to do is call the `Cache::forget('config_{key}')` method wherever you want to delete from the cache. This will remove the the value from the cache and you are good to go! ## Conclusion Some other benefits of caching endpoints with Laravel is it's so easy to switch caching services. Just swap out your config and your cache will go somewhere else without updating the code. You might have to "re-prime" your new cache but at least you won't have to re-factor a ton of code. You can also cache values that may not be endpoints, but take a long time to compute. For example, we are working on "Net Worth Over Time" and monthly stats with [Financial Freedom](https://github.com/serversideup/financial-freedom){rel=""nofollow""}. Those can take a long time to compute if you have a ton of transactions. However, we can compute them when updated and save them to a cache to load them instantly when needed. You can even cache them after a queue process so your app is smooth and responsive! Hopefully this helped shed some light on how to speed up API endpoints with Laravel and Caching. Any questions, reach out on Twitter ([@danpastori](https://twitter.com/danpastori){rel=""nofollow""}). Also, check out our [book on API Development](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). We have a ton of tricks and real world code available and the API is 100% Laravel! # Capturing an image from an HTML5 Canvas or Video Element Ever want to save your HTML5 canvas element as an image? You totally can! I've done this a few times to solve a variety of different issues. With this trick you can capture a screenshot from a video, save a rendered graphic, pretty much anything within the canvas element. ## Step 1: Make a Reference To your Canvas Element There are two approaches to this. First, you may have a canvas element that actually appears on the screen. It will live within your HTML and will display on the screen. The second approach is to have the canvas element represented simply as a javascript object. I've used this approach to capture screen shots of HTML5 video elements. If your canvas is within HTML, make a reference to it in your Javascript like this: ```javascript [Reference canvas element from HTML] let canvas = document.getElementById('screenshot-canvas'); ``` You can obviously use whatever reference way you choose to select your canvas from the screen. You can also just have a canvas object behind the screen that can be used to capture what's within. I just set it up like this: ```javascript [Create canvas element programmatically] let canvas = document.createElement('canvas'); ``` ## Step 2: Capturing your Canvas Element as an Image Here's where the magic takes place. I create a method that captures the element like this: ```javascript [Convert canvas to image data URL] let image = canvas.toDataURL('image/jpeg'); ``` With `canvas` being the variable of your HTML5 canvas element we set up in step 1. That's all there is to it! You now have a capture of whatever is in the canvas as an image! You can set the `image` variable to the `src` of any image element and you will be good to go. One thing to note. The size of your image will be the size of your canvas. This is much easier to see if the canvas is on your screen. However, if you are using a Javascript object, you need to set the `width` and `height` of the element like so: ```javascript [Set canvas dimensions] let canvas = document.createElement('canvas'); canvas.width = 1920; canvas.height = 1080; ``` ## Taking a Screenshot of an HTML5 Video Element One of the coolest features of this script is you can take a screen shot of an HTML5 video element using this as well. You just have to pipe the video to the canvas element. Just a heads up, this will work better if the canvas you are referencing is a javascript object, otherwise you will have 2 videos on your screen. To do this, set up a reference to your HTML5 Video Element. I'm going to assume this element lives on your page. You can however, do the same object instantiation of the video element and hide both the canvas and video element and just output a screen shot. If you are interested in those examples, reach out on the [community forum](https://community.serversideup.net){rel=""nofollow""}. Let's first set up the reference to our HTML5 video element: ```javascript [Reference video element] let video = document.getElementById('my-video'); ``` Once again, you can use whatever method you want to reference your video. We just need a reference to the element. Now that we have our video, let's tie everything together. For best results, I'd recommend keeping the aspect ratio of your canvas to the size of your video. It doesn't have to be the full size if you want smaller, scaled images, but you also don't want any funky resizing. ```javascript [Capture video frame to canvas] let canvas = document.createElement('canvas'); let video = document.getElementById('my-video'); canvas.width = 1920; canvas.height = 1080; let ctx = canvas.getContext('2d'); ctx.drawImage( video, 0, 0, canvas.width, canvas.height ); let image = canvas.toDataURL('image/jpeg'); ``` That's all there is to it! The meat of the script is to create a "2D" context and draw an image to that context of the video. This will take whatever frame is currently being displayed on the video and capturing it as an image. ## Capturing an HTML5 Video Frame at a Timestamp If you want to capture a frame at a certain timestamp in your video, there's one little gotcha. You have to use the `seeked` callback to make sure your video has finished seeking before you capture the frame. All you have to do is modify your code to look like this: ```javascript [Capture video frame at specific timestamp] let canvas = document.createElement('canvas'); let video = document.getElementById('my-video'); let image = ''; video.addEventListener('seeked', function(){ canvas.width = 1920; canvas.height = 1080; let ctx = canvas.getContext('2d'); ctx.drawImage( video, 0, 0, canvas.width, canvas.height ); image = canvas.toDataURL('image/jpeg'); }); video.currentTime = TIME_IN_SECONDS; ``` Now whatever you set for the current time on the video in seconds, the video will seek to that location and take a snap shot. Hopefully this helps! It's kind of a neat feature of the HTML5 canvas element that you can save it as an image. If you have any questions, definitely feel free to ask on the [community forum](https://community.serversideup.net){rel=""nofollow""}, I'd be happy to help! # Choosing Your Services and Database The goal of creating a self-hostable application is, well, allowing it to be self-hosted. That means also allowing *others* to self host your app. It has to be straight forward to do and maintain. As mentioned in the previous article, [Importance of Repeatable Environments](https://serversideup.net/blog/importance-of-repeatable-environments/){rel=""nofollow""}, we use Docker for everything and recommend you do the same. However, there are still some considerations when choosing the database and what services you include. ## Basic Considerations The power of Docker is amazing. You can add an extremely powerful full text, Meilisearch container to your project with minimal set up time. Configure your Docker compose file and you are off to the races. However, that Meilisearch container requires a lot more resources, disk space, and maintenance. Even if you are licensing your self-hostable app to a business, it might be overkill for what they are using. They might only have 10-15 users and want the quickest solution to get up and running. But that's one consideration. What if you want to re-use the same code for a cloud hosted version that does have a million users? These type of architecture and user decisions really have to come into play when designing your app. So what do you do? Well, there are options. ## Service Options (Queues, Search, etc.) The consideration for full text searching is similar for other processes as well. When we approached designing [Bugflow](https://bugflow.io){rel=""nofollow""} we ran into this issue with our queues. We wanted to keep our self-hosted version as small as possible so it's easier to distribute, maintain and deploy on their own hardware. Why? Because the use case for our customers who want to self-host won't run into the same usage scenarios as we do (if they do, we offer an enterprise self-hosted version). ### Architecture For reference, before moving forward, we use [Laravel](https://laravel.com){rel=""nofollow""} to build our apps, and they make these next steps a breeze. All connection information for services is handled through the ENV file. I'm sure other frameworks have this as well, but the beauty comes from the abstraction layer above. If we dispatch a queue job, like we do for every feedback request, Laravel reads the configuration connection and dispatches the job where it needs to go. If you wanted to use SQS, Horizon (Laravel's powerful queue runner), or just a database, the job gets routed where it needs to go. If you aren't using a framework that supports this architecture, I'd recommend developing something similar for your app. I'd recommend a data transfer object, a contract, and some env configuration that allows you to easily swap out what service you need depending on infrastructure and usage requirements. ### How We Solved This With Bugflow, on our cloud hosted version, we need to process multiple simultaneous queue jobs to ensure everything gets processed as quickly as possible. For self-hosted instances, we just use the database. This is all managed from our environment variable. That means we aren't bringing up an unnecessary server for a use-case that our customers don't have. It also means that users paying for our SaaS get the benefit of speedy feedback on our Horizon Queue. You can also solve this similarly for full text searching depending on the database you choose (see next section). ## Database Options With Docker, the database is, guess what, another service and container! This means that when you bring up your app, you might have to bring up another service as well. However, the database is slightly different and depending on your use case, you could possibly remove the container entirely. Let's say you want to get an application as small as possible to make it as simple as possible to distribute. You need full text search, queuing, and a database. Forget cloud for now, you can do whatever you want since you manage the infrastructure, let's focus on self-hosting. The first thing I'd do is move all queued tasks to a "jobs" table on the database. This will eliminate the need for a queue runner and another overkill container. Next, I'd choose PostgreSQL or another database that supports full text indexing. You can then eliminate the huge overhead of another full text search engine running in your app. This is just a consideration! If your app heavily relies on natural language processing or super advanced indexing, this might not be worth it. However, you'd be surprised how far you can get! If you really want to cut down and won't have an app that requires an insanely powerful database, you can even use SQLite. This removes the need entirely for even a database container! You could literally get down to deploying just your app code to self-hostable instances. We actually do this with Bugflow as well. On self hosted instances, SQLite runs our database and our queues. It makes our deployment extremely light weight and easy to run anywhere. ## Building for Cloud and Self-Hosted Now you've heard me talk about how we deploy to self-hosted environments and to cloud using the same code base. How does this work? Well, if you use Docker, it's a lot easier than you may think! The most important pre-requisite to make your life easier is to allow your databases and services to be configurable and not hard coded. I mentioned this earlier, but it's even more important if you want to re-use your code and scale in the cloud while also keeping your footprint small in a self-hosted environment. So how we do it with Bugflow and Docker is straight forward. All the configuration for our services and databases is held in the environment variables that are required for the app to run. The actual server configuration is set up in our `docker-compose.yml` file. This means, when we set our queues to run on Laravel Horizon, we can configure the service to run on Horizon container in production. The Horizon container comes on line when we bring up our app. In the self-hosted version, we just have a database configuration file. We set our queues to run in the database. The combination of what services get deployed + how we access them are all managed through the environment variables and what gets brought up through Docker! ## Need A Hand? If you have questions about how to architect your application to make it easy to self-host [or want us to do it for you](https://serversideup.net/hire-us){rel=""nofollow""}, feel free to reach out. We've done the same approach for multiple clients that we've worked with and are well-versed in the process. Hop on [Discord](https://serversideup.net/discord/){rel=""nofollow""} and we'd love to lend a hand! # Cisco IOS Command Cheat Sheet for Routers and Switches I've been receiving numerous questions about programming Cisco Routers and Switches. This document contains my notes from when I had to program a Cisco Router running Cisco IOS. While primarily focused on routers, most commands are also applicable to switches. The commands in this cheat sheet are organized into two modes: - Enable mode - Config terminal mode Make sure to use the commands according to the mode you're currently in. This Cisco IOS Command Cheat Sheet is valuable for both advanced users and beginners. If you have any questions, please leave a comment! ## Enable Mode Commands | Command | Description | | ------------------------------------ | -------------------------------------------------------- | | `write erase` | Erases config file | | `reload` | Reloads config files | | `enable` | Enters console mode | | `config t` | Enters config mode | | `clock set 10:50:00 Oct 26 2009` | Sets clock to 10:50AM and the date to October 26, 2009 | | `show ip interface brief` | Shows interfaces | | `copy running-config startup-config` | Copies running config and saves it to the startup config | ## Config T Mode Commands | Command | Description | | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | | `enable secret ` | Sets enable secret to password of choice | | `enable password ` | Sets enable password to password of choice | | `line con 0`:br` password `:br` logging synchronous`:br` exit` | Sets console password to password of choice and prevents router from displaying annoying messages | | `line aux 0`:br` password `:br` exit` | Sets AUX port password to password of choice | | `line vty 0 4`:br` password `:br` exit` | Sets Telnet port password to password of choice | | `service password-encryption` | Enables password encryption | | `hostname ` | Sets name of router to name of choice | # VueJS Route Security and Authentication In our last tutorial [Public and Private API with Laravel](https://serversideup.net/blog/public-private-api-laravel/), we converted the Laravel API Backend to be accessible for both an authenticated user and an unauthenticated user. This tutorial will go through the process of securing pieces of data that we don't want the user to to view if they are not authenticated. Before we make any crazy claims it's very possible for users to just inject Javascript and tweak all sorts of things. We don't want to rely 100% on the security of front end JS to secure our data. We just want to block actions from users. Our server and API will handle the hardcore security like preventing users from inserting or updating data. ## Step 1: Add Redirect so Layout is Never in View We are using the `/` route in VueJS to act as a global layout for all of our other routes. However, this is still a navigable route. We want to redirect this to our cafes home page. Open up the `/resources/assets/js/routes.js` file and add the redirect key to your `routes` object underneath the path we are using for layout: ```javascript [Route configuration with redirect] { path: '/', redirect: { name: 'home' }, name: 'layout', component: Vue.component( 'Layout', require( './pages/Layout.vue' ) ), children: [ ``` Now whenever anyone visits this page, they are redirected to the home screen. ## Step 2: Create Modal for Logging In Since we removed the login route from the PHP side in the last tutorial [Public and Private API with Laravel](https://serversideup.net/blog/public-private-api-laravel/), we are now allowing our social login through a modal that we can call from anywhere. This way every thing is contained in our single page application. First, let's create the `/resources/assets/js/components/global/LoginModal.vue` component and enter the basic component stub: ```vue [Basic LoginModal component structure] ``` This component will essentially just provide links to our `/login/{social}` url. The structure, we will have copied from our now deleted `login.blade.php`. First, let's fill out the template with: ```vue [LoginModal template with social login links] ``` What this does is wrap a `login-box` element with a `login-modal` element. Each link in the login box allows the user to log in with whatever social network that they want similar to what we had before. One thing to note about those links is they have an attribute of `v-on:click.stop=""`. What this does is stop the propagation so the event stops after the user clicks. This is because the `login-modal` will close on a click and we don't want the modal to close on a child click. Now let's add our functionality: ```javascript [LoginModal component script with event handling] ``` The two important features of the functionality are 1. The `show` variable in the `data()` and 2, the `prompt-login` event we listen to. The `show` data allows us to toggle the show/hide of the modal. When set to `true` the modal is shown. When set to `false` the modal is hidden. The `prompt-login` event we listen to toggles the `show` to either true or false to show the login. Now to style the form, we will use some of the same styles as we did on the `login.blade.php` . Add these styles to the ` ``` ## Step 3: Set Up LoginModal.vue Now that we got our login modal created we need to set it up. First open up your `/resources/assets/js/pages/Layout.vue` file. We will be importing our login modal here so add to the top of the component script: ```javascript [Layout component imports] import LoginModal from '../components/global/LoginModal.vue'; ``` Then we will need to register it in our `components` object: ```javascript [Layout component registration] components: { Navigation, AppFooter, LoginModal }, ``` and place it right before the `` component in the template: ```vue [Layout component template] ``` We placed it in the `Layout.vue` template so we can call the login screen from any part of the app. Now we need to open the `Navigation.vue` component. In this component, we will display a login link if the user isn't logged in. In the template of this component, adjust the code for the right side navigation: ```vue [Navigation component template with conditional rendering] Logout ``` What this does is determine if we should display the avatar if the user is logged in and a logout button or if we should display the login button. We also need to import the `EventBus`: ```javascript [Navigation component EventBus import] import { EventBus } from '../../event-bus.js'; ``` This way when the user clicks the `login()` link, we can emit the `prompt-login` method which opens our login modal. Our `login()` method looks like: ```javascript [Navigation component login method] login(){ EventBus.$emit('prompt-login'); }, ``` We also added a logout method that dispatches an action to our Vuex state to clear any data referring to the user. Our logout method looks like: ```javascript [Navigation component logout method] logout(){ this.$store.dispatch('logoutUser'); window.location = '/logout'; } ``` When we visit the `/logout` route our Laravel side of the application will log out the user and redirect them back to the application. We can also listen for the `logoutUser` event in any Vuex module and clear any data we don't want saved after the user has logged in. I added the action to `/resources/assets/js/modules/users.js`: ```javascript [Vuex logout user action] /* Logs out a user and clears the status and user pieces of state. */ logoutUser( { commit } ){ commit( 'setUserLoadStatus', 0 ); commit( 'setUser', {} ); } ``` ## Step 4: Adjust Views Based On Authentication There are a few views we have to adjust based on whether the user is authenticated or not. We need to hide the add button on the cafes homepage, the edit but on the individual cafe page and the like button on the individual cafe page. First, let's open our `Home.vue` component. At the top of the page, adjust the template to look like: ```vue [Home component template with conditional rendering] + Add Cafe Want to add a cafe? Create a profile and add your favorite cafe! ``` What this does is hide the add cafe button if the user is not authenticated. Now this doesn't prevent the user from typing in the URL and visiting the page. We will be dealing with Vue Router Navigation Guards in the next tutorial. Now let's open the `Cafe.vue` page. I wrapped the `Edit` link in a container and hid it if the user is not logged in: ```vue [Cafe component edit link with conditional rendering]
Edit
``` I also added a toggle for the like button if the user is not logged in. It prompts the user to log in by dispatching the event to login to the Login modal. Our template update should look like: ```vue [Cafe component like button with conditional rendering] ``` and we should add the `login()` method to the methods object on the page: ```javascript [Cafe component login method] /* Defines the methods used by the component. */ methods: { login(){ EventBus.$emit('prompt-login'); } } ``` This way we will be able to show the login modal when we click the link to log in. ## Conclusion We now have cleaned up some of the VueJS front end and accounted for both public and private routes, we will add some navigation guards in the next tutorial. This will make sure that users do not navigate to pages they don't have access to when they are not authenticated. This combined with the changes we've made on the Laravel side of things make sure no data is leaked. Even if they were to inject javascript to view a page to add a cafe, we already block the API from adding the cafe if submitted. Be sure to check out all of the code here: [GitHub - serversideup/roastandbrew: Helping the coffee enthusiast find their next cup of coffee. Also, helping aspiring web and mobile app developers build a single page app and converting it to a mobile hybrid. All tutorials can be found at https://serversideup.net](https://github.com/serversideup/roastandbrew){rel=""nofollow""} # Collections, Blueprints, and Entries with Statamic 3 To be up front, this tutorial is guided at users who are entirely new to Statamic. The documentation on all of these concepts is incredible. Where I stumbled was just tying everything together. I'm also coming from a WordPress background and it really being the only CMS I know. I've been developing Laravel apps for about 8 years now and is one of the reasons I was so interested in Statamic. So what my intent is with this tutorial is to explain these aspects through my understanding. I will relate these to anyone coming from a WordPress background to hopefully bridge that gap. ## Collections Let's start with collections. The [official documentation](https://statamic.dev/collections){rel=""nofollow""} is beautiful and well laid out. I'm going to explain how I grasped this concept coming from a WordPress background. Coming from WordPress, these would be your "posts" or custom post types. In WordPress, you get a few post types out of the box such as Posts and Pages. However, when switching to Statamic, you won't get these defaults, unless you follow the [Quick Start Guide](https://statamic.dev/quick-start-guide){rel=""nofollow""}. Then you will have a "Pages" collection. If you just install Statamic to a Laravel install, you won't have any collections by default. That's the route I took when I first started Statamic. ![Empty Collections screen within Statamic](https://serversideup.net/blog/collections-blueprints-and-entries-with-statamic-3/collections-screen-1024x573.png) So in order to have a place to write a post, article, tutorial, etc. you have to create a collection to hold those entries (what we will talk about next). My mindset when approaching a Statamic site is you are given a blank canvas where you can easily design the structure of your data, how it relates to each other, and how to display it. With WordPress, you get some out of the box, pre-configured ideas and you can use those to build what you need. If I was going to re-build this blog (which is currently running WordPress) in Statamic, the first step I'd take is to create a collection called "Tutorials". Currently, We have a Post "post type" by default in WordPress where each of these tutorials live. Tutorials is more explicit to what we will be writing. We use a "Post" because that's what we get out of the box with WordPress. ![Create a new collection, in our case "Tutorials"](https://serversideup.net/blog/collections-blueprints-and-entries-with-statamic-3/create-collection-1024x502.png) Now that we have our collection, let's create some entries. ## Entries Entries belong to collections. Or, from the [official docs](https://statamic.dev/collections){rel=""nofollow""}, "Collections are containers that hold groups of related entries." Entries in Statamic are the equivalent to an individual Post in WordPress. If you are really familiar with WordPress you could have created custom post types. An individual creation of a custom post type in WordPress is the equivalent to an entry in Statamic. The custom post type itself would be the collection. When creating your first entry, you should see a few basic fields: ![Create a new Entry within Statamic](https://serversideup.net/blog/collections-blueprints-and-entries-with-statamic-3/create-entry-1024x419.png) Almost inception like, isn’t it? For this tutorial, I’m going through the process of updating Server Side Up with Statamic as an experiment. Compared to what you get out of the box with WordPress, there are minimal fields. This is where the platforms really diverge and where I feel Statamic becomes EXTREMELY powerful. Yes, you only have a title, content, slug and status (published) as editable fields right away with Statamic. However, if you want to add fields super easily, you just have to adjust the blueprint (we are going there next). To add a field to a post, or custom post type in WordPress, you’d have to edit the theme code or install the Advanced Custom Fields plugin and create your extra field. With Blueprints, you just have customization options that are incredible and extremely powerful. Let’s take a look at those. ## Blueprints If collections are equivalent to "posts" or custom post types in WordPress. And Entries are the individual post that belongs to a custom post type in WordPress. Blueprints would be the custom meta fields added by your custom theme, custom plugin, Advanced Custom Fields plugin, or other 3rd party extension. To add a custom field to a post type, or even make a custom post type, requires a lot of code and proper hooks into the WordPress loop. Then you have to handle the updating of this data and loading it in your template. With Statamic, if you want to add a custom field, you need to edit the Blueprint. There are two ways to edit your blue print. First is on the left side bar by clicking "Blueprints" and finding the collection blueprint you’d like to edit. Otherwise, if you are on your collection, select "Edit Blueprints" from the dropdown. ![Where you can edit Blueprints for a Collection.](https://serversideup.net/blog/collections-blueprints-and-entries-with-statamic-3/edit-blueprint-1024x432.png) From there, you will be brought to a list of the available Blueprints in your Statamic instance. If you click on the name of the collection, in our case "Tutorials", you will see a page where you can layout what the user will see when adding an Entry to your collection: ![Screen to edit blueprints.](https://serversideup.net/blog/collections-blueprints-and-entries-with-statamic-3/edit-blueprint-screen-1024x534.png) Now all of this is heavily documented in the Statamic documentation, including the [field types](https://statamic.dev/reference/fieldtypes){rel=""nofollow""} you can add to a collection. There are so many to choose from. After years of doing WordPress dev and having to link custom javascript to get advanced field functionality, this is a breath of fresh air. You can extend your collection extremely easily with a ton of options, natively supported out of the box. ## Conclusion As I’m just getting started on my Statamic journey, I can already see so much I love about the CMS. First and foremost is the power to do amazing customizations, while staying out of the way and letting you make the decisions on implementation. I’m still learning a lot, but will be writing tutorials along the way. This one is very basic, but I hope this helps clear up some of the confusion I had when I first started! I plan to write a ton more as I take notes myself on certain features I'll be using. If you have a subject you’d like to see me cover, let me know on our [community](https://community.serversideup.net){rel=""nofollow""} or reach out on [Twitter](https://twitter.com/danpastori){rel=""nofollow""} and I’ll see what I can do! # Configure a secure guest wireless network using VLANs, firewalls, and throttling Learn how to create a secure guest wireless network that prevents guests from bringing malicious activity to your network. You'll also learn how to throttle the guest network down so that you do not worry about your guests using up all of your bandwidth. See the entire "Complete Ubiquiti UniFi + Synology Network Build" course here: {rel=""nofollow""} # Configure Stripe to Work with Laravel Cashier in Laravel 6 So we have [Laravel Cashier installed](https://serversideup.net/blog/installing-laravel-cashier-on-laravel-6-x/), now it's time to set up Stripe so we can actually bill for the app's services. This is where I felt there was a gap in documentation on the web. Laravel has their side documented beautifully, Stripe also has beautiful documentation, but they were kind of in separate columns. I hope this helps to merge the two together and make the billing process a breeze! ### Pre-Requisites A functioning Stripe account. ## 1. Log Into Stripe You should have at least signed up for Stripe and created an account. From there, it's pretty easy to create a "New Account" which would be your product. In our case, we have a sign in for multiple accounts, so I had to create a new account for the app I was making. Either way, you will need an account for the name of your app. Once you log into the dashboard, you will see the account you are on in the top left Corner: ![](https://serversideup.net/blog/configure-stripe-to-work-with-laravel-cashier-in-laravel-6/Stripe-Dashboard.png) ## 2. Grab Your API Keys This is the key (pun intended) to binding everything together. To do this, look at the bottom left of the dashboard and find `Developers`. Click that link. ![](https://serversideup.net/blog/configure-stripe-to-work-with-laravel-cashier-in-laravel-6/Stripe-Developers.png) When you open up that screen, you will see a sub menu called `API keys`. This is where we will grab our keys from. We will be doing a lot more work in this part in the upcoming tutorials when we discuss webhooks and events. Once you land on the `API keys` page you will see 2 keys, a Publishable key and a Secret key. There are 2 sets of these keys, `Live` and `Test`. For this tutorial, we will be using the `Test` keys. MAKE SURE that you swap these out for the `Live` keys when you push your product to production or you WILL NOT get paid! ![](https://serversideup.net/blog/configure-stripe-to-work-with-laravel-cashier-in-laravel-6/Stripe-Keys.png) Make sure you keep your secret key secret! This is what gets passed along server side with your requests. The publishable key you will place in your Javascript file to use the Stripe.js javascript framework. We will get into that more when we start working with Stripe Elements. ## 3. Place API Keys in your .env Now that you have your keys, go back to your Laravel install and open up the `.env` file in the root of your app. Place both of these keys in the placeholders we set up in the last tutorial. Your `.env` file should look like this: ```text [Stripe ENV Variables] CASHIER_MODEL=App\User; STRIPE_KEY=pk_test_TEST_KEY_RANDOM_STRING STRIPE_SECRET=sk_test_SECRET_KEY_RANDOM_STRING ``` Once you have these in your `.env` file, you should be ready to continue the Stripe setup! In the next tutorial, we will go into the process of creating your subscriptions! # Configuring Axios Globally with VueJS In the [last tutorial](https://serversideup.net/blog/using-axios-to-make-api-requests-with-vuejs/), we went through installing Axios on VueJS and NuxtJS. We also made our first requests with each set up! However, there was one glaring difference between the VueJS and NuxtJS setups that will exponentially grow as you develop. That difference is the global access to Axios within NuxtJS compared to importing it with VueJS. If you are using NuxtJS, this won't be of any use to you since global access is already available. I'd move on to the next tutorial where we submit some data! ## Why is this a big deal? Simply put, the less you need to repeat yourself, the better. You don't want to have to import Axios every time you want to use the library. Let alone, when you need to configure all of your requests to have certain headers, or intercept errors and gracefully handle them, doing this EVERY time adds up. I'm not for putting every library used in a global set up. As a matter of fact, I'm against it most of the time. However, Axios is an exception. There's global config and if you are building a Single Page Application, you will be making API requests all the time! Luckily, this is fairly straight forward, so let's get started! ## Set Up If you need to still install Axios within VueJS check out [Using Axios to Make API Requests With VueJS](https://serversideup.net/blog/using-axios-to-make-api-requests-with-vuejs/). If Axios is already installed, let's find the root javascript file where you set up your VueJS install. For me this is usually in a `/resources/js` directory and named `app.js` or `index.js`. Whatever it's named, find that file. In the file, you will simply need to add the following line: ```javascript [Require axios globally] window.axios = require('axios'); ``` That's really it! What this did was bind the axios variable to the `window` variable which gives us access to the functionality within any VueJS component! This may look familiar if you have been using VueJS with Laravel. By default, Laravel sets up the VueJS install similarly on the front end to work with their framework. You now can use `axios.get()` or `axios.post()` from within any component without having to import it every time. ## Other Global VueJS Axios Options Even though that's my preferred method, there may be other ways you choose to structure your VueJS app. Honestly, this is why I prefer NuxtJS because it's an opinionated way on how to set up the VueJS ecosystem and extend it with other modules. And it makes a ton of logical sense! The other options I'd consider are dependent upon what kind of app you are creating. If you will be using lots of request transformations (usually when working with multiple APIs) or working with various authentication methods, I'd add all of this in a separate file. This will make sure your VueJS initial file stays clean and easy to read. ## What's Next? There's a lot! It's nice to have Axios globally, but even that won't be enough for larger applications. You will not only be re-using axios, but also API requests. I'll show you have to abstract those requests into nice re-usable modules with VueJS and NuxtJS. However, next up, we will be going through a quick tutorial on how to send data to your API through a POST/PUT/PATCH request. # Configuring JS and SASS for a Single Page App So far we haven't made any front end changes. Our app so far is simply just a bunch of back end tools and configuration. We installed Laravel (/installing-configuring-laravel-spa/), configured Laravel Socialite ([Installing And Configuring Laravel Socialite - Server Side Up](https://serversideup.net/blog/installing-configuring-laravel-socialite/)), and configured Laravel Passport (/installing-configuring-laravel-passport/). Now we will begin structuring our front end for our app. Once again Laravel comes in clutch with Laravel Mix [Compiling Assets (Laravel Mix) - Laravel - The PHP Framework For Web Artisans](https://laravel.com/docs/5.6/mix){rel=""nofollow""}. You don't have to worry much about any of the complex Webpack configurations, sass configurations, or any of the build tools. We will still be using NPM, but Laravel mix will handle all of the building and compiling. Developers can even use mix outside of Laravel which is awesome! ## Step 1: Inspect /webpack.mix.js Laravel ships with a default configuration for Laravel Mix. If you visit `webpack.mix.js` in your root directory you will see a file that looks like: ```javascript [Default Laravel Mix Configuration] let mix = require('laravel-mix'); /* |-------------------------------------------------------------------------- | Mix Asset Management |-------------------------------------------------------------------------- | | Mix provides a clean, fluent API for defining some Webpack build steps | for your Laravel application. By default, we are compiling the Sass | file for the application as well as bundling up all the JS files. | */ mix.js('resources/assets/js/app.js', 'public/js') .sass('resources/assets/sass/app.scss', 'public/css'); ``` For our app this will solve our needs. What this is doing is requiring the 'laravel-mix' NPM module in the first line. The next group of code is the magic of Laravel Mix. It simply chains together a mix method. In mix.js, the first parameter is the location of your app.js file and the second is where that will compile and export to which will be the `public/js` directory. What Laravel Mix will do is use the `resources/assets/js/app.js` file as the entry point and build out a file named `app.js` and dump it in the `public/js` directory. The sass command operates the exactly same way as the .js method. It takes the entry point from `resources/assets/sass/app.scss` and dumps a compiled css file to named `app.scss` to the `public/css` directory. These we will keep the same since it will be easy to maintain. More complex apps can run multiple mix commands or change the directory, file names, etc. ## Step 2: Inspect package.json First we will inspect the `package.json` file in the root directory of the app. You should see something like this: ```json [package.json] { "private": true, "scripts": { "dev": "npm run development", "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", "watch-poll": "npm run watch -- --watch-poll", "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", "prod": "npm run production", "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" }, "devDependencies": { "axios": "^0.16.2", "bootstrap-sass": "^3.3.7", "cross-env": "^5.0.1", "jquery": "^3.1.1", "laravel-mix": "^1.0", "lodash": "^4.17.4", "vue": "^2.1.10" } } ``` This is preconfigured from your Laravel install and contains a few scripts and devDependencies. When building your app on your development machine, you will want to run either: `npm run dev` or `npm run watch`. I prefer to open a terminal window and start `npm run watch`. This will watch your JS/SCSS files and whenever you save a change it will re-compile your changes to the appropriate file. When you want to run production, simply run `npm run production` and your assets will be minified and ready for production use. As for some of the packages, we will use all of what is in the devDependencies object except "bootstrap-sass". We will be removing Twitter Bootstrap in favor of Zurb Foundation [The most advanced responsive front-end framework in the world. | Foundation](https://foundation.zurb.com/){rel=""nofollow""} Axios is an awesome HTTP client for JS and works seamlessly with Vue. It's documentation can be found here: {rel=""nofollow""}. jQuery will be used by foundation, and of course vue will be the center point of our SPA. ## Step 3: Remove Bootstrap (optional) If you want to follow along completely with our tutorial, run the following commands first to remove bootstrap: 1. `npm remove bootstrap --save-dev` 2. `npm remove bootstrap-sass --save-dev` These will remove all of the Twitter Bootstrap functionality. Next go to your `/resources/assets/js/app.js` file and change the remove the following line: ```javascript [Bootstrap Import to Remove] require('./bootstrap'); ``` This will remove the reference the file: `/resources/assets/js/bootstrap.js`. We will be merging what that file contains into our app.js file. Next go to your terminal window and run `npm install foundation-sites --save-dev`. This will make sure foundation is included in your project. > **Update 10/12/2017** I found a bug transpiling es5 with foundation and found the solution:{rel=""nofollow""}. The bug happens when running a production build. The solution is below and what your webpack.mix.js file should look like: ```javascript [Updated Webpack Mix Configuration with Foundation Support] let mix = require('laravel-mix'); /* |-------------------------------------------------------------------------- | Mix Asset Management |-------------------------------------------------------------------------- | | Mix provides a clean, fluent API for defining some Webpack build steps | for your Laravel application. By default, we are compiling the Sass | file for the application as well as bundling up all the JS files. | */ mix.js('resources/assets/js/app.js', 'public/js') .webpackConfig({ module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules(?!\/foundation-sites)|bower_components/, use: [ { loader: 'babel-loader', options: Config.babel() } ] } ] } }) .sass('resources/assets/sass/app.scss', 'public/css'); ``` ## Step 4: Configure app.js We've got a little bit of work to do to structure our app for scalability. Laravel ships some of the default functionality for starting an SPA in the `/resources/assets/js/bootstrap.js` file. I don't like this file, and since we will be structuring our app in a way to organize Vue components, pages, Vuex modules, I say we will remove this file and merge it into `/resources/assets/js/app.js` Navigate to your `resources/assets/js/bootstrap.js` file and grab the following line from the top: ```javascript [Lodash Import] window._ = require('lodash'); ``` and add that right away to the top of your `/resources/assets/js/app.js` file. Next we will grab the following try, catch block and move it right under the last line copied on top of the app.js file: ```javascript [jQuery and Foundation Setup] try { window.$ = window.jQuery = require('jquery'); require('foundation-sites'); } catch (e) {} ``` Then change the following line from: `require('bootstrap-sass')` to `require('foundation-sites')` if you are removing Twitter Bootstrap. Last, grab the next chunk of code that configures Axios and add that to your app.js file. Your file will now look like this: ```javascript [app.js] window._ = require('lodash'); try { window.$ = window.jQuery = require('jquery'); require('foundation-sites'); } catch (e) {} window.Vue = require('vue'); /** * We'll load the axios HTTP library which allows us to easily issue requests * to our Laravel back-end. This library automatically handles sending the * CSRF token as a header based on the value of the "XSRF" token cookie. */ window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; /** * Next we will register the CSRF Token as a common header with Axios so that * all outgoing HTTP requests automatically have it attached. This is just * a simple convenience so we don't have to attach every token manually. */ let token = document.head.querySelector('meta[name="csrf-token"]'); if (token) { window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; } else { console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); } /** * Next, we will create a fresh Vue application instance and attach it to * the page. Then, you may begin adding components to this application * or customize the JavaScript scaffolding to fit your unique needs. */ Vue.component('example', require('./components/Example.vue')); const app = new Vue({ el: '#app' }); ``` What the axios configuration does is include the X-CSRF-TOKEN to the the headers for each request sent. This will automatically allow access to the API and proper CSRF protection. Lastly, remove the following lines: `window.Vue = require('vue');` and ```text /** * Next, we will create a fresh Vue application instance and attach it to * the page. Then, you may begin adding components to this application * or customize the JavaScript scaffolding to fit your unique needs. */ Vue.component('example', require('./components/Example.vue')); ``` The first line removes Vue which you may be wondering why. Don't worry, in the next tutorial we will add it back in when we install the Vue ecosystem. The second chunk of lines removes the default Vue component. This we just don't need. Our `/resources/assets/js/app.js` file should look like: ```javascript [app.js] window._ = require('lodash'); try { window.$ = window.jQuery = require('jquery'); require('foundation-sites'); } catch (e) {} /** * We'll load the axios HTTP library which allows us to easily issue requests * to our Laravel back-end. This library automatically handles sending the * CSRF token as a header based on the value of the "XSRF" token cookie. */ window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; /** * Next we will register the CSRF Token as a common header with Axios so that * all outgoing HTTP requests automatically have it attached. This is just * a simple convenience so we don't have to attach every token manually. */ let token = document.head.querySelector('meta[name="csrf-token"]'); if (token) { window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; } else { console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); } const app = new Vue({ el: '#app' }); ``` Notice that Vue is binding to an el: '#app'? That is an element referenced in our `app.blade.php` file that we created in [Installing and Configuring Laravel SPA](https://serversideup.net/blog/installing-configuring-laravel-spa/). Vue will bind to this element and work it's magic with the Vue Router! ## Step 5: Remove Unnecessary JS Now we remove the unnecessary javascript. 1. Remove: `/resources/assets/js/bootstrap.js` since we merged this into our app.js. 2. Remove: `/resources/assets/js/components` directory. We will re-add some of these directories in the next tutorial ## Step 6: Configure SASS There are 2 things we need to do to configure SASS for our project. First, if you are using Foundation, open up `/resources/assets/sass/app.scss` and clear out the contents. Then add the following line: `@import "node_modules/foundation-sites/assets/foundation.scss";` Your entire `app.scss` file should look like: ```scss [Foundation SASS Import] @import "node_modules/foundation-sites/assets/foundation.scss"; ``` Next remove: `/resources/assets/sass/_variables.scss` . We will use a variables.scss file but not the one loaded with Laravel. We will be using a variables file, but we will place it in a different directory. ## Step 7: Set up SASS directories SASS makes CSS so much easier, but structuring it by a standard is the icing on the cake. When building an app I follow the 7-1 Pattern for structuring SASS: [Sass Guidelines](https://sass-guidelin.es/#the-7-1-pattern){rel=""nofollow""} The only difference is I keep app.scss instead of their recommended main.scss file. To configure the app for the 7-1 Pattern build the following directories: 1. `/resources/assets/sass/base` 2. `/resources/assets/sass/components` 3. `/resources/assets/sass/layouts` 4. `/resources/assets/sass/pages` 5. `/resources/assets/sass/themes` 6. `/resources/assets/sass/abstracts` 7. `/resources/assets/sass/vendors` In the Sass Guidelines: [Sass Guidelines](https://sass-guidelin.es/#the-7-1-pattern){rel=""nofollow""} it runs through what should be in each directory. This is super helpful when laying out your app. We will be heavily utilizing the components directory to style our web components. ## Conclusion In the next tutorial we will be focusing on VueJS and all of the sweet Vue ecosystem (Vue-Router and Vuex) extensions. We will build some more folders and get that section structured behind the scenes as well. I also promise that Roast will start becoming more useful in more tutorials. We are just building the foundation for our Single Page App right now, but soon we will be adding routes, displaying data, implementing components, etc. # Configuring Vue Router for a Single Page App Single page apps are possible on the web due to the HTML5 history API: [History - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/History){rel=""nofollow""} What the history API does is allow the developer to adjust the history of the web browser without changing pages and can still have permanent URLs for deep linking. Why would developers want this? It allows developers a significant load time change and more control over transitions within their site. Each route only loads the data unique to the route after the initial load. So in a traditional application, if you have a CSS file, a JS file and some images. Those are loaded every time you make a page request. In a single page application, these are loaded initially on the first load and any other images, data, CSS is loaded through AJAX. In the end this saves lots of server requests and makes for a much speedier app. There are some downfalls when building apps like this discussed here: [The disadvantages of single page applications by Adam Silver](https://adamsilver.io/articles/the-disadvantages-of-single-page-applications/){rel=""nofollow""}. I agree with some of the statements, but when building smaller apps like Roast, the code is generally more maintainable and super easy to transfer to a hybrid mobile app which is our end goal. Single Page Applications do a good job of separating the visual display from the data source which is perfect for transferring to mobile. Now learning the HTML 5 History API can be challenging, but thanks to Vue Router [Introduction · vue-router](https://router.vuejs.org/en/){rel=""nofollow""}, all of the hard stuff is taken care of such as pushing and popping states. And since we are building our app in VueJS, it works seamlessly with everything else in VueJS. In this tutorial, we are simply going to set up our home route, a cafes list route, an individual cafe route, and a cafe submission route. These will be on our front end only routes and the next tutorial we will add the backend component for the API. ## Step 1: Configure Routes File We will need to open our `/resources/assets/js/routes.js` file. This will be where we maintain every route in our application. We will need to import Vue and VueRouter first into the file so we can tell Vue that we are using VueRouter to handle the routes in our application. The top of your routes.js file should look like: ```javascript [routes.js] /* |------------------------------------------------------------------------------- | routes.js |------------------------------------------------------------------------------- | Contains all of the routes for the application */ /* Imports Vue and VueRouter to extend with the routes. */ import Vue from 'vue' import VueRouter from 'vue-router' /* Extends Vue to use Vue Router */ Vue.use( VueRouter ) ``` First we import Vue from the 'vue' package and then import VueRouter from the 'vue-router' package. Then we instructed Vue to use VueRouter. Pretty simple right off of the bat. ## Step 2: Add Routes We will need to add 4 routes to our app right off of the bat. The routes will be: - / -> Home - /cafes -> Cafe Listing - /cafes/new -> Add Cafe - /cafes/\:id -> Display Individual Cafe The Vue Router documentation is very thorough on how to add routes: [Getting Started · vue-router](https://router.vuejs.org/en/essentials/getting-started.html){rel=""nofollow""}. One thing I like to do is use named routes. What this means is I can reference routes by a specific name instead of typing out the entire path when I write links. Each route is an object in a routes array that we use when we construct a new VueRouter. Since we have the routes in a separate file, we export the default module so we can import it into our app.js file. The following is what we should have in our `/resources/assets/js/routes.js` file: ```javascript [routes.js] /* |------------------------------------------------------------------------------- | routes.js |------------------------------------------------------------------------------- | Contains all of the routes for the application */ /* Imports Vue and VueRouter to extend with the routes. */ import Vue from 'vue' import VueRouter from 'vue-router' /* Extends Vue to use Vue Router */ Vue.use( VueRouter ) /* Makes a new VueRouter that we will use to run all of the routes for the app. */ export default new VueRouter({ routes: [ { path: '/', name: 'home', component: Vue.component( 'Home', require( './pages/Home.vue' ) ) }, { path: '/cafes', name: 'cafes', component: Vue.component( 'Cafes', require( './pages/Cafes.vue' ) ) }, { path: '/cafes/new', name: 'newcafe', component: Vue.component( 'NewCafe', require( './pages/NewCafe.vue' ) ) }, { path: '/cafes/:id', name: 'cafe', component: Vue.component( 'Cafe', require( './pages/Cafe.vue' ) ) } ] }); ``` There's a few things to point out. Each route has a name. For example, the home route has the name of 'home' which we can reference from any place in our app. We also have a key in each route object called 'component'. This defines the Vue component used to render each page. Like I mentioned before, I store these in a special directory: `/resources/assets/js/pages`. The other thing to point out, is that the last route (`/cafes/:id`) notice the '\:id' at the end. This is a dynamic route segment ([Dynamic Route Matching · vue-router](https://router.vuejs.org/en/essentials/dynamic-matching.html){rel=""nofollow""}) which allows us to define a pattern matched ID to dynamically load a specific cafe. ## Step 3: Add Page Components In Vue Router, all of the pages being rendered are Components. In the last tutorial we created a directory to house these pages `/resources/assets/js/pages`. We need to add the following files: - `/resources/assets/js/pages/Cafe.vue` - `/resources/assets/js/pages/Cafes.vue` - `/resources/assets/js/pages/Home.vue` - `/resources/assets/js/pages/NewCafe.vue` Each file should contain just the shell of a Vue single file component which looks like: ```vue [Basic Vue single file component template] ``` If you haven't used Single File Vue Components before, I'd read about them here: [Single File Components — Vue.js](https://vuejs.org/v2/guide/single-file-components.html){rel=""nofollow""}. Simply put they are your JS, HTML, and CSS all scoped within a single file. Since we are using Laravel and Laravel Mix, these compile to the appropriate files out of the box. I find this approach amazing for all apps, single page or not, because it really helps maintain a clean codebase for all components. You can even scope your CSS so it only applies to the component it's registered in. ## Step 4: Import Router into App.js We now have our routes.js file ready to rock and roll and basic page template components outlined. We are now ready to import our VueRouter into our `/resources/assets/js/app.js` file. To do that, all we have to do is add ```javascript [Router import statement] import router from './routes.js' ``` right after ```javascript [Vue import statement] import Vue from 'vue'; ``` Next we will instruct our Vue instance to use the `router` we just imported by adding the `router` to the new Vue instruction like this: ```javascript [Vue instance with router configuration] new Vue({ router }).$mount('#app') ``` Our `app.js` file should now look like: ```javascript [app.js] window._ = require('lodash'); try { window.$ = window.jQuery = require('jquery'); require('foundation-sites'); } catch (e) {} /** * We'll load the axios HTTP library which allows us to easily issue requests * to our Laravel back-end. This library automatically handles sending the * CSRF token as a header based on the value of the "XSRF" token cookie. */ window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; /** * Next we will register the CSRF Token as a common header with Axios so that * all outgoing HTTP requests automatically have it attached. This is just * a simple convenience so we don't have to attach every token manually. */ let token = document.head.querySelector('meta[name="csrf-token"]'); if (token) { window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; } else { console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); } import Vue from 'vue'; import router from './routes.js' new Vue({ router }).$mount('#app') ``` We now define Vue, configure axios and configure the Vue Router so our app has a few pages. Our app still doesn't do too much yet, but we are getting there! ## Step 5: Build This is just a reminder step that if you haven't been running `npm run watch` then you should run an `npm run dev` to build your app on your development environment. If you are curious, you can log in socially and visit one of the routes defined. The next tutorial will go through adding the API routes and using Axios for HTTP requests to our API. If you want to take a look at the features of Axios check out their documentation: [GitHub - axios/axios: Promise based HTTP client for the browser and node.js](https://github.com/mzabriskie/axios){rel=""nofollow""}. It's super easy to use and we already have it configured out of the box with Laravel to attach the token so we can access our API. The next tutorials will cover building these API requests in Javascript, building the API endpoints on the Laravel side, then configuring Vuex to store the data we get returned. Once we have that done we can start making the app look pretty and doing some other cool stuff to the app! Let me know if there are any questions, suggestions, or opinions on the structure so far! Remember, you can follow along on GitHub: {rel=""nofollow""} # Creating Cordova App Icon Sizes the Fast Way Building a hybrid mobile app can save you a ton of time. But when it comes to making your icons pixel perfect, it is overwhelming with the amount of Cordova app icon sizes that are required to build for each platform. Luckily, there are tools that can help you make this process a lot less painful for you. What we're going to achieve today is building a file that will create all of the icons needed for us. If I need to update the icons in the future, all I need to do is update one design and it will update the rest of the sizes for me. For this example, I am going to design for two platforms: Android and iOS. ## We can do all of this with Sketch [Sketch is a design application](https://www.sketchapp.com/){rel=""nofollow""} similar to Adobe Photoshop. If you haven't heard of this before, it's been quite the tool to use amongst the most talented designers. The price is $99/year, but they also have a free trial. It's built for **Mac OS X only**, but it's totally worth the investment if you are doing UI design often. The product is built all around User Interface design (unlike Adobe Photoshop which is geared towards Photographers and static Graphic Designers). Everything you create is in a "vector" format so it makes it easy for your design to scale with all of the crazy pixel densities out there. ## Step 1: Cheat and [download my template](https://drive.google.com/uc?export=download&id=0B8VOU9nEC6Ybdjk3V2ltUWMxSUU){rel=""nofollow""} Once you have Sketch installed, I made it really easy for you to do make your icons. [Download my template](https://drive.google.com/uc?export=download&id=0B8VOU9nEC6Ybdjk3V2ltUWMxSUU){rel=""nofollow""} and open it in Sketch. ## Step 2: Create your iOS icon You should be able to toggle the guides on and off. Align everything perfectly and keep your design within the guides. The following abides to [Apple's App Icon Guidelines](https://developer.apple.com/ios/human-interface-guidelines/graphics/app-icon/){rel=""nofollow""}. ![iOS Icon Creation](https://serversideup.net/blog/cordova-app-icon-sizes/1479-1.png) ## Step 3: Create your Android icon [Android has it's own set of guidelines](https://material.io/guidelines/style/icons.html#app-shortcut-icons){rel=""nofollow""}. Same as before, keep your design within the guides. I made it all nice and cute for the Android Oreo circular pattern. ![Android Icon Creation](https://serversideup.net/blog/cordova-app-icon-sizes/1479-2.png) ## Step 4: Let the magic happen This is where Sketch becomes **really** powerful. All you need to do now is hit `Export` in the upper right. **Make sure you set the guides to HIDDEN** (otherwise they will appear in your exported icon). Then choose the folder of your Cordova project where you want them placed. ![Sketch Export Interface](https://serversideup.net/blog/cordova-app-icon-sizes/1479-3.png) The magic is all happening within the file that I gave you. Sketch allows us to export specific widths. ![Sketch Export Options](https://serversideup.net/blog/cordova-app-icon-sizes/1479-4.png) ## Step 5: Update your config.xml Once you have the images exported into your project folder, all you will need to do is update your `/config.xml` in your Cordova Project to be able to access your images. The [Cordova App Icon Sizes can be found in their documentation](https://cordova.apache.org/docs/en/latest/config_ref/images.html){rel=""nofollow""} for every file size you need (incase you need to add other platforms). ![Config.xml Update](https://serversideup.net/blog/cordova-app-icon-sizes/1479-5.png) You should now be able to run a `cordova build` and both platforms should build now. If you enjoyed this article, please let me know by leaving a comment below or hitting me up on Twitter! # Creating a Stripe Subscription with Laravel Cashier + Laravel Passport Now we are getting down to business! We have just finished allowing the user to save a payment method with their account ([Managing Stripe Payment Methods in VueJS SPA and Laravel API](https://serversideup.net/blog/managing-stripe-payment-methods-in-vuejs-spa-and-laravel-api/)) and we are now ready for the user to create a Stripe subscription to the plan within our app! Before we display any options for the plan and what we are allowing the users to subscribe to, we will have to display the possible payment options that they have available to select. Luckily in the last tutorial ([Managing Stripe Payment Methods in VueJS SPA and Laravel API](https://serversideup.net/blog/managing-stripe-payment-methods-in-vuejs-spa-and-laravel-api/)), we have everything we need to get started! ## 1. Allow user to select stored payment option So we have a simple form where we allow the user to store payment methods. After the payment method is stored, we save it as the default and then load available payment methods for the user to select. The user will have to select one of these to start their subscription. In the last [tutorial](https://serversideup.net/blog/managing-stripe-payment-methods-in-vuejs-spa-and-laravel-api/) we added the proper API route and VueJS component method to load these payments. Let's open up the `SubscriptionManagement.vue` component and build a simple template to allow these payment methods to be selected. To do that, add the following HTML to the template after the `Save Payment Method` button. We will post the final component's code in the last tutorial to see how it all works. ```vue [Payment Methods Selection Template] ``` Or with a dynamic variable: ```vue [Nuxt 3 dynamic page title using Head component] ``` In the example above, we placed our `` tag right in with our `
`. This may seem weird if you've made HTML pages in the past. However, since these are custom components, and not actually the `` and `` tags from HTML, they are just for ease of use. The components will render the content in the correct spot when the page is rendered. I really like this component approach. It's pretty straight forward and easy to add to the page you want the title on. There's one more place we can look to add page titles, and that's a default title in the `nuxt.config.ts` file. ## Step 4: Set a default title in `nuxt.config.ts` Similar to Nuxt 2, you can also define a default title for your app in the `nuxt.config.ts` file. To do that, you need to add the following: ```typescript [Nuxt 3 default title configuration] export default defineNuxtConfig({ meta: { title: 'ROAST', } }) ``` Setting a title here is good practice just as a fallback. You should provide a specific title on each page that needs one. However, having a fall back in place is a good idea as well. Since this is global app level, this can't be dynamic. ## Conclusion Hope this helped show some of the differences between setting page titles with Nuxt 2 and Nuxt 3. The flexibility provided by the composable and components in Nuxt 3 is amazing. If you want to see how this works in a much larger sense, we have a book about building a single page application with Nuxt 3. You can see a more cohesive approach along with a bunch of other tips and tricks! If you have any questions, feel free to reach out on our [community](https://community.serversideup.net) or get in touch on [Twitter](https://twitter.com/danpastori)! --- # Using Environment Variables in Nuxt 3 > Learn how to manage and use environment variables in Nuxt 3. This guide covers best practices for runtime configuration, environment-specific settings, and tips for premium developers building scalable, production-ready applications. There's only a slight difference in how to reference environment variables from Nuxt 2 to Nuxt 3. Nuxt 3 provides a simple composable that you can include in your `setup()` method. Nuxt 2 set the environment variables in a global `$config` variable. The functionality is similar, but the syntax is slightly different, let's take a look. ## Step 1: Setting an Environment Variable To set an environment variable, you must have a `.env` file in the root of your directory. Both versions of Nuxt have [built in support for dotenv](https://v3.nuxtjs.org/docs/usage/runtime-config#environment-variables) and can load variables from this file. What that means is any variable in the `.env` is loaded into `process.env` and can be handled from there. Let's set our API base url variable in our `.env`: ```env [Environment variable configuration] API_BASE_URL: https://api.roastandbrew.coffee/api/v1 ``` This is super helpful if you are working in multiple environments. You'd set this to the environment according to where you are located. For example, this URL might be `https://api-roast.dev.test/api/v1` on your local testing environment. Now that we have the variable set, let's load it into our Nuxt 3 application ## Step 2: Using the Variable in Your App We use the base url variable to make API requests from within our app so we have to access it. To do this, it's the same on both Nuxt 2 and Nuxt 3. You have to set it in your run time config accordingly. Since the URL is not secret and sensitive data, we can use the `publicRuntimeConfig:` key. If it was private, like an API token, you'd want to do this using the `privateRuntimeConfig:`. These are set within your `nuxt.config.ts` file in Nuxt 3: ```typescript [Nuxt runtime configuration] publicRuntimeConfig: { API_BASE_URL: process.env.API_BASE_URL }, ``` Remember, using `.env` with dotenv support, any variable defined in your `.env` file is accessible through `process.env.{variable_name}`. Now let's get to how we actually access this within our component. This is where it differs from Nuxt 2 to Nuxt 3. ## Step 3: Accessing these variables So in Nuxt 2, if you were to access your API Base URL, you'd use the global `$config` variable: ```javascript [Access the global config variable] this.$config.API_BASE_URL ``` In Nuxt 3, it's slightly different. You'd first load the config using the `useRuntimeConfig()` composable method in your `setup()` method or `<script setup>`: ```vue [Vue component with runtime config] <script setup> const config = useRuntimeConfig(); </script> ``` Now you can access your configuration variables like this: ```javascript [Access the base URL variable] config.API_BASE_URL ``` from anywhere within your component. Slight difference, same functionality! ## Conclusion Hopefully these little migration tips help out a little bit! So far, our experience with Nuxt 3 has made it completely worth it! If you'd like to see how we use environment variables in the context of a Single Page Application, [we have a book](/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). The book shows you how to build an entire API + SPA with Nuxt 3. Feel free to reach out on [Twitter](https://twitter.com/danpastori) or on our [community](https://community.serversideup.net) if you have any questions! --- # Using asyncData in Nuxt 3 > Learn how to use asyncData in Nuxt 3 for efficient data fetching and migration from Nuxt 2. This guide covers the new Composition API, useAsyncData, and best practices for loading data in modern Nuxt applications. Nuxt 3 has a slightly different syntax for using `asyncData()` than Nuxt 2. In this short tutorial, we will touch on a few of the differences we ran across when migrating ROAST from Nuxt 2 to Nuxt 3. There are a lot of updated changes when it comes to data fetching, so I'd recommend checking out the [Nuxt 3 docs](https://v3.nuxtjs.org/docs/usage/data-fetching) for further insight! In ROAST we used asyncData for a lot of our initial data loading. In Nuxt 3 you can continue to use asyncData, it's just been adapted for Vue 3. ## Individual `asyncData()` request The biggest difference when setting up async data from Nuxt 2 to Nuxt 3 is migrating to the Vue 3 composition API. In Nuxt 2, `asyncData` worked with the options API where Nuxt 3 provides a super helpful composable (`useAsyncData()`) that performs the same function. When loading the statistics in the footer of ROAST, we have a simple `asyncData()` method that loads the stats. In Nuxt 2, the request looks like: ```javascript [Nuxt 2 asyncData example for loading stats] async asyncData({ $http }) { const stats = await $http.$get(`/api/v1/stats`) return { stats } } ``` We make a request to our endpoint, and return the `stats` variable that we can use within our component. In Nuxt 3, the same setup request looks like: ```javascript [Nuxt 3 asyncData example using useAsyncData composable] const { data:stats } = await useAsyncData( 'stats', () => $fetch( config.API_BASE_URL+'/stats') ); ``` Just a heads up! You will need to put this in your `setup()` function or within `<script setup>` to use the composable! The main difference between the two requests is the composable `useAsyncData()`. The request calls the `$fetch` plugin provided by Nuxt to perform the request. When returned, we decouple the `data` from the response, and give it the name of `stats`. This way we can use the `stats` variable in our component and in our template. Next up, let's take a look at making two simultaneous requests. ## Simultaneous `asyncData()` requests An example of where we used `asyncData` is on the individual [cafes page](https://roastandbrew.coffee/companies/ruby-coffee-roasters/cafes/cafe-1410-third-street-stevens-point-wi) to load up the `cafe` and the `company` resources from our API. In Nuxt 2, we would call two endpoints that returned promises and our request looked like: ```javascript [Nuxt 2 example of simultaneous asyncData requests] async asyncData ( { $http, params, error } ) { try{ const [ cafe, company ] = await Promise.all([ $http.$get(`https://api.roastandbrew.coffee/api/v1/companies/${params.company}/cafes/${params.cafe}`), $http.$get(`https://api.roastandbrew.coffee/api/v1/companies/${params.company}`) ]); return { cafe: cafe, company: company } } catch ( e ){ error( { statusCode: 404, message: '' }); } } ``` Luckily, you can do the same thing with Nuxt 3 as long as you apply a few updates. Our Nuxt 3 request looks like: ```javascript [Nuxt 3 example of simultaneous asyncData requests using Composition API] export default defineComponent({ async setup() { const config = useRuntimeConfig(); const route = useRoute(); const [{ data: company }, { data: cafe }] = await Promise.all([ useFetch(config.API_BASE_URL+'/companies/'+route.params.company), useFetch(config.API_BASE_URL+'/companies/'+route.params.company+'/cafes/'+route.params.cafe) ]) return { company, cafe } } }) ``` First, since Nuxt 3 runs on Vue 3, it's recommended to use the Composition API. Because of that, we need to make the `setup()` method asynchronous. If you are doing async setup, that means you can't use the `<script setup>` syntax. Just a heads up! Next, will also need to use the `useFetch()` composable instead of the `$http` plugin. The `useFetch()` composable wraps the [ohmyfetch](https://github.com/unjs/ohmyfetch) API to make API requests a breeze! Finally, you will have to import the dynamic route parameters using `useRoute()`. We will talk more about dynamic route parameters in the next tutorial, just bringing awareness here! If you want to skip ahead and learn a little bit more about data fetching with Nuxt 3, check out [Advanced Data Fetching with Nuxt 3](/blog/advanced-data-fetching-with-nuxt-3/)! ## Conclusion There are a few syntax changes, but overall the functionality is very similar. Let me know if you are running into issues! Feel free to reach out on [Twitter](https://twitter.com/danpastori) or on our [community forum](https://community.serversideup.net). If you are interested in seeing the entire Nuxt 3 codebase for ROAST, [we have a book available](/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/)! In the book we go through the whole process of building an API with Laravel and an SPA with Nuxt 3. We then deploy to web, iOS and Android. It's a lot more cohesive and goes from empty code to production app! --- # Accessing Route Parameters in Nuxt 3 > Learn how to access and manage dynamic route parameters in Nuxt 3. This guide provides premium software developers with migration strategies from Nuxt 2, best practices for page naming, and actionable tips for building robust, production-ready Vue applications. Accessing route parameters is an essential for your Nuxt 3 app. Route parameters are the dynamic pieces of your URL that determine what resource or content is loaded. If you are following along in this [migration guide](/guides/upgrading-nuxt-2-to-nuxt-3/), you probably have seen them accessed in action. In the last section, we loaded [async data](/blog/using-async-data-in-nuxt-3/) from the API to display a company or cafe in ROAST. To load the specific resource, we grabbed a route parameter. Let's touch on some of what it took to migrate ROAST from Nuxt 2 to Nuxt 3 and access the route parameters. ## Step 1: Naming Pages The first step in using route parameters is to set up your naming conventions for your pages correctly. In both Nuxt 2 and Nuxt 3, the way you name your page component reflects the name of the variable. Let's use the individual company URL which would match `https://roastandbrew.coffee/companies/{company}`. The `{company}` would be the dynamic route parameter we will want to access. However, in order to even gain access, we have to set up our page structure correctly. ### Nuxt 2 Page Naming Conventions There are two ways you could do this in Nuxt 2. First, would be to add a page in the pages directory named `/pages/companies/_company.vue`. Any page prefixed with an `_` would be accessible as a route parameter with the name following the `_` (I.E. `company`). The second way you could do this with Nuxt 2 is if you had a directory that started with an `_` and you named a vue page within that directory `index.vue`. The directory would look like: `/pages/companies/_company/index.vue`. This approach is recommended if you have a page to edit a resource. You could throw an `edit.vue` file in the directory and get the following URLs: `https://roastandbrew.coffee/companies/{company}` and `https://roastandbrew.coffee/companies/{company}/edit`. ### Nuxt 3 Page Naming Conventions To migrate these pages to Nuxt 3, the first step is updating the naming conventions. Instead of an underscore, you need to change the name to be bracketed. For example, if we had `/pages/companies/_company.vue` in Nuxt 2, we'd change the name to be `/pages/companies/[company].vue` in Nuxt 3. The same process goes for folder naming conventions. You'd have to update `/pages/companies/_company/index.vue` to be `/pages/companies/[company]/index.vue` in Nuxt 3. Simple update, but important nonetheless. ## Step 2: Accessing Dynamic Route Parameters Now that we have our page layouts named correctly, we can access the dynamic route parameters that we configured. This is useful when you want to load a resource by its identifier on the page. Let's take a look at what we had in Nuxt 2: ```javascript [Accessing the route parameter in Nuxt 2] this.$route.params.company ``` In Nuxt 2, you could access the route parameters by accessing the global `$route` plugin. You'd then access the `company` variable under the `params` key. In Nuxt 3, the same functionality looks like: ```javascript [Accessing the route parameter in Nuxt 3] const route = useRoute(); route.params.company ``` You can access the `route` object once you load it from the `useRoute()` composable function. This must be set up in your `setup()` method or your `<script setup>` since it's an auto loaded composable. Same functionality, slightly different syntax! ## Conclusion Migrating the page naming conventions and route structure isn't too bad from Nuxt 2 to Nuxt 3. Moving to the Composition API just makes your code more flexible. [In our book](/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/), we have a fully functioning Nuxt 3 app where you can see what everything looks like put together and running in production. If you have any questions, feel free to reach out on [Twitter](https://twitter.com/danpastori) or on our [community forum](https://community.serversideup.net). --- # Dynamic API Requests with Nuxt 3 > Discover how to make dynamic API requests with Nuxt 3, including best practices for fetching data, handling responses, and integrating APIs in modern Nuxt applications. After your user interacts with your page, it can be necessary to refresh your data. A few examples of when to refresh data is when you need to filter API resources or paginate data. With Nuxt 3, these dynamic API requests can be structured in a reactive manner. When the query parameters change, the API request is automatically called allowing you to simply re-render the data. In Nuxt 2, this approach was done by wrapping the `$axios` plugin and calling a method to refresh the data. In Nuxt 3, we can compute a query string and when it changes, the new data will be fetched. Let's get started! ## Getting Started Let's say we are using our `/api/v1/cafes` endpoint in [ROAST](https://roastandbrew.coffee/). This endpoint allows you to search, paginate, and filter by brew methods through an API request. On our [search page](https://roastandbrew.coffee/search), we can set these filters and reload the data without reloading the entire page. This is the scenario we are going to step through in Nuxt 2 vs Nuxt 3 because it's slightly different. ## Step 1: Dynamic API Requests in Nuxt 2 Let's take a look at a quick example of how to make a dynamic API request with Nuxt 2: ```javascript [Nuxt 2 Dynamic API Request Implementation] data: { return { search: '', brew_methods: [], page: 1, cafes: [] } }, methods: { async searchCafes( page = 1 ) { let params = buildSearchCafes( page ); const cafes = await this.$axios.$get('https://api.roastandbrew.coffee/api/v1/cafes', { params: params }); this.cafes = cafes }, buildSearchCafes( page ){ let searchParams = {}; // Pagination searchParams.page = page; searchParams.take = 12; if( this.search != '' ){ searchParams.search = encodeURIComponent( this.search ); } if( this.brew_methods.length > 0 ){ searchParams.brew_methods = encodeURIComponent( this.brew_methods.join(',') ); } return searchParams; } } ``` There are 3 sections to this example. ### Defining Data First, we have the `data`. The `data` contains all of the different variables the user can use to load what they need from the API endpoint. In reality, there are a lot more, but for this example, we have a `search` string, `brew_methods` array, `page` for pagination, and `cafes` array used to display the data. ### Building the Query Parameters Next, we have the `buildSearchCafes( page )` method. This method, simply takes what the user has selected, converts the selections to a JSON object, and returns them. We use these query parameters in our request to our API endpoint in the next step. ### Making our Dynamic API Request Finally, we make our API request. In Nuxt 2, you had a few options. I always used the `$axios` module since it was easy to customize. You could use fetch or the `$http` plugin as well. Whatever way you chose to do it, you needed to make an API request with the query parameters. Once you got the data back from the API, you could set it to your local `cafes` data array. Vue would then reactively display the updates. When the user clicked a pagination button such as previous, a number, or next, we'd re-request the data with what the user selected. We'd also call the `searchCafes()` method if the user searches or filters the results. This process allows the user to refresh the data without leaving the page. In Nuxt 3, the process is similar, but a little more reactive. ## Step 2: Dynamic API Requests in Nuxt 3 When we build these dynamic API requests in Nuxt 3, we will be focusing solely on watching for changes to our filter variables. This will trigger our watched query string, which will in-turn update the data. Let's take a look: ```javascript [Nuxt 3 Dynamic API Request Implementation] const search = useState('search', ''); const brewMethods = useState('brewMethods', []); const page = useState('page', 1); const queryString = computed(() => { let values = '?page='+page.value+'&take=12'; values += search.value != '' ? '&search='+search.value : ''; values += brewMethods.value.length > 0 ? '&brew_methods='+brewMethods.value.join(',') : ''; return values; }); const { data:cafes, pending, refresh } = await useLazyAsyncData( 'cafes', () => $fetch( `https://api.roastandbrew.coffee/cafes${queryString.value}`) ) // When query string changes, refresh watch(() => queryString.value, () => refresh() ); ``` Before we break this functionality down and the differences, note that you should use this in a `<script setup>` tag or `setup()` method with Vue 3. Now this looks entirely different, but the functionality is the same. Instead of calling a method that refreshes our API, we watch for changes on a dynamic query string. ### Defining the State First, we define the state that we need within our component. We have a `search` string, `brewMethods` array, and `page` that keeps track of the pagination. We didn't include our `cafes` here, because we set that variable up with `asyncData` which we will discuss soon. ### Building our Query String Next, we have our computed `queryString`. This variable reacts and updates when one of the dependent variables changes. If you break down the method, whenever the `page`, `search` or `brewMethods` gets updated, we dynamically build the query string and return it. This is extremely powerful and you will see why shortly. ### Making our Dynamic API Request To define our `cafes` variable which is used to render the page, we use the `useLazyAsyncData()` composable. This composable allows us to decouple the `data:cafes`, `pending`, and `refresh` method. All are extremely important. The `data:cafes` defines the variable we use to load our cafes from our API request. To show whether the request has completed or not, we can use the `pending` variable. It will be set to `true` or `false` depending on whether the request is loading or not. Finally, we have a `refresh()` method. This will re-send our API request if we need to refresh the data stored in the `cafes` variable. This is where the magic happens! ### Watching for Changes The last line of our code ties everything together: ```javascript [Query String Change Watcher] watch(() => queryString.value, () => refresh() ); ``` What this little snippet does is watch the value of our query string. When the query string changes after the user adjusts a search parameter, pagination page, or filter, we call the `refresh()` method. Since we append the `queryString` to our API request, when we refresh after a change, the new query string will be sent to the API. This is how we update our data! So there's actually another way to do this using Watch Sources. Feel free to skip ahead to [Advanced Data Fetching with Nuxt 3](/blog/advanced-data-fetching-with-nuxt-3/) to learn more. ## Conclusion Both Nuxt 2 and Nuxt 3 allow you to easily build dynamic api requests into your application. Even though the structure and syntax is different, the functionality is the same. When migrating from Nuxt 2 to Nuxt 3, these changes may be cumbersome but as you finish your migration it all ties together. You get the speed and updated functionality of Nuxt 3 along with maintaining the existing features in your app. If you want to see a more cohesive approach to how this works, we do [have a book](/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/)! In the book, we go through multiple API requests, some dynamic, some more static and how they play into the app as a whole. If there are any questions, feel free to reach out on Twitter ([@danpastori](https://twitter.com/danpastori)) or on our [community forum](https://community.serversideup.net)! --- # Importing and Using Components in Nuxt 3 > Discover how to import and use components in Nuxt 3, including best practices for component organization, auto-imports, and efficient development workflows. When we migrated [ROAST](https://roastandbrew.coffee) from Nuxt 2 to Nuxt 3, we had to review how we imported components. In Nuxt 2, we had to explicitly import the component into a page, layout, or parent component. In Nuxt 3, components are auto-imported. With later versions of Nuxt 2, you could have done this through the components plugin. However, if you are like me, you most likely still had explicit imports in your code. Let's take a look on how to update your app to use the auto imports provided by Nuxt 3. ## Importing and Using Components in Nuxt 2 When you had to import components in Nuxt 2, you had to explicitly import each component every time you used it. Luckily, Nuxt 2 came with the helpful `@` alias which mapped to the root of your app. However, with larger pages and components, you'd have to do a lot of importing. Let's say we have a global header component that's stored in the `/components/global/AppHeader.vue` directory. We'd have to import that component like: ```vue [Nuxt 2 Component Import Example] <template> <div> <AppHeader/> </div> </template> <script> import AppHeader from '@/components/global/AppHeader.vue' export default { components: { AppHeader } } </script> ``` Not only would we have to provide an `import` statement, we'd also have to register the component in the `components` object in the parent component. This wasn't too bad, but there are even less steps when using Nuxt 3. ## Importing and Using Components in Nuxt 3 Let's say we have the same component in Nuxt 3 stored in the `/components/global/AppHeader.vue` directory. To use that component, our process would look like: ```vue [Nuxt 3 Auto-Import Component Example] <template> <div> <GlobalAppHeader/> </div> </template> ``` There's a lot less code in Nuxt 3 to get the same functionality! Specifically, there are 2 specific changes you will have to make when migrating to Nuxt 3. First, you will notice the name of the component is not `<AppHeader/>`, but `<GlobalAppHeader/>`. When Nuxt 3 auto imports, each sub-directory transforms to a camel-cased prefix on the component name. So our directory being `/global` and our component file named `AppHeader.vue` would translate to `<GlobalAppHeader/>`. Finally, you won't even have to register the component within the layout, page, or parent component. Since the name of the component is essentially namespaced, there's only one possibility for what component it is. Nuxt 3 takes care of the naming conventions and auto importing. To be honest, the auto-importing naming conventions caught me off guard. However, I enjoy using them now that I've gotten the hang of it. I did have to rename a few components and restructure the directories so they'd make more sense. But once I did, I felt like I could really flow when building the app. The main benefit is that you are pretty much guaranteed a two word component name. This is an [essential style rule for Vue 3](https://vuejs.org/style-guide/rules-essential.html#use-multi-word-component-names). You won't even have to think about it like you would if you were just importing a component and had to come up with a name on the fly. ## Conclusion Overall, I find it much quicker to develop using auto-imported components with Nuxt 3. If you want to see how we migrated our components for ROAST, [check out our book](/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). We have a Nuxt 2 and Nuxt 3 branch that you can compare and contrast. Of course, if you have any questions, feel free to reach out on our [community](https://community.serversideup.net) or on Twitter ([@danpastori](https://twitter.com/danpastori)). --- # POST, PUT, and PATCH Requests with Nuxt 3 > Learn how to handle POST, PUT, and PATCH requests in Nuxt 3 using the new $fetch API. This guide covers best practices for sending data, uploading files, and migrating from Nuxt 2 for modern web development. 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](https://github.com/unjs/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: ```javascript [Nuxt 2 Axios POST Request Example] 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: ```javascript [Nuxt 3 $fetch POST Request Example] 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](https://github.com/unjs/ohmyfetch). 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](https://javascript.info/formdata) 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: ```vue [Nuxt 2 File Upload Component with 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: ```vue [Nuxt 3 File Upload Component with $fetch] <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](/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/)! If you have any questions, feel free to reach out on Twitter ([@danpastori](https://twitter.com/danpastori)) or on our [community forum](https://community.serversideup.net)! --- # Advanced Data Fetching with Nuxt 3 > Master advanced data fetching techniques in Nuxt 3. This guide for premium software developers covers watch sources, infinite scrolling, pagination, and best practices for building scalable, production-ready Vue applications. Working with ROAST and Bugflow, both having a Nuxt 3 frontend, I've come across a lot of scenarios where I've had to do some more advanced data fetching with Nuxt 3 and the provided composables. I've written a basic article about [using asyncData() in Nuxt 3](/blog/using-async-data-in-nuxt-3/). This article will be extending on the previous article and covering some more advanced scenarios like automatic watch sources, infinite scrolling, pagination and tips for dealing with multiple async data sources. Let's get started! ## Nuxt 3 Watch Sources with `useAsyncData()` To be honest, this is my favorite feature of any of the new Nuxt 3 data fetching composables. Watch sources works with `useFetch()` and `useAsyncData()` along with their `lazy` counterparts. Let's set up a use case so we can explain how wild this is. Say you have a page that has a variety of filters, settings, parameters, etc used to query an API. In ROAST this would be like the [search page](https://roastandbrew.coffee/search) or in [Bugflow](https://bugflow.io), our bug listing page. These pages allow the user to set their parameters, filters, etc. and call the API to get data that matches what they are looking for. Every time the user updates one of these filters, you will have to re-query the API. Normally, this would be done by calling a function, or building a reactive query string. However, with watch sources, this is much easier! [Watch sources](https://nuxt.com/docs/api/composables/use-async-data#params) allow you to "watch reactive sources to auto-refresh". What does that mean? That means when the user changes a parameter you used in your query, the data auto refreshes. It's amazing! Let's look at the following code: ```vue [Example of watch sources] <template> <div> <ul> <li v-for="cafe in cafes.data" :key="cafe.id" v-text="cafe.company.name+' - '+cafe.location_name"></li> </ul> </div> </template> <script setup> const search = ref(''); const page = ref(1); const { data: cafes, error } = await useAsyncData( 'cafes', () => $fetch( `/api/v1/cafes`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { page: page.value, search: search.value, } } ), { watch: [ page, search ] } ); </script> ``` Before we start breaking this apart, I'm using `useAsyncData()` but watch sources work with all the new data fetching composables. In this example we are loading the first set of cafes from the [https://api.roastandbrew.coffee/api/v1/cafes](https://api.roastandbrew.coffee/api/v1/cafes) endpoint. This is a paginated resource and we can search cafes to get the find the ones we are looking for. So we set up the following filters: ```javascript [Filter set up] const search = ref(''); const page = ref(1); ``` Next, we set up our data fetching request and pass these two parameters in the `params` section: ```javascript [Adding filters to the watch data] const { data: cafes, error } = await useAsyncData( 'cafes', () => $fetch( `/api/v1/cafes`, { // ... other options params: { page: page.value, search: search.value, } } ), { watch: [ page, search ] } ); ``` Looks pretty familiar so far! However, the magic comes in the third parameter to the `useAsyncData()` composable and that's the `watch` key. What this does is allows us to pass an array of reactive sources that will re-query when changed. Let's start with `page` where we want to add simple next and previous pagination. When the user increments or decrements the `page` value, we want to refresh the data source with the next paginated set of data. Instead of calling `refresh` (a method in the composable) or dynamically computing the query string, we can automatically load the new data instantly in Nuxt 3 when the watch source changes. Add the following methods: ```vue [Pagination example] <script setup> //... Other settings and async data const previous = () => { if( page.value != 1 ){ page.value = page.value -1 ; } } const next = () => { if( page.value + 1 <= cafes.value.last_page ){ page.value = page.value + 1; } } </script> ``` Notice how these methods don't explicitly call a refresh or another method to reload the data? That's because `page` is one of the watch sources defined. All you have to do is increment or decrement the `page` value. This will automatically refresh the data! Super convenient for dynamic data fetching with Nuxt 3 and implementing searches, pagination, or other filters. The `location` variable works the same way. Once it changes, a new request to load the data will be made. However, you will probably want to debounce the input if it's a text search or you will send way too many API requests and blow through the throttling! Here's our final code example: ```vue [Full example of UI and Watch Sources] <template> <div> <ul> <li v-for="cafe in cafes.data" :key="cafe.id" v-text="cafe.company.name+' - '+cafe.location_name"></li> </ul> <button @click="previous()" v-if="page > 1">Previous</button> <button @click="next()" v-if="page < cafes.last_page">Next</button> </div> </template> <script setup> const search = ref(''); const page = ref(1); const { data: cafes, error } = await useAsyncData( 'cafes', () => $fetch( `/api/v1/cafes`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { page: page.value, search: search.value, } } ), { watch: [ page, search ] } ); const previous = () => { if( page.value != 1 ){ page.value = page.value -1 ; } } const next = () => { if( page.value + 1 <= cafes.value.last_page ){ page.value = page.value + 1; } } </script> ``` I really love how the watch sources clean up the code base and make the experience feel so much more optimized and dynamic. Let's touch on another advanced data fetching scenario, infinite scrolling, or "compounding/appending" requests. ## Append Data from `$fetch` in Nuxt 3 Specifically in Bugflow, we ran into a scenario where we wanted a "compounding" or "infinite scroll" type scenario. We had a bug listing screen where the user can see all bugs on a project, newest to oldest. As they scrolled, they had the option to load more. In this scenario we needed to keep appending the data returned from a data fetch with Nuxt 3 in order to show it all on one screen. For this scenario, I recommend using the `$fetch` method that's globally available to directly call the API. Why not a composable? You can, but the composables provided want to replace the data every request. That's how they are designed. We want to append the data. Take a look at the code: ```javascript [Example with appending data (Infinite Scroll)] const page = ref(1); const lastPage = ref(1); const companies = ref([]); const pending = ref(false); onMounted(() => { loadCompanies(); }) const loadMore = () => { if( page.value + 1 <= lastPage.value ){ page.value = page.value + 1; loadCompanies(); } } const loadCompanies = () => { pending.value = true; $fetch(`/api/v1/companies`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { page: page.value } }).then( function( companies ){ appendCompanies( companies.data ); pending.value = false; lastPage.value = companies.last_page; }); } const appendCompanies = ( newCompanies ) => { newCompanies.forEach( ( company ) => { companies.value.push( company ); }); } ``` As you can see, the code is a little bit more verbose than the elegant way you'd typically load data with `useFetch()` or `useAsyncData()`. However, the power is there. Let's start at the top: ```javascript [Variable set up for infinite scroll] const page = ref(1); const lastPage = ref(1); const companies = ref([]); const pending = ref(false); ``` Right away we declare 4 variables, `page`, `lastPage`, `companies`, and `pending`. Since we are loading a paginated resource, we keep track of the `page` (current page we are on) and the `lastPage`(the final page of results for the resource). We also implement our own simple `pending` state while more data loads. If you were using the `useAsyncData()` composable, this would already be available for you. Let's jump to our `loadCompanies()` method: ```javascript [Load companies method] const loadCompanies = () => { pending.value = true; $fetch(`/api/v1/companies`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { page: page.value } }).then( function( companies ){ appendCompanies( companies.data ); pending.value = false; lastPage.value = companies.last_page; }); } ``` What this does is first, set the `pending` value to `true`. This allows us to display a loader or handle other events to show the user the data is loading. Next we call `$fetch` on our endpoint and pass the `page` param. This will grab the current paginated chunk of companies from our API. Most importantly, we listen to the successful return of the promise with `.then()`callback. ```javascript [Callback to append the loaded data] .then( function( companies ){ appendCompanies( companies.data ); pending.value = false; lastPage.value = companies.last_page; }); ``` Upon success, we take the response, append the new companies to the local reactive companies array, set `pending` to `false`, and save the last page so we know when to not load any more. The `appendCompanies()` method is the guts of our "compounding" or "infinite scrolling" takes place: ```javascript [Functionality to append companies] const appendCompanies = ( newCompanies ) => { newCompanies.forEach( ( company ) => { companies.value.push( company ); }); } ``` This simple method takes the new companies, iterates over them, and appends them to the local `companies` array which is reactive. We can then display the reactive companies in our template like: ```html [UI to display all companies] <template> <div> <h2>Companies</h2> <ul> <li v-for="company in companies" :key="company.id" v-text="company.name"></li> </ul> </div> </template> ``` Finally, we have our `loadMore()` method. This method simply increments our page number and calls the `loadCompanies()` method. Unlike in the first section, using a watch source, we aren't using a composable so we have to call the method ourselves. The `loadMore()` method looks like: ```javascript [Load more method] const loadMore = () => { if( page.value + 1 <= lastPage.value ){ page.value = page.value + 1; loadCompanies(); } } ``` For the sake of thoroughness, I also initially call the `loadCompanies()` method with the `onMounted()` hook. You don't have to if you want to pre-populate your page on the server side. Our final implementation should look like: ```vue [Final implementation of the infinite scroll] <template> <div> <h2>Companies</h2> <ul> <li v-for="company in companies" :key="company.id" v-text="company.name"></li> </ul> <div v-if="pending">Loading...</div> <button @click="loadMore()">Load more</button> </div> </template> <script setup> const page = ref(1); const lastPage = ref(1); const companies = ref([]); const pending = ref(false); onMounted(() => { loadCompanies(); }) const loadMore = () => { if( page.value + 1 <= lastPage.value ){ page.value = page.value + 1; loadCompanies(); } } const loadCompanies = () => { pending.value = true; $fetch(`/api/v1/companies`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { page: page.value } }).then( function( companies ){ appendCompanies( companies.data ); pending.value = false; lastPage.value = companies.last_page; }); } const appendCompanies = ( newCompanies ) => { newCompanies.forEach( ( company ) => { companies.value.push( company ); }); } </script> ``` You can implement this in a component or in a page itself. We implemented it in a table listing on Bugflow. The user initially sees the newest bugs, but can view more as they scroll down. I've also mentioned "infinite" scrolling, but as you can see in the template, I have a button that calls the `loadMore()` method. However, there is a simple VueUse method where you can check if an element, such as an "end of list" element, is visible and then call `loadMore()`. And just like that you have infinite scrolling! Check out [useElementVisibility(](https://vueuse.org/core/useelementvisibility/)) for more info. ## Helpful Nuxt 3 Data Fetching Hints Here are a few hints that can help you when you make more advanced data fetching requests with Nuxt 3. ### Renaming the Refresh Method in Nuxt 3 As your app grows, you will no doubt hit a time where you will have to do multiple `asyncData()` requests on the same page. Let's look at loading a few companies and cafes on the same page from the ROAST API: ```vue [Standard refresh method] <script setup> const search = ref(''); const { data: cafes } = await useAsyncData( 'cafes', () => $fetch( `/api/v1/cafes`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { search: search.value, } } ) ); const { data: companies } = await useAsyncData( 'companies', () => $fetch( `/api/v1/companies`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { search: search.value, } } ) ); </script> ``` Note: You can also do simultaneous `asyncData()` requests like [I went through here](/blog/using-async-data-in-nuxt-3/). Let's just use the above code as an example for now. It will work great right away. But what if you need to actually call the `refresh()` method provided by the composable for each data source. When destructuring `refresh()` from the composable, you will have two methods with the same name. This will not work! To rename the destructured `refresh()` method, simply destructure each as follows: ```vue [Renamed refresh method] <script setup> const { data: cafes, refresh: refreshCafes } = await useAsyncData( 'cafes', () => $fetch( `/api/v1/cafes`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { search: search.value, } } ) ); const { data: companies, refresh: refreshCompanies } = await useAsyncData( 'companies', () => $fetch( `/api/v1/companies`, { method: 'GET', baseURL: 'https://api.roastandbrew.coffee', params: { search: search.value, } } ) ); </script> ``` Now you can call `refreshCafes()` and `refreshCompanies()` when you need to! ### When to use `refresh()` vs a Watch Source? The simple answer, use `refresh()` when you know data on the server side has changed you need to reload the data on the client side. Use a watch source when the user changes parameters that need to be sent to the server. For example, let's say you have a list of companies. A user deletes a company. This will not change a query parameter, but the data on the server side will change. Run `refresh()` and you will have accurate data. If you want to filter results from the API via a search parameter, set that search parameter as a watch source. When a user changes their query, fresh and accurate data will be re-loaded from the API. Quick note, probably want to use a debounce method from VueUse so you don't query the API on every keystroke or you will hit a throttle limit in no time! ## Conclusion Hope this helps with your advanced data fetching in Nuxt 3! If you have any questions, feel free to hit me up on [Twitter](https://twitter.com/danpastori) or in our [Discord](/discord/)! If you want to see how these pieces fit into the scope of an entire application, [we have a book available](/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). With the purchase of the complete package you can see the entire source code behind [ROAST](https://roastandbrew.coffee/). --- # Using Fetch API with Vue.js > Learn how to use the Fetch API with Vue.js to make HTTP requests and handle responses in your web applications. ## About this series For many years, I've been a huge fan of [Axios](https://github.com/axios/axios) (actually, I still am). It's made API requests extremely modular, re-usable, functional, and so many other amazing benefits. However, when I learned about the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and it's growing popularity, I had to check it out! What is the Fetch API? According to [Mozilla docs](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch), "The [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) provides a JavaScript interface for accessing and manipulating parts of the HTTP pipeline, such as requests and responses." I strive to limit dependencies in projects, so working on implementing the Fetch API in our projects made a ton of sense. This course will show a side by side comparison of how to begin the migration from Axios to Fetch using VueJS. ## What you'll learn - Update from Axios to Fetch API using VueJS - Upload files with the Fetch API - Perform simple API requests using the Fetch API --- ## Posts in this series # Basic GET Requests with Fetch API and VueJS > A detailed comparison of making GET requests using Fetch API versus Axios in Vue.js applications, including response handling, error management, and best practices for API integration. The best way to learn something or solve a problem is to break it down into the smallest pieces. While transitioning from Axios to the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) and integrating it into my VueJS application, I had to start small. I actually wrote both requests side by side so I could migrate in pieces and ensure the integrity of the app. In this tutorial, we are going to be making some basic `GET` requests using Fetch and comparing those requests to Axios. I tend to learn best when I need to solve a problem and apply new tools to do it. For this course, I'm building a budgeting app (I actually am too as a side project, if it ever gets polished up, we'll release it). This will provide a great example of the different use-cases we need to account for during our migration. Since Fetch is a web browser API, there's no need to configure it [globally](/blog/configuring-axios-globally-with-vuejs/)! All methods are made available through the web browser. For this tutorial we are just creating a simple VueJS component. This VueJS component will call the `/api/v1/transactions` endpoint on our API through both the Fetch API and Axios so you can see how they both work. ## Step 1: Create your API Request For loading our transactions, I made 2 methods (one with the Fetch API and the other with Axios) within the same component. I'll show both of the methods and then break down the differences. ### Fetch API GET Request ```javascript [Fetch API Example] methods: { loadFetch(){ fetch( 'https://api.roastandbrew.coffee/api/v1/companies' ) .then( function( response ){ if( response.status != 200 ){ throw response.status; }else{ return response.json(); } }.bind(this)) .then( function( data ){ this.fetchResponse = data; }.bind(this)) .catch( function( error ){ this.fetchError = error; }.bind(this)); } } ``` ### Axios API GET Request ```javascript [Axios API Example] methods: { loadAxios(){ axios.get(' https://api.roastandbrew.coffee/api/v1/companies' ) .then( function( response ){ this.axiosResponse = response.data; }.bind(this)) .catch( function( error ){ this.axiosError = error; }.bind(this)); } } ``` I also had to import the library in to our component: ```javascript [Import the Axios module] import axios from 'axios'; ``` So those are our two requests! From first glance, they are extremely similar! In the next step, we will break down the differences. ## Step 2: Breaking Down the Method Signature So these are very simple GET requests, we will get to the more advanced stuff later on, but starting here we can begin to see the differences between the two requests. So to make the request, the method signature is very similar. Fetch API uses `fetch( END_POINT_URL )` and Axios uses `axios.get( END_POINT_URL )`. Right away you can see that Axios provides a method where you can explicitly call the HTTP Verb of `GET`. You can also do that with: ```javascript [Define the GET method on an Axios Request] axios({ method: 'get', url: END_POINT_URL }) ``` or using their default (which looks just like a Fetch API request): ```javascript [Use the default GET call] axios( END_POINT_URL ) ``` I prefer the `.get()` when using Axios, just because it's easy to read and very explicit. Fetch API doesn't provide that explicit `.get()` method. Nor does it apply the `.post()` method when we get to that later. To specify more options to your request, the method allows for a second parameter which is an object of configuration. We won't touch on that now, but it will look very similar to the Axios method without the explicit `.get()`. ## Step 3: Breaking Down the Method Response This is where the two methods differ a little bit. Both return promises which is nice and both work with `async/await`. However, the `fetch()` API returns a [Response object](https://developer.mozilla.org/en-US/docs/Web/API/Response) when the request completes successfully. This object has a ton of methods and settings you can work with! Axios returns the response in an object as well and it's accessible through `response.data`. To get the data from the request with the Fetch API you can call a method on the `Response` object. In our example we called `.json()` which returns another promise. However, let's take a step up one block of code and look at: ```javascript [Ensure we have a valid response code] if( response.status != 200 ){ } ``` We first check to see if the response completed successfully. Within the Fetch API, the promise will be resolved even if there was a server side error. Axios allows you to catch the server side error with `.catch()`. For me, I can see this being a hard habit to break, I like the `.catch()` syntax. There's another handy method which is `response.ok` which checks to make sure the status code is a `2XX` status code (perfect for 204 and other API type responses). If we don't get an error in our Fetch API request, we then call `response.json()` and chain another promise to load our data: ```javascript [Access the JSON response returned from the server] response.json().then( function( data ){ this.response = data; }.bind(this)); ``` This is called on the implemented [Body](https://developer.mozilla.org/en-US/docs/Web/API/Body) interface. There are a variety of other methods you can parse your response body with as well depending on your circumstance. One thing to note about both of these functions is the `.bind(this)` at the end of them. That statement gives the methods scope to our VueJS component so we can set local variables within it. You have to bind twice since there's a child promise returned in the Fetch API when you convert the response to `json()`. ## Conclusion So this is beginning our journey of learning the Fetch API alongside Axios. So far, in my opinion, what I like about the Fetch API is: - No external library - Powerful enough to give you access to the data and settings you need What I don't like about the Fetch API is: - Parsing the data with another promise (`response.json`) feels weird. - Not catching server side errors with `.catch()` will be a hard habit to break. I'm looking forward to doing some data creation with both libraries and eventually authentication (both handling oAuth Tokens and Laravel Sanctum). Also, I'm interested to see how we can make some API wrappers with Fetch API [like we did with Axios](/blog/build-an-api-wrapper-with-vuejs-axios/). For the rest of the series, I'll be focusing on the side-by-side comparison with Axios and using them both within VueJS. Matt Netkow over at Ionic wrote a super helpful article about [switching to Fetch as well](https://ionicframework.com/blog/replacing-native-plugins-with-web-apis/). If you want to see the actual components that make these Fetch API requests, head [over to our Github repo.](https://github.com/serversideup/fetch-api-vuejs) The Fetch API could also be used instead of Axios if you are creating an [API driven web and mobile application](/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). Feel free to reach out if you have any more questions. On to more advanced requests! --- # Sending POST, PUT, and PATCH Requests with Fetch API and VueJS > Learn how to send POST, PUT, and PATCH requests using the Fetch API in VueJS. This guide covers practical examples, differences from Axios, and best practices for premium developers building robust, production-ready applications. In the [last tutorial](/blog/basic-get-requests-with-fetch-api-and-vuejs/), we covered the differences between the Fetch API and Axios when sending a GET request. Let's take it up another notch and send POST, PUT, and PATCH requests with the Fetch API and compare how these methods operate compared to an Axios request. If you want to jump ahead and see the answer, check out the Github repo for this [component here](https://github.com/serversideup/fetch-api-vuejs/blob/main/src/components/PostPutPatchRequests.vue). You can see the differences right away. If you want to see how this works, keep reading! ## Step 1: Overview For this tutorial, we are going to send data to an API endpoint. We are going to use our publicly available [ROAST API](https://roastandbrew.coffee/). I'll walk through how we send data to an endpoint, apply authorization (these methods require authorization 99.9% of the time) and the differences between the Fetch API and Axios. The endpoint we will be using will be for adding a Brew Method ([https://api.roastandbrew.coffee/api/v1/brew-methods](https://api.roastandbrew.coffee/api/v1/brew-methods)) and one for Updating a Brew Method ([https://api.roastandbrew.coffee/v1/brew-methods/{methodID}](https://api.roastandbrew.coffee/v1/brew-methods/%7BmethodID%7D)). Not to disappoint, but these are protected endpoints and will not create data unless a valid access token is provided. Most endpoints implemented through POST, PUT, and PATCH are protected. These endpoints serve as great examples though and will show in detail the differences between the Fetch API and Axios. ## Step 2: Creating Your POST API Request To show the differences between the Fetch API and Axios, I created the same request twice, one with each tool. The POST requests below are: ### Fetch API POST Request ```javascript [Fetch API POST Request Example] fetch( 'https://api.roastandbrew.coffee/api/v1/brew-methods', { method: 'POST', headers: { 'Authorization': 'Bearer '+this.token, 'Accept': 'application/json', 'Content-Type': 'application/json;charset=utf-8' }, body: JSON.stringify( this.form ) } ) .then( function( response ){ if( response.status != 201 ){ this.fetchError = response.status; }else{ response.json().then( function( data ){ this.fetchResponse = data; }.bind(this)); } }.bind(this)); ``` And with Axios: ### Axios POST Request ```javascript [Axios POST Request Example] axios.post('https://api.roastandbrew.coffee/api/v1/brew-methods', this.form, { headers: { 'Authorization': 'Bearer '+this.token, } } ) .then( function( response ){ this.axiosResponse = response.data; }.bind(this)) .catch( function( error ){ this.axiosError = error; }.bind(this)); ``` Just at first glance you can see that the Fetch API requires a little more configuration than Axios out of the box. Right away I start to think that if we are using the Fetch API in our application, we will have to abstract some of these settings to an object so the code is a little cleaner. Let's dive into the method signatures. ## Step 3: Differences Between Fetch API and Axios POST Request Right away, the two methods look different. The Fetch API is much more complex. This is because Axios provides some standard defaults for what to send and what to expect from the server. As I mentioned above, I think that in order to keep using the Fetch API, we will have to make an easily configurable settings module ourselves. Otherwise, we will end up with what looks like spaghetti code everywhere and more possibilities for errors. Before even diving into the method signature, I had to include ```javascript [Axios Import Statement] import axios from 'axios' ``` In order to use Axios. While on the topic of Axios, let's start with the Axios POST method: ```javascript [Axios POST Method Signature] axios.post('https://api.roastandbrew.coffee/api/v1/brew-methods', this.form, { headers: { 'Authorization': 'Bearer '+this.token, } } ) ``` I love how Axios provides an explicit `.post()` method that allows you to pass parameters directly to it. I find this to be super easy to read and explicit. Axios does have a signature similar to the Fetch API where you can pass the `method` as an option, but honestly, I never use it. The POST method accepts the `URL` as the first parameter. This is the endpoint we will be hitting with data. The second parameter, `this.form` is the data we are sending to the endpoint. For now, this will just be JSON. When we get to files, it will be slightly different to send. The third parameter is any other options we wish to send with the request. In this case, we added an `Authorization: Bearer {token}` header. Like I mentioned before, these methods usually require some sort of authorization to run. If you want to see this component in full where `this.form` and `this.token` are defined, check out the [GitHub repo](https://github.com/serversideup/fetch-api-vuejs/blob/main/src/components/PostPutPatchRequests.vue). That's it for the Axios request! We can now send this request to our endpoint and it will include the data and proper authorization. Moving on to the Fetch API, we start to really see differences with the methods: ```javascript [Fetch API POST Method Signature] fetch( 'https://api.roastandbrew.coffee/api/v1/brew-methods', { method: 'POST', headers: { 'Authorization': 'Bearer '+this.token, 'Accept': 'application/json', 'Content-Type': 'application/json;charset=utf-8' }, body: JSON.stringify( this.form ) } ) ``` Like the Axios POST request, the first parameter is the URL of the endpoint we are sending data to. After that, everything changes. The second parameter in this request is an object that configures everything about the request. First, we define the method as a `POST` request in the settings using `method: 'POST'`. This is different from using Axios where the method is explicitly available with the `.post()` method. Next, we define our headers. The first header we include is the `Authorization: Bearer {this.token}` header similar to Axios. Next, using our API, we have to define the `Accept` and `Content-Type` headers. Axios has these pre-defined, but the Fetch API does not. Your API may differ, but for standard JSON APIs, this is what is required. When we discuss uploading files, this will be different since we will be sending everything as form data. Finally, we have our POST body which is defined as `body: JSON.stringify( this.form )`. Unlike Axios where you can pass JSON as the second parameter, you have to define the `body` explicitly. ## Step 4: Handling a POST Request Response Using the Fetch API Similar to how we handle a [GET request response](/blog/basic-get-requests-with-fetch-api-and-vuejs/), we handle our responses depending on the status: ```javascript [Fetch API Response Handling] if( response.status != 201 ){ this.fetchError = response.status; }else{ response.json().then( function( data ){ this.fetchResponse = data; }.bind(this)); } ``` The only difference is POST requests should return a `201` response code instead of a `200`. ## Step 5: Sending PUT and PATCH requests Using the Fetch API Both of these requests have the exact same signature as a `POST` request when using the Fetch API. The only difference is you have to change the `method:` setting in your request to either `PUT` or `PATCH`. With Axios, you'd call either `axios.put()` or `axios.patch()`. Upon response, the status code may be different as well such as an empty response which would be a `204` that you'd have to check for. ## Conclusion After running through the examples with both Fetch API and Axios, I'm definitely seeing the need to make some sort of standardized settings if I want to continue with the Fetch API. Axios makes this extremely easy using their configuration set up. I do like the idea of removing another unnecessary library if I don't need it, but I love Axios and honestly it will be hard to switch. I'll run through a few more examples with the Fetch API and see what other differences come up. --- # File Uploads using Fetch API and VueJS > Learn how to handle file uploads using the Fetch API and Vue.js, including step-by-step examples, progress tracking, and integration tips for modern web apps. We've talked about file uploads with [Axios a ton of different ways](/guides/guide-uploading-files-vuejs-axios/). Since we are just comparing the Fetch API with Axios, I won't do as many examples. However, we will touch on the differences between the Fetch API and Axios when uploading files. Once again, if you want to see exactly how this component works, check out our [GitHub repo](https://github.com/serversideup/fetch-api-vuejs). ## Step 1: Preparing Your File Upload with Fetch API For this request, we will be using a `POST` request. However, you can use any proper HTTP Verb such as `POST`, `PUT`, or `PATCH`. The two main differences between this type of request that includes a file and one that doesn't is the `Content-Type` is not JSON and the body is Form Data. This is exactly the same as Axios. Let's dive into that. ## Step 2: Uploading a File with Fetch API and VueJS The first thing we need to do is create our file input in our VueJS template. It should look like this: ```vue [File input template] <input type="file" @change="handleFileUpload( $event )"/> ``` This will be the same whether you are using Axios or the Fetch API. The big thing to note is we apply a change listener through `@change` and pass the `$event` to the listener. Let's add that method to our component: ```javascript [File upload handler method] handleFileUpload( e ){ this.form.icon = e.target.files[0]; } ``` What this does, is once a user selects a file, we grab the file from the event and store it locally. We store it to our `form.icon` reference so we can use it later on. What's stored is a representation of the `File` object {LINK_TO MDN}. We can use this to send the file through the Fetch API or Axios. Now it's time to implement both of our requests: ### Fetch API File Upload ```javascript [Fetch API file upload implementation] let formData = new FormData(); formData.append( 'method', this.form.method ); formData.append( 'icon', this.form.icon ); fetch( '<https://api.roastandbrew.coffee/api/v1/brew-methods>', { method: 'POST', headers: { 'Authorization': 'Bearer '+this.token, 'Accept': 'application/json', 'Content-Type': 'multipart/form-data' }, body: formData } ) .then( function( response ){ if( response.status != 201 ){ this.fetchError = response.status; }else{ response.json().then( function( data ){ this.fetchResponse = data; }.bind(this)); } }.bind(this)); ``` ### Axios File Upload ```javascript [Axios file upload implementation] let formData = new FormData(); formData.append( 'method', this.form.method ); formData.append( 'icon', this.form.icon ); axios.post(' <https://api.roastandbrew.coffee/api/v1/brew-methods>', formData, { headers: { 'Authorization': 'Bearer '+this.token, 'Content-Type': 'multipart/form-data' } } ) .then( function( response ){ this.axiosResponse = response.data; }.bind(this)) .catch( function( error ){ this.axiosError = error; }.bind(this)); ``` Both of these request should look very similar to sending a [standard POST request](/blog/sending-post-put-and-patch-requests-with-fetch-api-and-vuejs/). However, there are a few key differences. Let's look at the differences between Fetch API request and the Axios. The first thing we do is implement a `FormData()` [object](https://developer.mozilla.org/en-US/docs/Web/API/FormData).. This allows us to handle and send files. We need this request to be a `FormData` object so we can send files to the server. ```javascript [FormData initialization] let formData = new FormData(); formData.append( 'method', this.form.method ); formData.append( 'icon', this.form.icon ); ``` The next difference is we need to change the `Content-Type` to `'Content-Type': 'multipart/form-data'`. This alerts the endpoint we are using a `multipart/form-data` request and the server should look for attached files. We have to add this header to Axios as well. The final difference is in the `body` setting. Instead of `JSON.stringify()` we pass the `FormData()` object we created (`formData`) as the setting for `body`. This is very similar to the Axios request where we pass `formData` as the second parameter to the `axios.post()` method. ## Gotchas There are a few gotchas with doing file uploads. First, as I mentioned in the intro, you can use `PUT` to send files to the server as well. However, if you are using a Laravel backend, you have to send this as a `POST` request and append `_method=PUT` to the body (`formData.append('_method', 'PUT')`). Laravel will not pick up a `multipart/form-encoded` request through `PUT`. Next, if you've checked out the [Uploading Files with VueJS and Axios Course](/guides/guide-uploading-files-vuejs-axios/), you'd notice a progress indicator. With Axios, you can easily build in a progress indicator to show progress on uploading large files. As of this writing, you can not do this with the Fetch API. I really hope they fix this soon since it's huge for UX. If I run across a fix, I'll update this article. ## Conclusion Uploading a file with the Fetch API and VueJS should look very similar to sending a `POST`, `PUT`, or `PATCH` [request](/blog/sending-post-put-and-patch-requests-with-fetch-api-and-vuejs/) with only a few changes. Next up, we will try to abstract some of the settings to a standard location to clean up the Fetch API requests and make them easier to use. If you have any questions, be sure to reach out on Twitter ([@danpastori](https://twitter.com/danpastori)) or on the community! --- # Fetch API Components with Vue 3 Composition API > Discover how to fetch API components using the Vue 3 Composition API, including data fetching patterns, reactivity, and integration tips for modern Vue apps. One of the biggest pain points I see between using the Fetch API and Axios is the way Fetch is configured out of the box. I love the power, but Axios comes with some default config and a way to make it easy to set global settings. I want to see if I can abstract some of the settings that are generally used across all API requests and make them reusable with the Fetch API. I'm going to structure this reusability similar to building an [API wrapper with axios](/blog/build-an-api-wrapper-with-vuejs-axios/). The main difference (besides using Fetch instead of Axios), is we are going to implement these methods using the new Vue 3 Composition API! ## Step 1: Plan our API Let's keep it really simple for this tutorial and do an example of sending data to the API and retrieving data from the API. We will use the [https://api.roastandbrew.coffee/api/v1/brew-methods](https://api.roastandbrew.coffee/api/v1/brew-methods) end point. We will create a method that performs a `GET` request and loads all of the brew methods available and a method that sends `POST` data to the endpoint to create a brew method. With that being said, we will need some standardized settings for our Fetch API wrapper. These will be: - token → The authorization token we will add to the headers - headers → The default headers represented by the [Header() object](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#headers) - baseURL → The base URL of our API We can then set up our config and re-use it with all of our API requests. ## Step 2: Create Directories and Config The first step I take when making reusable API wrapper modules is to create a directory to house our implementation. This is very similar to [Building an API Wrapper with VueJS and Axios](/blog/build-an-api-wrapper-with-vuejs-axios/). The only difference is with our Fetch API implementation, we will create a small config object that we can control in one area that has some defaults already defined for us. Since we are working with Brew Methods, let's first create an `api/resources/BrewMethods.js` file. I usually put all API resources in the root level of my source javascript: ```javascript [Initial BrewMethods API structure] export default { index( ){ }, store( data ){ } } ``` For now these are just the place holder methods we will be implementing. We can leave it that way for now. Next, let's create our `api/config.js` file: ```javascript [API configuration settings] export const APISettings = { token: '', headers: new Headers({ 'Accept': 'application/json' }), baseURL: 'https://api.roastandbrew.coffee/api/v1', } ``` This is just a simple JSON object that just houses a few of our global values we will be implementing in all of our requests. The `token` field will hold our access token. WARNING!! If you are working with a third party API and you are wrapping it's resources for all users of your application to use. DO NOT STORE YOUR TOKEN HERE! This is meant to be a personal access token. Secret tokens should never be stored in javascript. It will lead to the ability for users to inspect and grab the token and act on your app's behalf! ### Working with Headers() The next `headers` field allows us to set default headers that we will use in each request. The field instantiates the Headers interface ([](https://developer.mozilla.org/en-US/docs/Web/API/Headers)[https://developer.mozilla.org/en-US/docs/Web/API/Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers)). Since our API returns JSON, we default the `Accept: 'application/json'` header. When we implement our API wrapper, we will add other headers to handle authorization and such. Finally, our `baseURL` is the base of the endpoints we will be calling. This is a nice little feature so if the API updates, we can just switch to a different API version with ease and all of our requests will work. ## Step 3: Include Config in Wrapper Now that we have our config set up, we need to include it in every endpoint wrapper so we can re-use the settings. To do this, add the following to the top of the file: ```javascript [Import API settings] import { APISettings } from '../config.js'; ``` Now we have access to our config from within our API Wrapper. Let's build out each of the endpoints, then we can include them in our components with the Vue 3 Composition API. ## Step 4: Build out Reusable API Endpoints The biggest goal with this is to store each endpoint request in a way that we can use it in whatever component we need. Why? So if we need to make changes to the endpoint we can update it in one place and it's fixed in our entire application. Let's shell out our `GET` request to load all of the Brew Methods. To do this, add the following code to your `BrewMethods.js` file: ```javascript [GET request implementation] index( ){ return fetch( APISettings.baseURL + '/brew-methods', { method: 'GET', headers: APISettings.headers } ) .then( function( response ){ if( response.status != 200 ){ throw response.status; }else{ return response.json(); } }); }, ``` Now, when we call `BrewMethods.index()` we will return the `fetch()` method which in turn returns a promise that we can listen to. Two things to notice in our method call. First, we call `APISettings.baseURL + '/brew-methods'` as our endpoint. This appends the baseURL we set up in Step 3 to build out our endpoint. Next, we reference the same config with the `headers: APISettings.headers` setting. All of our headers that are in the global config are appended to the request. In the next request, we will append our authorization header. You can do that in this request as well, but we are just building up the settings as we go. We then do one consistent "pre-process" with the returning of the request data. That is, upon completion, if the status is not `200` (successful response), we `throw response.status` which throws an error with the code that was returned. This way we can handle that error in a `catch` callback. If the request was successful, we return `response.json()` which decodes the JSON and returns the promise we can handle in our method. Let's add our `POST` request here while we are at it: ```javascript [POST request implementation] store( data ){ APISettings.headers.set('Content-Type', 'multipart/form-data'); APISettings.headers.set('Authorization', 'Bearer '+APISettings.token); return fetch( APISettings.baseURL + '/brew-methods', { method: 'POST', headers: APISettings.headers, body: data } ) .then( function( response ){ if( response.status != 201 ){ throw response.status; }else{ return response.json(); } }); } ``` The major differences between sending the request to the server and getting data from the server are the headers, and the way the body is encoded. Right away, we set two extra headers. The first let's the server know that the `Content-Type` is `multipart/form-data`. This header allows us to send files to the server. The second header `Authorization` sends the token we set in our config as the `Authorization: Bearer` token. This allows properly authenticated users to create a resource. Ever other part of this request should look very similar to [uploading files](/blog/file-uploads-using-fetch-api-and-vuejs/). We do the same pre-processing where we throw an error if the status is not `201` which is "Resource Created". We now have a reusable Fetch API module. Let's implement these re-usable endpoints through the Vue 3 Composition API. ## Step 5: Set Up Composition API to Use API Module Let's say we have a component that handles the Brew Method resource in our application ( [check out our GitHub for an example](https://github.com/serversideup/fetch-api-vuejs/blob/main/src/api/resources/BrewMethods.js) ). The first thing we need to do is import our module into our component: ```javascript [Import BrewMethods API] import BrewMethodsAPI from '../api/resources/BrewMethods.js'; ``` Now that we have the API module imported into our component, let's use the Vue 3 Composition API to set up what we need from the module. First, we will look at the `setup()` hook. This is where the magic takes place with the Composition API. We can set up our component to use pieces of other modules allowing us to easily re-use code. In [past tutorials](/blog/build-an-api-wrapper-with-vuejs-axios/), I've done this by just making local methods that call our module's methods. This works too! However, if you want a standardized validation before the form is submitted and other helper methods, you can easily re-write a ton of code and it's not that re-usable. With the Composition API, I can store all of my validator methods with the request so it's all localized! ### The setup() Method Our `setup()` method looks like this: ```javascript [Vue 3 Composition API setup method] setup( ){ const brewMethods = ref({}); const loadBrewMethods = async() => { brewMethods.value = await BrewMethodsAPI.index(); }; const form = { method: '', icon: '' }; const saveBrewMethod = async() => { let formData = new FormData(); formData.append('method', form.method); formData.append('icon', form.icon); await BrewMethodsAPI.store( formData ) } return { brewMethods, loadBrewMethods, form, saveBrewMethod } }, ``` For those who have not worked with the composition API, this may look a little bit unique. Before we dive in, add the following line to the imports on your component: ```javascript [Import Vue ref] import { ref } from 'vue'; ``` Don't worry, we will break this all down. Let's start at the top of the method: ```javascript [Initialize reactive data] const brewMethods = ref({}); const loadBrewMethods = async() => { brewMethods.value = await BrewMethodsAPI.index(); }; ``` Right away we initialize a `brewMethods` variable to `ref({})`. What this does is create a reactive piece of data using the `ref()` helper. Right now, we just initialize it to an empty object. Vue will now make whatever is set to the value reactive so we can work with it accordingly. ### Building our async() Function Next, we define `const loadBrewMethods` and set it equal to an `async()` function. Since the Fetch API returns a promise, we can clean up the request by using `async/await`. Inside the `async` wrapping, we make our call to `BrewMethodsAPI.index()`. What is returned from this method call is set to `brewMethods.value` which will reactive for use within our component. At the very end of our `setup()` method, we return `brewMethods` and `loadBrewMethods` (as well as some other variables we will talk about next). By returning the `brewMethods` variable and `loadBrewMethods` function, we can now access that from within our component. Any call to `this.loadBrewMethods()` will call our reusable module! Adding the request to create a brew method is very similar in the `setup()` method: ```javascript [Form handling and submission] const form = { method: '', icon: '' }; const saveBrewMethod = async() => { let formData = new FormData(); formData.append('method', form.method); formData.append('icon', form.icon); await BrewMethodsAPI.store( formData ) } ``` The big thing I'd like to point out is the `const form`. We return the `form` from our `setup()` method. This allows us to use `v-model` directives and bind directly to the pieces of data returned. The other difference is we are using `FormData()` to send the data to our endpoint. That's why we instantiate a new `FormData()` object and send the data from our form this way. This allows us to send a file. For more information about uploading files with Fetch API, check out [File Uploads using Fetch API and VueJS](/blog/file-uploads-using-fetch-api-and-vuejs/). ## Conclusion Abstracting these requests into reusable components saves so much time and maintenance. Using the Vue 3 Composition API makes this a lot easier as well. If you have any questions, reach out on the community, I'd be happy to help! If you want to see how all of this works together, [check out our GitHub repo](https://github.com/serversideup/fetch-api-vuejs). You can see our entire component there. --- # Using Laravel Cashier with VueJS SPA and Laravel Passport API > Learn how to use Laravel Cashier with VueJS SPA and Laravel Passport API. Managing subscriptions within your product can be challenging, especially when dealing with complex payment systems. Laravel Cashier and Stripe significantly simplify this process, but keeping up with Stripe's frequent updates and understanding all the available options can be overwhelming. The Laravel Cashier documentation, while comprehensive, can be complex to navigate due to Stripe's extensive feature set. This complexity is further amplified when implementing subscription management through a Single Page Application (SPA) that communicates with a Laravel API. This comprehensive course will guide you through installing Laravel Cashier and implementing subscription management features, including payment method storage and subscription management. We'll cover best practices and practical tips that will benefit both SPA-based applications and traditional web applications using Stripe. ### Prerequisites Before starting this course, you'll need: - A working Laravel 6.x installation - A basic Single Page Application built with VueJS - Laravel Passport installed and configured If you haven't set up your development environment yet, you can follow our tutorial on [API Driven Development With Laravel and VueJS](/guides/api-driven-development-laravel-vuejs/). While this tutorial uses Laravel 5.6, the installation process remains largely the same for Laravel 6.x. For Laravel Passport setup, you can refer to either: - [The official Laravel Passport documentation](https://laravel.com/docs/6.x/passport) - Our guide on [Installing and Configuring Laravel Passport](/blog/installing-configuring-laravel-passport/) Let's begin our journey into implementing subscription management with Laravel Cashier and Stripe! ## What you'll learn - How to enable users to subscribe to your app through an API using an SPA - Save Stripe credit card payment details for your users through an API - Allow users to manage their subscription to your app through an SPA --- ## Posts in this series # Installing Laravel Cashier on Laravel 6.x > Learn how to install Laravel Cashier on Laravel 6.x for subscription billing, including setup instructions, configuration, and integration tips for Stripe payments. So to kick off this mini-series, we first need to Install Laravel Cashier. The documentation ([https://laravel.com/docs/6.x/billing](https://laravel.com/docs/6.x/billing)) to install Laravel Cashier is pretty straight forward and will get you where you will need to be within your application. We will be using the newest version of the Laravel Cashier Package (10.2.1). I'll step you through the process and explain a few things that I thought stood out along the way. ## 1. Install Laravel Cashier Through Composer This is really straight forward. Like the documentation states, you just need to run `composer require laravel/cashier` and voila! You are ready to rock and roll! ## 2. Migrate the Database to Contain the Fields Needed For Stripe Integration For our course, we aren't going to do anything really tricky. We are simply going to allow users to subscribe to plans within our application. This will require a few extra database columns and tables to make this work. When installing Laravel Cashier, you will get a couple database migrations along with the package. To migrate these changes simply run `php artisan migrate`. A quick note, we are using the default `users` table that came with Laravel, so the columns needed will be added to that. If you are using your own authentication table or allowing a different entity like a team to subscribe to your application, you probably should read the documentation on overriding these migrations. This migration will add the following columns to the `users` table: `stripe_id`: The unique identifier for Stripe for the user `card_brand`: The brand of the card they have on file `card_last_four`: The last four digits of the card that's on file `trial_ends`: The date when the trial period for your application ends (if you are doing a trial period) The migration will also add a `subscriptions` table which will be in relation to the `user`. This table contains the following columns: `id`: Unique ID of the subscription `user_id`: The ID of the user who has the subscription. References the `users` table. `name`: The name of the subscription (this is set in Stripe, will talk about soon!) `stripe_id`: The unique ID of the subscription for the user `stripe_status`: The status of the subscription such as `active` `stripe_plan`: The ID of the plan that the user is subscribed to (you set this up within Stripe and will discuss soon!) `quantity`: The amount of the subscriptions the user has (in our case, this will be 1) `trial_ends_at`: When the trial ends at `ends_at`: When the subscription ends at if they cancel ## 3. Extend User Model to Account For Stripe The documentation to do this is found here: [https://laravel.com/docs/6.x/billing#billable-model](https://laravel.com/docs/6.x/billing#billable-model). What you will need to do is open up your User model and add the `Billable` trait. This will ensure that you can call the methods used to perform billable processes on the entity. In our example, the file is our `User.php` model. By default this is located in the `/app` directory. We will need to add the `Billable` trait like: ```php [User Model with Billable Trait] use Laravel\Passport\HasApiTokens; use Laravel\Cashier\Billable; class User extends Authenticatable { use HasApiTokens, Billable; } ``` Pretty much the same as the documentation! Notice, we've already installed Laravel Passport so the user has the `HasApiTokens` trait which we will be heavily relying on later when we call our API. Next, open up your environment variable file and add the following line: `CASHIER_MODEL=App\User;` This just ensures that Laravel Cashier knows what model it should use to perform billing methods. Yes, this is the default, but I like it explicit especially if your application grows and you move the models to a different directory, it's a nice reminder to configure this later. If it's not in the .env I tend to forget about it. ## 4. Add Placeholders for API Keys Like I mentioned in the last step, I like to have placeholders in my `.env` file even if I haven't configured the variables yet. So before we start to diverge from the official Laravel docs, add the following variables to your `.env` file: ```env [Stripe Environment Variables] STRIPE_KEY=your-stripe-key STRIPE_SECRET=your-stripe-secret ``` This is mentioned in the documentation as well, but we will be setting up this information in the next section. ## Next Steps So to be honest, we've essentially run through the Laravel Docs for setting up Laravel Cashier. I included what we needed for this course and we will begin to dive in deeper in the next tutorial where we set up our Stripe account. We will also be taking some of the javascript that is in the documentation and making some Vue components to work in our SPA. On to setting up Stripe! --- # Configure Stripe to Work with Laravel Cashier in Laravel 6 > Step-by-step guide to configuring Stripe with Laravel Cashier in Laravel 6, including API key setup, environment configuration, and integration tips for seamless billing. So we have [Laravel Cashier installed](/blog/installing-laravel-cashier-on-laravel-6-x/), now it's time to set up Stripe so we can actually bill for the app's services. This is where I felt there was a gap in documentation on the web. Laravel has their side documented beautifully, Stripe also has beautiful documentation, but they were kind of in separate columns. I hope this helps to merge the two together and make the billing process a breeze! ### Pre-Requisites A functioning Stripe account. ## 1. Log Into Stripe You should have at least signed up for Stripe and created an account. From there, it's pretty easy to create a "New Account" which would be your product. In our case, we have a sign in for multiple accounts, so I had to create a new account for the app I was making. Either way, you will need an account for the name of your app. Once you log into the dashboard, you will see the account you are on in the top left Corner: ![](/blog/configure-stripe-to-work-with-laravel-cashier-in-laravel-6/Stripe-Dashboard.png) ## 2. Grab Your API Keys This is the key (pun intended) to binding everything together. To do this, look at the bottom left of the dashboard and find `Developers`. Click that link. ![](/blog/configure-stripe-to-work-with-laravel-cashier-in-laravel-6/Stripe-Developers.png) When you open up that screen, you will see a sub menu called `API keys`. This is where we will grab our keys from. We will be doing a lot more work in this part in the upcoming tutorials when we discuss webhooks and events. Once you land on the `API keys` page you will see 2 keys, a Publishable key and a Secret key. There are 2 sets of these keys, `Live` and `Test`. For this tutorial, we will be using the `Test` keys. MAKE SURE that you swap these out for the `Live` keys when you push your product to production or you WILL NOT get paid! ![](/blog/configure-stripe-to-work-with-laravel-cashier-in-laravel-6/Stripe-Keys.png) Make sure you keep your secret key secret! This is what gets passed along server side with your requests. The publishable key you will place in your Javascript file to use the Stripe.js javascript framework. We will get into that more when we start working with Stripe Elements. ## 3. Place API Keys in your .env Now that you have your keys, go back to your Laravel install and open up the `.env` file in the root of your app. Place both of these keys in the placeholders we set up in the last tutorial. Your `.env` file should look like this: ```text [Stripe ENV Variables] CASHIER_MODEL=App\User; STRIPE_KEY=pk_test_TEST_KEY_RANDOM_STRING STRIPE_SECRET=sk_test_SECRET_KEY_RANDOM_STRING ``` Once you have these in your `.env` file, you should be ready to continue the Stripe setup! In the next tutorial, we will go into the process of creating your subscriptions! --- # Creating SAAS Products in Stripe to Sell with Laravel Cashier > Learn how to create and sell SAAS products using Stripe and Laravel Cashier. This step-by-step guide walks you through setting up products, adding pricing plans, and preparing your Laravel app for seamless subscription billing and recurring payments. Now we are getting to the meat of the application! This is where we will be adding our products and plans in Stripe so we can allow users to subscribe to them through Laravel. Up to this point, we have [Laravel Cashier](/blog/installing-laravel-cashier-on-laravel-6-x/) (the official Stripe package installed) and we've grabbed our API tokens from Stripe and placed them in our `.env` file. Next up, we need to add some products and plans for users to put a card on file and pay for! This is specifically where I got lost when I first did this so I hope it helps fill the gaps with other people's implementation. ## 1. Adding a Product The first thing we will need to do is add a product. To do this, make sure you are in the Stripe dashboard and go to `Billing`. Once on the `Billing` page, you will get a sub-menu where there's an link called `Products`. Click that link. ![](/blog/creating-saas-products-in-stripe-to-sell-with-laravel-cashier/Stripe-Products.png) You might get confused with `Subscriptions` since we are working with users subscribing to features. However, `Subscriptions` is a list of users who are subscribed to a product. The way I look at products in Stripe is this is your App. You have an account with Stripe and then you have a product. Say you are building a note taking platform that has 3 levels of pricing. The app's name is "Super Notes". You will be creating a product named "Super Notes" that has 3 different pricing plans. Don't worry, we will be going through all of these steps. ## 2. Creating a Product Stripe is set up to handle a lot of different subscription type products. We are going to look at this from the lens of an app. The first thing you need to do is click the + New button on the top of the table. ![](/blog/creating-saas-products-in-stripe-to-sell-with-laravel-cashier/Screen_Shot_2019-12-12_at_4.16.34_PM.png) A modal will appear. In there you will enter the following fields: - Product Name → This would be your app. The other 2 fields `Unit label` and `Statement descriptor` are optional. Enter them only if you need to. ![](/blog/creating-saas-products-in-stripe-to-sell-with-laravel-cashier/Stripe-Create-Product.png) When you are ready, click `Create product` and your first product will be created! ## 3. Add Pricing Plans to Your Product Now it's time to add a few pricing plans to your product! These are what your users will be signing up for. So let's think about a few different plans. Say we have a 3 tiered service with a Basic, Professional, Enterprise and we will charge $10.00, $15.00, $20.00 per month accordingly. ![](/blog/creating-saas-products-in-stripe-to-sell-with-laravel-cashier/Stripe-Create-Plan.png) Let's start with "Basic". For the plan nickname, this will be "Basic". We will leave the ID blank so Stripe will generate this for us. We will also keep 'Recurring quantity' checked so we will charge the user at the proper recurring time. Finally, we will enter $10.00 per unit and our plan will be ready! ![](/blog/creating-saas-products-in-stripe-to-sell-with-laravel-cashier/Stripe-Create-Pricing-Plan.png) All we have to do now, is click `Add pricing plan` and we will be ready to move to the next one! We just have to repeat the above process for the next 2 pricing tiers, swapping what is necessary such as name and price. That's all we need to do for this step! We now have Laravel ready to work within Stripe. In Stripe we are set up to receive requests and some products available for the user to subscribe to. The next part of this series will be to begin connecting everything together. --- # Using Stripe Elements in a VueJS Component > Integrate Stripe Elements into your VueJS components for secure, customizable payment forms. This tutorial covers dynamic script loading, card element setup, and best practices for handling payments in modern Vue applications. At this point, we have [Laravel Cashier installed](/blog/installing-laravel-cashier-on-laravel-6-x/), [Stripe configured and API keys in our Laravel instance](/blog/configure-stripe-to-work-with-laravel-cashier-in-laravel-6/), and we've [added a product with 3 plans (Basic, Professional, Enterprise) in our Stripe platform](/blog/creating-saas-products-in-stripe-to-sell-with-laravel-cashier/). Now it's time to jump back to Laravel & VueJS and start connecting the two platforms. The first place to start is with Stripe Elements. For those who aren't familiar with Stripe Elements ([https://stripe.com/payments/elements](https://stripe.com/payments/elements)), I'd recommend checking out the documentation. Essentially they are a programmatic way to set up a Stripe form and accept a payment. They are also beautifully designed, capable of handling all of the validation necessary, and able to be adapted to fit the flow of your application. ## 1. Create Your Subscription Management Component For this course, we will be making a single component to manage your subscriptions within your single page application. Before we get too far, I'd like to say this will end up being a fairly large component. Feel free to divide it up into smaller sub-components if you want. Also, feel free to style this at will. I won't be going through many UI/UX features besides installing Stripe Elements. The first thing we will do is make a `SubscriptionManagement.vue` component wherever you are storing your components. In our Single Page Application, we have a directory called `components`. The initial component should be a basic structure of the VueJS component like this: ```html [Basic Vue Component Structure] <div> <h3>Manage Your Subscription</h3> </div> ``` ```javascript [Define javascript part of Vue component] export default { data(){ return { } } } ``` ## 2. Add the Stripe Public Key Remember the public Stripe Key? We will need to add this in our component. This is the PUBLIC key the one that starts with `pk_test` in a testing scenario or `pk_live` in a live scenario. However you want to include this in your build process is up to you. An environment variable of some sort would be preferable, but configuring that for an SPA is an entirely different tutorial. For now, take the public key and add it as a piece of data to the local component data like so: ```vue [Adding Stripe Public Key to Component] <template> <div> <h3>Manage Your Subscription</h3> </div> </template> ``` ```javascript export default { data(){ return { stripeAPIToken: 'pk_test_YOUR_RANDOM_KEY' } } } ``` Now we have the key readily available for when we make front end Javascript requests. ## 3. Include Stripe Elements Javascript In a normal application, you'd include this in your header. In a Single Page Application, you could do this in your `index` file, or you could load it dynamically from the component. There are multiple times where your component will not need to be rendered so loading a third party JS script every single time may not be the best approach. You will also need to ensure it's loaded before you configure your form. Outside of your SPA, this could be difficult since it's a trick to fire a callback inside of a component within an SPA. I prefer to load it dynamically within the component only when we need it. To do this, we need to add 2 methods to our component. The first is `includeStripe` . This method will create a script tag and load our Stripe javascript file and add it to the head of our application. The second is `configureStripe` which will set up our Stripe elements. Add the following method: ```javascript [Dynamic Stripe Script Loading Method] methods: { /* Includes Stripe.js dynamically */ includeStripe( URL, callback ){ let documentTag = document, tag = 'script', object = documentTag.createElement(tag), scriptTag = documentTag.getElementsByTagName(tag)[0]; object.src = '//' + URL; if (callback) { object.addEventListener('load', function (e) { callback(null, e); }, false); } scriptTag.parentNode.insertBefore(object, scriptTag); }, } ``` This method accepts 2 parameters, the URL of the file we are loading dynamically which will be the Stripe JS file and a callback function that will run when the file is loaded. Now, when we have the file properly loaded, we will callback and Stripe will be configured. In the `mounted` lifecycle hook on your component, call this method with the URL of the StripeJS file. This is important not to house this file on your server for security reasons. ALWAYS load it from their CDN: ```javascript [Mounted Hook with Stripe Loading] mounted(){ this.includeStripe('js.stripe.com/v3/', function(){ this.configureStripe(); }.bind(this) ); }, ``` 2 things to note, we are binding this so we have access to the local component's methods inside the callback and 2, we are calling configureStripe once everything has been loaded. We have not implemented that yet, but that's next! That's our callback method that will set up Stripe locally within the component. ## 4. Configure Stripe Elements Before we get started, there are A LOT of different options you can do to style your Stripe Elements, add new fields, etc. For more information on what you can do with Stripe elements, visit: [https://stripe.com/docs/stripe-js](https://stripe.com/docs/stripe-js). For this course, we are going to be using the very basic design and style. Styling is outside the scope of this course. So let's first implement the `configureStripe()` method we brushed on in step 3. This is where we will be adding our configuration of Stripe elements. First, let's add a few variables to our local component's `data()`: ```javascript [Adding Stripe Configuration Variables] data(){ return { stripeAPIToken: 'pk_test_', stripe: '', elements: '', card: '' } }, ``` We've added 3 variables `stripe`, `elements` and `card`. The `stripe` variable will house a local instance of the `Stripe` object that is in the Stripe JS API that we loaded. The `elements` will house an instance of the Stripe Elements and `card` will be the object representation of the Card for Stripe Elements. Let's set this up! Let's add the following method to our `methods` object: ```javascript [Stripe Elements Configuration Method] /* Configures Stripe by setting up the elements and creating the card element. */ configureStripe(){ this.stripe = Stripe( this.stripeAPIToken ); this.elements = this.stripe.elements(); this.card = this.elements.create('card'); this.card.mount('#card-element'); }, ``` This line `this.stripe = Stripe( this.stripeAPIToken );` initializes the local Stripe variable to an instance of the Stripe object with our public API token available to make calls. Next we have `this.elements = this.stripe.elements();` and `this.card = this.elements.create('card');` . The first line initializes our local `elements` variable with a Stripe elements instance. The second line creates a special card element and assigns it to our local `card` variable. You can optionally pass custom `styles` as the second parameter to the `create` method to style the card explained here: [https://stripe.com/docs/stripe-js#elements](https://stripe.com/docs/stripe-js#elements). The third line `this.card.mount('#card-element');` mounts an element in our component with the id of `card-element`. We haven't created that yet, but guess what? That's what's next! Right now, we've included our Stripe API and have elements configured. We assigned all of these to local variables within the component and not with in the method because we will be using the `card` variable in a variety of other places in later tutorials. We will need that to be cleared, listen to actions, and create intents. All of which we will go through in this course. ## 5. Add Card Element This is pretty simple, but it's the first time we are adding to the template. We simply just need to add the following in the Vue component's template: ```vue [Adding Card Element to Template] <label>Card</label> <div id="card-element"> </div> ``` One thing I want to point out. This is the only form element WITHOUT a `v-model` attached to it. That's very important. We do NOT want the credit card number within our component and accidentally hitting our server. With Stripe Elements, this is all housed with in the Stripe Elements card object and gets submitted to their server. In our case, we will take the setup intent (discussed later) and save the submitted payment method token in the database so we can charge it later. Right now, we have Stripe Elements set up and configured in our VueJS component! At this state, our component should be this: ```vue [Complete Component Implementation] <template> <div> <h3>Manage Your Subscription</h3> <label>Card</label> <div id="card-element"> </div> </div> </template> ``` ```javascript export default { data(){ return { stripeAPIToken: 'pk_test_', stripe: '', elements: '', card: '' } }, mounted(){ this.includeStripe('js.stripe.com/v3/', function(){ this.configureStripe(); }.bind(this) ); }, methods: { /* Includes Stripe.js dynamically */ includeStripe( URL, callback ){ let documentTag = document, tag = 'script', object = documentTag.createElement(tag), scriptTag = documentTag.getElementsByTagName(tag)[0]; object.src = '//' + URL; if (callback) { object.addEventListener('load', function (e) { callback(null, e); }, false); } scriptTag.parentNode.insertBefore(object, scriptTag); }, /* Configures Stripe by setting up the elements and creating the card element. */ configureStripe(){ this.stripe = Stripe( this.stripeAPIToken ); this.elements = this.stripe.elements(); this.card = this.elements.create('card'); this.card.mount('#card-element'); }, } } ``` At the end of the course I will provide all source code. Let me know if you have any questions so far in the comment section below! In the next tutorial, we will be talking about Payment Intents which are the user's intent to pay for something with a payment method. --- # Creating Stripe Setup Intents With Laravel API and VueJS SPA > Learn how to implement Stripe Setup Intents in a Laravel API and VueJS SPA for secure payment method management, including API integration and frontend best practices. We are making progress in implementing our Subscription component! Next up, we have to get our setup intent. When I first started implementing Stripe into applications for recurring transactions, the concept of the set up intent was a little confusing to me. In the Laravel 6.x Cashier docs ([https://laravel.com/docs/6.x/billing#payment-methods](https://laravel.com/docs/6.x/billing#payment-methods)) it states that "A "Setup Intent" indicates to Stripe the intention to charge a customer's payment method." What kind of made sense to me was to look at the setup intent as "hey, we need to save your card because you **intend** on letting us charge it for our service at a certain rate". It sounds a lot more complicated than it is, but Stripe and Laravel make it a breeze. We are just going to put it in an API + SPA form. The setup intent will be sent to Stripe along with the credit card info so Stripe will store the information on behalf of the user and return the proper data to us to store locally so we can charge accordingly. Before you can actually charge for a subscription and create that subscription, you have to have a card on file. In this tutorial we are taking the first steps to getting there! The next step will be actually persisting the card and getting the payment ID so we can charge the customer accordingly. ## 1. Add API Route To Grab Setup Intent This is right away where this tutorial diverges from the official Laravel documentation. In the official Laravel 6.x docs on Storing Payment Methods ([https://laravel.com/docs/6.x/billing#storing-payment-methods](https://laravel.com/docs/6.x/billing#storing-payment-methods)), the documentation goes through returning a blade view in a call and response type structure, with a variable that is the setup intent token. We don't have that available in our Single Page Application, so we have to do it a little bit differently. As long as you have Laravel Passport installed ([https://laravel.com/docs/6.x/passport](https://laravel.com/docs/6.x/passport)), and configured, open up `routes/api.php`. All of the API routes we are using in this course will be behind the `auth:api` middleware which requires a valid authentication token to be present. This is VERY important so no matter how you secure the routes (group, middleware in constructor, etc) make sure they are behind the `auth:api` middleware. I like putting all of my secure API routes within a secure route group. I find it easier to organize and view so I can place secure routes in that group easier. This is what my routes/api.php file looks like: ```php [API Routes Configuration] Route::group(['prefix' => 'v1', 'middleware' => 'auth:api'], function(){ }); ``` If you are implementing in an existing application, you might have a ton of existing routes. Just make sure the routes we add next are blocked by the `auth:api` middleware. I also have a `v1` prefix for my API so it makes versioning easier. That's a different subject entirely, but you won't have to worry about that in the scope of this course. Next, add the following route within your secure route group: ```php [Setup Intent Route Definition] Route::get('/user/setup-intent', 'API\UserController@getSetupIntent'); ``` 2 things to note. The route will resolve to a `GET` request on `/api/v1/user/setup-intent` with the `v1` prefix that I have (may be different on your API) and I am using an `API` namespaced `UserController.php`. I put this in a controller and specifically namespaced it for API usage. It will respond on the method `getSetupIntent()`. ## 2. Add getSetupIntent method to UserController Now we need to handle the response to the `/api/v1/user/setup-intent` route. Open up your controller, in this case the `API\UserController` and add the following code: ```php [UserController Setup Intent Method] namespace App\Http\Controllers\API; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class UserController extends Controller { /** * Creates an intent for payment so we can capture the payment * method for the user. * * @param Request $request The request data from the user. */ public function getSetupIntent( Request $request ){ return $request->user()->createSetupIntent(); } } ``` The `createSetupIntent()` is a trait on the `User` model that returns the setup intent object `client_secret`. We will pass an object back to our SPA (unlike in the documentation where it binds it to a blade view). Since we are calling our API, this returns a beautiful, JSON formatted response that we can easily integrate into our SPA. ## 3. Using the Setup Intent in the VueJS SPA Let's switch back to the `SubscriptionManagement.vue` component that we started making in [Using Stripe Elements in a VueJS Component](/blog/using-stripe-elements-in-a-vuejs-component/). First, let's add a local variable to our `data()` object named `intentToken`. This will house the response from the API. ```javascript [Vue Component Data Property] data(){ return { ... intentToken: '' } }, ``` Next, let's add a method that loads the intent from the API and add this to the `methods` object in the local component: ```javascript [Load Intent Method] /* Loads the payment intent key for the user to pay. */ loadIntent(){ axios.get('/api/v1/user/setup-intent') .then( function( response ){ this.intentToken = response.data; }.bind(this)); }, ``` Finally, let's look at our `mounted()` lifecycle hook. In there, we will add a call to the `loadIntent()` method like this: ```javascript [Vue Component Mounted Hook] mounted(){ ... this.loadIntent(); }, ``` Now when our component is mounted, we will load the intent to setup a new payment. There are a LOT of different UX things you can do with this. Maybe you don't want to display the form right away to add a payment, that way you don't need a setup intent. If that's the case, don't call the `this.loadIntent()` from inside `mounted`. Call it when you want the user to begin adding their payment. The flow of getting the setup intent from within the SPA is as follows: 1. Our Subscription Management component is `mounted()` 2. We call the `loadIntent()` method which loads the setup intent from `/api/v1/user/setup-intent` . 3. Upon completion, this token (the object returned) is stored to the local `intentToken` variable. The `client_secret` key will be used when we call the Stripe API. A few final notes. Since we are getting a `client_secret` from the API for the setup intent, it's good to have protections in place (such as being behind the `auth:api` middleware). It's also okay to note that we are saving this to a local variable. Our Stripe client secret is still housed in our `.env` server side. Even in the examples provided, this intent token is stored in the HTML. Let me know your thoughts and if you have any questions in the comment section below! In the next tutorial we will be actually saving the payment and creating a subscription! --- # Managing Stripe Payment Methods in VueJS SPA and Laravel API > Learn how to manage Stripe payment methods in a VueJS SPA with a Laravel API backend. This guide covers saving, retrieving, and removing payment methods for seamless subscription management in production apps. We've gotten up to the meat and potatoes of our course, managing payment methods! All of this has to be in place before we actually subscribe a user to a plan. Since we are working with recurring payments in a subscription, we have to have payment methods on file and a way to manage them correctly. In the last tutorial, we loaded a payment intent ([Creating Stripe Setup Intents With Laravel API and VueJS SPA](/blog/creating-stripe-setup-intents-with-laravel-api-and-vuejs-spa/)). In this tutorial, we will be using that intent `client_secret` to pass to Stripe and save a payment method to charge. We will also be adding 3 API routes. The first route will be to save the payment method, the second route will be to retrieve payment methods (through an API this requires a little modification), and the 3rd route will be to remove payment methods. Let's get started! ## 1. Saving a Stripe Payment Method (with Stripe) This is by far the most important part of the tutorial. Everything depends on having a payment method saved. First, let's start in our `SubscriptionManagement.vue` component. We will need to add a `name` field to save along with the payment method. This will be the name of the credit card holder and will be used to pass along with the card information to Stripe to store as a payment. If you need to pass and store any other billing information, add that now similar to how we have the `name` variable. [More information can be found here →](https://stripe.com/docs/stripe-js#elements) Let's also add a status called `addPaymentStatus` so we can display results to the user and an `addPaymentStatusError` so we can properly display any error messages if they arise. Let's add the `name`, `addPaymentStatus`, `addPaymentStatusError` variables to the `data()` like this: ```javascript [Adding payment-related data properties to Vue component] data(){ return { ... name: '', addPaymentStatus: 0, addPaymentStatusError: '' } }, ``` Next, add an input above the `Card` field in your template for the `name`: ```vue [Adding card holder name input to template] <template> <div> <h3>Manage Your Subscription</h3> <label>Card Holder Name</label> <input id="card-holder-name" type="text" v-model="name" class="form-control mb-2"> <label>Card</label> <div id="card-element"> </div> </div> </template> ``` Now we have the card holder name in our component. While we are in our component's template, let's add a button to allow the user to save the payment method. Once everything has been entered correctly, this is what the user will press to save the payment method with Stripe: ```vue [Adding save payment method button to template] <template> <div> ... <button class="btn btn-primary mt-3" id="add-card-button" v-on:click="submitPaymentMethod()"> Save Payment Method </button> </div> </template> ``` Okay, so now we have our template laid out, it's time to shell out some functionality and actually save our payment method! In the `methods` object in our component, let's add the method `submitPaymentMethod()` that you saw in the `v-on:click` handler in the template. When the user clicks to `Save Payment Method`, this is what will send the data to Stripe's API to save the payment method for the user and return the payment identifier key that we can use later for charges. The method is fairly large and should look like this (don't worry, we will step through all of it!): ```javascript [Implementing submitPaymentMethod function to handle Stripe payment setup] submitPaymentMethod(){ this.addPaymentStatus = 1; this.stripe.confirmCardSetup( this.intentToken.client_secret, { payment_method: { card: this.card, billing_details: { name: this.name } } } ).then(function(result) { if (result.error) { this.addPaymentStatus = 3; this.addPaymentStatusError = result.error.message; } else { this.savePaymentMethod( result.setupIntent.payment_method ); this.addPaymentStatus = 2; this.card.clear(); this.name = ''; } }.bind(this)); }, ``` That's quite the method so let's break it down! First, we set our `addPaymentStatus` to `1` which means we are adding a payment method, but haven't heard a response back. I like keeping it simple so we can show in the UI or listen and respond accordingly. Next, we call the `confirmCardSetup()` method on our local `stripe` object that we set up in [Using Stripe Elements in a VueJS Component](/blog/using-stripe-elements-in-a-vuejs-component/) . For more information, check out the Stripe Docs here: [https://stripe.com/docs/stripe-js/reference#stripe-confirm-card-setup](https://stripe.com/docs/stripe-js/reference#stripe-confirm-card-setup) This method accepts 2 parameters: 1. **Intent Token Client Secret →** This is the `client_secret` key from the intent token we received from the API in [Creating Stripe Setup Intents With Laravel API and VueJS SPA](/blog/creating-stripe-setup-intents-with-laravel-api-and-vuejs-spa/). 2. **Data →** This is the guts of the request. The first key of the object is `payment_method` which contains the information regarding the card entered and billing details. The `card` key is the **Card Element Object** from Stripe Elements. This will contain ALL of the credit card data to be sent to **Stripe's API**. The `billing_details` contains the name of the card holder. After we send our payment to Stripe, we need to handle the result. Luckily the `confirmCardSetup()` returns a promise with all of the information that we need! In the callback function we are passed a `result` object. This object will contain an error, if there is one, otherwise it will contain a `setupIntent` key with a `payment_method` key. This key is what we are looking to save with the user! It's an identifier for the user and their card on file so we can bill them at set periods. In our method, if the callback function has a key of `error`, we set the `addPaymentStatus` to be `3` which means there was an error in the process and `addPaymentStatusError` to be the `result.error.message` which is the message returned from Stripe's API. We can then display that in the UI if you want. What we are MOST concerned about is what happens on a successful payment method stored in Stripe. The first thing we do is call `this.savePaymentMethod( result.setupIntent.payment_method );`. Wonderful right? We don't have this implemented yet. Next Step. This method accepts the `result.setupIntent.payment_method` which is the unique identifier for the user's credit card within Stripe. We will be saving this with the user to use later. Next we set the `addPaymentStatus` equal to `2` which means everything is successful. You can then display success to the user in your component when `addPaymentStatus` == `2`. Finally, we call `this.card.clear()` and `this.name = ''`. `this.card.clear()` calls the method to remove any of the user's entered credit card input from the Stripe Elements card element and `this.name = ''` resets the card holder name to empty. Whew! That was an intense method, but we now have the payment being saved within Stripe! Now, let's persist that payment method to our own database. ## 2. Saving the Stripe Payment Method for Later Use Now that we have the payment method's unique identifier we can save it locally to our own database. Since we are using an API to communicate with our backend, we will have to set up another route. Let's get that in place before we write our `savePaymentMethod( method )` function discussed at the end of Step 1. First, open up your `routes/api.php` file and add the following route: ```php [Adding payment methods API route] Route::post('/user/payments', 'API\UserController@postPaymentMethods'); ``` MAKE SURE to do it under the `auth:api` middleware group or however you prevent unauthorized access. This URL will accept a POST request to `/api/v1/user/payments` and will save the payment with the user. Now, let's add the following method to our `API\UserController`: ```php [Implementing postPaymentMethods controller method] /** * Adds a payment method to the current user. * * @param Request $request The request data from the user. */ public function postPaymentMethods( Request $request ){ $user = $request->user(); $paymentMethodID = $request->get('payment_method'); if( $user->stripe_id == null ){ $user->createAsStripeCustomer(); } $user->addPaymentMethod( $paymentMethodID ); $user->updateDefaultPaymentMethod( $paymentMethodID ); return response()->json( null, 204 ); } ``` In this method, we are going to make heavy use of the `Billable` traits added to the user from Laravel Cashier. These are extremely helpful and take SO MUCH pain away from doing this straight through the Stripe API. First, we grab the `$user` and `$paymentMethodID` from the `$request`. The `$request->user()` is attached through Laravel Passport and the `$request->get('payment_method')` is what we send to the API route from our VueJS component when we implement the `savePaymentMethod()` (next step). Now, the first thing we do is check to see if the user's `stripe_id` is set. This is the customer id for the user within Stripe. If this is not set, we need to call the `createAsStripeCustomer()`method on the user to add them as a Stripe user ([https://laravel.com/docs/6.x/billing#creating-customers](https://laravel.com/docs/6.x/billing#creating-customers)). This helps before creating the subscription for the user so we have an identifier for all future transactions. Next, we call 2 more methods from the `Billable` trait on the user. First it's the `addPaymentMethod()` which takes the unique `$paymentMethodID` as the parameter to save with the user. This will save the payment method with the user within Stripe. Finally, we call the `updateDefaultPaymentMethod()` and pass the `$paymentMethodID` for the payment ID. I choose to set the newest payment method as the default right away, but you don't have to do this. If you want to have an option to save as default, you could implement that as well. Finally, we return a `204` null response which means everything was successfully saved! One thing you will notice though is the actual `payment_method_id` is NOT persisted to the database. This is GOOD! We just created a customer within Stripe for the user and saved that ID locally. The payment method is bound to that customer within Stripe. We don't need to store that and loading any saved payment methods we will use a trait on the user to make an API request call to Stripe. Always best practice to have as much secure information stored with Stripe as possible! ## 3. Sending the Payment Method to the API So now that we have our payment method identifier and our API set up to save it with the user, it's time to glue the front and back end together! Luckily this is fairly straight forward. First, let's add the following method to our methods object in the `SubscriptionManagement.vue` component: ```javascript [Implementing savePaymentMethod function to send payment to API] /* Saves the payment method for the user and re-loads the payment methods. */ savePaymentMethod( method ){ axios.post('/api/v1/user/payments', { payment_method: method }).then( function(){ this.loadPaymentMethods(); }.bind(this)); }, ``` Now, we've pieced together the puzzle! After we get the `payment_method` from the `setupIntent` in Step 1, we call the `savePaymentMethod()` method and pass the unique ID of the payment method. This method calls the API route that we set up in Step 2, saving the method with the user. Finally, we call another method (which, once again, we will define in the next step) that loads the payment methods for the user. Other than that, we now have a payment method saved with the user and connected through Stripe! Since this tutorial covers the management of payment methods (adding, loading, deleting) we have a few more methods to add! Luckily, they aren't all super complicated. ## 4. Loading a User's Payment Methods with Laravel Cashier through the API Now that we have the payment methods being saved, it'd be nice to load these methods and display them to the user so they can select what they want to use when selecting a a subscription to purchase. Once again, since we are in an API Driven Development lens, we need to make an API route to handle this. We will then complete the flow by implementing the `loadPaymentMethods()` method foreshadowed in the last step. First, let's open up our `routes/api.php` file and add the following route ONCE AGAIN protected behind the `auth:api middleware`: ```php [Adding get payment methods API route] Route::get('/user/payment-methods', 'API\UserController@getPaymentMethods'); ``` This API route will resolve to `/api/v1/user/payment-methods`. Next, let's open up our `UserController.php` and add the following method: ```php [Implementing getPaymentMethods controller method] /** * Returns the payment methods the user has saved * * @param Request $request The request data from the user. */ public function getPaymentMethods( Request $request ){ $user = $request->user(); $methods = array(); if( $user->hasPaymentMethod() ){ foreach( $user->paymentMethods() as $method ){ array_push( $methods, [ 'id' => $method->id, 'brand' => $method->card->brand, 'last_four' => $method->card->last4, 'exp_month' => $method->card->exp_month, 'exp_year' => $method->card->exp_year, ] ); } } return response()->json( $methods ); } ``` What this method does is grabs the payment methods stored with the user through Stripe. First, we check through the `hasPaymentMethod()` if the user even has any payment methods. If they do, we load all of the methods by calling `$user->paymentMethods()` and adding them to the local `$methods` array. We then return the `$methods` array as JSON to the component so we can display the possibilities to the user to select when purchasing a subscription. Now that we have our API built, let's open up our `SubscriptionManagement.vue`component and add the following method: ```javascript [Implementing loadPaymentMethods function to fetch payment methods] /* Loads all of the payment methods for the user. */ loadPaymentMethods(){ axios.get('/api/v1/user/payment-methods') .then( function( response ){ this.paymentMethods = response.data; }.bind(this)); }, ``` This method will call our new route, loading the payment methods for the user. The only thing we need to add is the `paymentMethods` array to the `data()` locally in the component like so: ```javascript [Adding paymentMethods array to component data] data(){ return { ... paymentMethods: [], } }, ``` Finally, we should call this method in the `mounted()` lifecycle hook: ```javascript [Adding loadPaymentMethods call to mounted hook] mounted(){ ... this.loadPaymentMethods(); }, ``` This way we have all of the payment methods displayed for the user when they are signing up for the app. When we get to the next tutorial of actually signing up for the subscription, we will display these payment methods as options for the user to select. ## 5. Deleting a Stored Payment Method While we won't add the UI in this tutorial (the next tutorial we will), let's set up our methods to delete a stored payment method. First, let's start with our `routes/api.php` file and add the following route: ```php [Adding remove payment method API route] Route::post('/user/remove-payment', 'API\UserController@removePaymentMethod'); ``` This would resolve to a POST request to `/api/v1/user/remove-payment`. In a proper RESTful API this would be a DELETE request. However, we don't want to pass the payment ID through the URL, we want it in the body on an encrypted https request, we will use `POST`. Now, let's add the following method to the `UserController.php`: ```php [Implementing removePaymentMethod controller method] /** * Removes a payment method for the current user. * * @param Request $request The request data from the user. */ public function removePaymentMethod( Request $request ){ $user = $request->user(); $paymentMethodID = $request->get('id'); $paymentMethods = $user->paymentMethods(); foreach( $paymentMethods as $method ){ if( $method->id == $paymentMethodID ){ $method->delete(); break; } } return response()->json( null, 204 ); } ``` What this does is accepts a parameter named `id`. It will then iterate over all of the payment methods the user has which are instances of the `Laravel\Cashier\PaymentMethod` class. Once it finds the ID that matches the one we are looking for, we run the `delete()` method which removes the payment method from the user. For more information view: [https://laravel.com/docs/6.x/billing#deleting-payment-methods](https://laravel.com/docs/6.x/billing#deleting-payment-methods). Note, that if the user has an active subscription, prevent them from deleting their default payment method as noted in the docs! Finally, let's re-open the `SubscriptionManagement.vue` component and add the following method to the `methods` object: ```javascript [Implementing removePaymentMethod function to delete payment method] removePaymentMethod( paymentID ){ axios.post('/api/v1/user/remove-payment', { id: paymentID }).then( function( response ){ this.loadPaymentMethods(); }.bind(this)); } ``` This makes a simple request to our newly defined remove payment method route and accepts the ID of the payment method we are removing as it's parameter. Upon completion, it re-loads the active payment methods that the user has. ## Conclusion Well, that was a long one! Now we can add payment methods, save them to the user, load the existing payment methods and delete them. Up next, we will build on this base and actually subscribe the user to a plan for the app! If you have any questions, like to contribute thoughts or ideas, please leave a comment in the comment section below or reach out to me via Twitter [@danpastori](https://twitter.com/danpastori) ! On to subscriptions! --- # Your Guide to Using an API with VueJS/NuxtJS and Axios > Learn the tips, tricks and efficient techniques of using Axios with VueJS and NuxtJS. ## About this series We've been doing a lot of work with APIs lately, especially with VueJS and NuxtJS. Throughout this time, we've experienced a variety of different scenarios.  In this course, we will go through the basics of making an API request using Axios from within VueJS or NuxtJS framework. From there, we will advance and touch on a variety of techniques to make handling API requests a breeze. Consider this a cookbook of helpful recipes that you can integrate into any VueJS or NuxtJS platform using Axios. Each tutorial should be simple to integrate into your existing application. I hope you enjoy the course and let me know your feedback! If you enjoy this course and want to learn more about building APIs and integrating with them, [check out our book](/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/)! ## What you'll learn - How to reuse API requests within VueJS and NuxtJS Components - Proper error handling and display - Using asyncData() with NuxtJS and Axios --- ## Posts in this series # Using Axios to Make API Requests With VueJS > Learn how to make API requests with Axios in VueJS. This comprehensive guide covers GET, POST, PUT, PATCH, and DELETE requests, configuration, and best practices for integrating APIs into your Vue applications. Let's start with the basics, making API requests with Axios from within VueJS. This will give us a solid foundation to work from as we get into more complex requests. If this is your first time working with an API, it can seem intimidating. However, once you learn the basics, APIs begin to make a lot more sense. You can then apply these principals to figure out how to make tools and integrate with a variety of platforms. ## Types of Requests Let's start with the types of API requests you can make with Axios. ### GET Request This is probably the request you are most familiar with. Every time you request a web page through the web browser, you run a GET request. GET requests essentially "get" data from a server in the form of HTML, XML, JSON, an image, etc. You can also pass data to the API with a GET request. Be aware though, that the data will be passed through a string in the URL. Ever see a clean URL with `?variable=xyz&another=123`? That's how you pass data using a GET request. Not ideal if you need to pass secure information, but wonderful if you need to limit what's returned! For passing data securely (granted everything is configured on your server correctly), that brings us to our next type of request. ### POST Request The POST request is used when you need to send data to a server with the intent of creating a resource or submitting a form such as a login or registration form. POST requests pass data through the body of the request. Assuming you are using `https://` (which you better be!) this should be a secure way to send data to the server. ### PUT Request This is a relatively new "HTTP Verb" which is meant to be used to send data to update a specific resource, not create a resource like POST. When working with properly structured APIs (or creating them), it's important to use the right verb. Other developers can integrate easily and read your code with the right intent. There's one more verb that comes with the intent of "updating" a resource and that's the PATCH request. ### PATCH Request Very similar to the PUT request, the PATCH request is meant for updating a specific resource in an API. Now, when should you use each? Well they both have a specific purposes. The difference is slight but really important. The PATCH request should be used when you need to send part of a piece of data to update on an API, where PUT should be used when you essentially want to replace an entire resource with an updated resource. Let's think about this example. Say we have an API that works with music. You have a song resource at `/api/v1/songs`. The user wants to change the name of the song only. They'd submit a PATCH request to `/api/v1/songs/{id_of_song}` with just the name of the song. The intent of the PATCH request is to update the name in the database, and if the API is designed correctly, that's what it should do. Now, if there was a form where you could edit all of the information about the song (name, artist, album), you'd send a PUT request to the `/api/v1/songs/{id_of_song}` with the entire song resource. This would update every attribute on the song submitted by the user. ### DELETE Request This request is probably the most straight forward. You submit a DELETE request when you want to delete a resource from the API. That's it, nothing real special to it. Now that we have a little background on what types of requests you can send, let's get to it! ## Installing Axios For all of the different ways to install Axios, visit their [installation documentation.](https://github.com/axios/axios#installing) When I create a VueJS app I use NPM or Yarn, so I'd run either: ```bash [Install Axios using NPM] npm install axios ``` or ```bash [Install Axios using Yarn] yarn add axios ``` If you are using NuxtJS, then you will want to install their Axios module. They have an amazing Axios module that works seamlessly within their framework. There are going to be a few tutorials that are NuxtJS only using this module. To install this module within your NuxtJS framework, use NPM or Yarn ```bash [Install Nuxt Axios module using NPM] npm install @nuxtjs/axios ``` or ```bash [Install Nuxt Axios module using Yarn] yarn add @nuxtjs/axios ``` Next, you will have to go to your `nuxt.config.js` file and add the following: ```javascript [Configure Axios in Nuxt config] modules: [ '@nuxtjs/axios', ], axios: { } ``` You've now registered the Axios module with your NuxtJS project. The `axios` key allows you to have a global configuration set up to use on all of your API requests. Super convenient for keeping track of base urls, authorization headers, etc. We are all set up to make a few requests! ## Making an Axios Request So we've went through the different types of requests and installed let's take a look at how you'd actually create and send a request. So for every request we mentioned, Axios has a specific method to call to perform one of the requests. Their [documentation](https://github.com/axios/axios#request-method-aliases) does a wonderful job of going through these if you need more information. The only thing I'd like to point out is between the GET requests and the POST, PUT, DELETE requests and where you send the data. If you are making a GET request, the method signature is: ```javascript [Basic GET request syntax] axios.get('https://api.com/your/GET/url', config) ``` Now if you wanted to pass some query parameters to filter the results (like only grab resources after a certain date) and create a query URL that looks like `https://api.com/your/GET/url?date=YYYY-MM-DD` you'd add the following configuration: ```javascript [GET request with query parameters] axios.get('https://api.com/your/GET/url', { params: { date: 'YYYY-MM-DD' } }) ``` So what's the point? Well, with a POST, PUT, PATCH request, there are 3 method parameters to those axios methods and you'd send the data as the second parameter (which will put it in the body of the request). A POST request would be like: ```javascript [POST request with data] axios.post( 'https://api.com/your/POST/url', { name: 'name', date: 'date' }, config ); ``` With the explicit data as the second parameter in a POST, PUT, PATCH request, it tripped me up when trying to pass query data in a GET request. Just thought I'd point that out. ## Using Axios Within VueJS This is what we are all looking for right? Making Axios API requests from within VueJS? We've got Axios installed in our project, so let's say we have a simple Vue component that displays all of our users. ```javascript [Basic Vue component with users data] export default { data(){ return { users: [] } } } ``` So how do we get those users into the component from the API? First, let's start by making Axios accessible within the component by importing it: ```javascript [Import Axios in Vue component] import axios from 'axios'; export default { // Component data, methods, etc. } ``` Now, we can create a method to call the users endpoint and load all of the users! I'd add a method to the component like: ```javascript [Load users method with Axios] methods: { loadUsers(){ axios.get('https://api.com/v1/users') .then(function( response ){ this.users = response.data; }.bind(this)); } } ``` Whoa! There's a lot going on in there besides just a request, so let's break it down! ### Breaking Down the Request First, we are loading users, so we need to use a GET request. Essentially, this means we aren't creating or updating resources, we are just retrieving them. Next, we are chaining a `then()` statement to the request. Axios is a [promise](https://javascript.info/promise-api) based HTTP client, meaning that the request is either resolved or rejected (successful or not). This is extremely useful for loading data since we can handle a request and any errors that come with (400, 404, 403, 401 any of the friendly 4xx error codes). What the `then()` method chained to the end of the request is doing, is essentially saying "when this request completes successfully, then handle the data returned from the request" (we will be covering errors in a later tutorial). The callback function passed to the `then()` method accepts the `response` as it's first parameter. The `response` parameter contains all of the details about the response sent back from the server such as header information, status code, and most importantly, `data`. Now that our request has been completed, we can set the local `users` variable to the `response.data` returned from the API. However, there's one more thing to note! Within the callback function, we reference `this.users` . Since the callback function doesn't have a `users` variable and no access to the Vue Component's scope, we need to attach `.bind(this)` to the end of the callback function. This is extremely important for working with Axios within VueJS. This gives the callback function scope to the Vue component, allowing us to set the component's local data from the response of the API. ## Using Axios Within NuxtJS Implementing the same request within NuxtJS is very similar, however you don't have to import Axios every time you need to use it. This is because the module that was installed, is available across all components out of the box. The main difference you will see in the NuxtJS ecosystem is the more heavily used [async/await syntax](https://javascript.info/async-await). This syntax simply makes working with promises a little bit easier to read. You can chain a ton of callback functions after a promise and it gets messy really quick. With async/await, it's a much cleaner syntax. Using the combination, your script won't run until the "promise settles and returns its result" - [https://javascript.info/async-await](https://javascript.info/async-await). Let's see how we'd make the same request with NuxtJS: ```javascript [Load users method with Nuxt Axios] methods: { async loadUsers(){ let users = await this.$axios.$get('https://api.com/v1/users'); this.users = users } } ``` ### Breaking Down The Async/Await Request In half the amount of code, we get the same result! You can use the syntax in straight VueJS as well if you want! By putting the `async` keyword in front of the `loadUsers()` method, we are stating that we are working with promises. Within the method, we have the `await` keyword. They have to co-exist. The `await` keyword will return the result of the promise. The other thing to note is see we are using `this.$axios.$get`? This is how you access the NuxtJS axios plugin. It's bound globally which is super nice! Now we don't have to import it every time (which we will solve in the next tutorial for VueJS). So let's assume we that the request goes as planned (terrible to do this as a dev, but for now, errors don't exist)! The response from the promise will be the `users` that we want. We will then assign it locally using `this.users`. There are no callbacks, so we don't have to bind the local component to the callback function which really cleans up the code! That's the exact same request, just from a NuxtJS perspective. ## Next Up, Making Axios Global for VueJS In the next tutorial, we will want to re-use axios globally across the entire VueJS application. NuxtJS has this set up easily with their Axios module, but with the normal VueJS ecosystem, we will have to do this ourselves. ## Resources Axios Documentation: [https://github.com/axios/axios](https://github.com/axios/axios)<br /> NuxtJS Axios Module: [https://axios.nuxtjs.org/](https://axios.nuxtjs.org/) --- # Configuring Axios Globally with VueJS > Learn how to configure Axios globally in VueJS applications for efficient API requests, including best practices for global access, setup, and SPA development workflows. In the [last tutorial](/blog/using-axios-to-make-api-requests-with-vuejs/), we went through installing Axios on VueJS and NuxtJS. We also made our first requests with each set up! However, there was one glaring difference between the VueJS and NuxtJS setups that will exponentially grow as you develop. That difference is the global access to Axios within NuxtJS compared to importing it with VueJS. If you are using NuxtJS, this won't be of any use to you since global access is already available. I'd move on to the next tutorial where we submit some data! ## Why is this a big deal? Simply put, the less you need to repeat yourself, the better. You don't want to have to import Axios every time you want to use the library. Let alone, when you need to configure all of your requests to have certain headers, or intercept errors and gracefully handle them, doing this EVERY time adds up. I'm not for putting every library used in a global set up. As a matter of fact, I'm against it most of the time. However, Axios is an exception. There's global config and if you are building a Single Page Application, you will be making API requests all the time! Luckily, this is fairly straight forward, so let's get started! ## Set Up If you need to still install Axios within VueJS check out [Using Axios to Make API Requests With VueJS](/blog/using-axios-to-make-api-requests-with-vuejs/). If Axios is already installed, let's find the root javascript file where you set up your VueJS install. For me this is usually in a `/resources/js` directory and named `app.js` or `index.js`. Whatever it's named, find that file. In the file, you will simply need to add the following line: ```javascript [Require axios globally] window.axios = require('axios'); ``` That's really it! What this did was bind the axios variable to the `window` variable which gives us access to the functionality within any VueJS component! This may look familiar if you have been using VueJS with Laravel. By default, Laravel sets up the VueJS install similarly on the front end to work with their framework. You now can use `axios.get()` or `axios.post()` from within any component without having to import it every time. ## Other Global VueJS Axios Options Even though that's my preferred method, there may be other ways you choose to structure your VueJS app. Honestly, this is why I prefer NuxtJS because it's an opinionated way on how to set up the VueJS ecosystem and extend it with other modules. And it makes a ton of logical sense! The other options I'd consider are dependent upon what kind of app you are creating. If you will be using lots of request transformations (usually when working with multiple APIs) or working with various authentication methods, I'd add all of this in a separate file. This will make sure your VueJS initial file stays clean and easy to read. ## What's Next? There's a lot! It's nice to have Axios globally, but even that won't be enough for larger applications. You will not only be re-using axios, but also API requests. I'll show you have to abstract those requests into nice re-usable modules with VueJS and NuxtJS. However, next up, we will be going through a quick tutorial on how to send data to your API through a POST/PUT/PATCH request. --- # POST, PUT & PATCH Requests with VueJS and Axios > Master POST, PUT, and PATCH requests in VueJS and NuxtJS using Axios. This comprehensive guide explains how to send and handle different request types, work with JSON and FormData, and seamlessly integrate these methods into your applications for robust API interactions. These requests allow you to manipulate data on your API. If you want a refresher, visit [Using Axios to Make API Requests With VueJS](/blog/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`](https://github.com/axios/axios#using-applicationx-www-form-urlencoded-format). 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](/guides/guide-uploading-files-vuejs-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](/blog/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: ```javascript [Basic Axios POST request structure] 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: ```javascript [POST request with FormData] 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](/guides/guide-uploading-files-vuejs-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: ```javascript [NuxtJS POST request with async/await] 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: ```javascript [NuxtJS POST request without async/await] 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: ```javascript [VueJS mixin for FormData transformation] /** * Simple transformation to form data for uploading files * using VueJS and Axios. * * Assuming that you pass your entire form as the `form` * parameter. For example: * * data(){ * return { * form : { * name: 'Dan', * job: 'Software Developer' * website: 'https://serversideup.net' * logo: FileList * } * } * } * * this.transformToFormData( 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](https://developer.mozilla.org/en-US/docs/Web/API/FileList)), 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](https://inertiajs.com/forms) 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: ```vue [Vue component with form data structure] <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](/blog/using-axios-to-make-api-requests-with-vuejs/)? 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: ```javascript [Axios PUT request example] axios.put('https://api.com/v1/resource/{id}', { name: 'name', date: 'date' }, { // Config } ); ``` and the `PATCH` method like this ```javascript [Axios PATCH request example] 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: ```javascript [NuxtJS PUT request example] 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](https://twitter.com/danpastori)). --- # Build an API Wrapper with VueJS & Axios > Discover how to create a modular, maintainable API wrapper in VueJS using Axios, enabling reusable, scalable, and easily updatable API interactions for production-grade applications. Creating an API wrapper using VueJS & Axios makes your API interfacing code extremely fluid, modular, and maintainable. Before we get started, those using NuxtJS should skip to the next tutorial. This will tutorial will ONLY work with VueJS and not within NuxtJS. With that being said, so far we've installed [Axios and got it to work with VueJS](/blog/using-axios-to-make-api-requests-with-vuejs/) and configured [Axios to work globally](/blog/configuring-axios-globally-with-vuejs/). We've also went through a few more [complex requests like POST, PUT, & PATCH](/blog/post-put-patch-requests-with-vuejs-and-axios/). In this tutorial we will abstract all of our API requests into wrapper modules. Before we get started, let's begin with why. ## Why Should We Do This? Simply put, for code maintainability and re-usability. What we will be doing is taking all of our API requests for a specific resource and wrapping them in a module. If you need to make a request to an API endpoint using this module with a Vuex action, you can. If you need to make an API request in a component, you can do that as well. This process will work with your own API or a 3rd Party API. However, the best part is, if you need to change a request in any way (like adding a header, upgrading API versions, etc), you update it once. This will update the request through your entire app! It's really convenient and easy to use. In the next tutorial I'll show you how to do this with NuxtJS if that's what you are using. Let's get started! ## Step 1: Build a Front End API Directory The way I approach development is to set up "buckets" (which are just folders) to house code and pseudo-structure before I start developing. Before I add any API wrapping modules, I always create an `/api` directory at the root of my app. In this directory I will place all of my modules. If you have a very complex API, feel free to add sub-directories to this as well. The more code organization, the better! Let's add our first module. ## Step 2: Add Your First Module Let's say we have an API focused around music. This API has endpoints that allow us to manage the songs resource. We can view all songs (`GET`), create a song (`POST`), update a song (`PUT`), view a song (`GET`) and delete a song (`DELETE`). There are a variety of places in our application where we interact with this resource, so we want to build a module. The first step is to add the file: `/api/songs.js`. In this file, add the following code: ```javascript [Template for our API request module] export default { index( params ){ }, show( id ){ }, update( id, data ){ }, create( data ){ }, delete( id ){ } } ``` If you've worked with Laravel in the past, this naming convention may look familiar. If you haven't, you might be wondering what's up with the names of these methods. Laravel uses a naming scheme for resource endpoints that I really enjoy implementing on both back and front end. If you want to read about it, check out their [documentation on Resource Controllers](https://laravel.com/docs/7.x/controllers#resource-controllers). You don't have to use Laravel as your backend and you really don't even have to use this naming format. These are just the methods that we will be implementing for each resource regardless of first party or third party. Each method will return an Axios request which is a promise. This way we can efficiently handle the request in context. ### The `index` Endpoint When following a resource naming scheme, the `index` endpoint will load all of a specific resource. It's a `GET` request that usually accepts parameters so you can filter/order your response. You don't really want to load all of the resource when you call this route. Especially in larger systems where this could literally be over a million items, so these filters usually come in handy or maybe even be required. Let's take a look at our music app example. Say we want to load all of the songs, we will have to implement the `index()` method. To do that, add the following code: ```javascript [Example of calling an index endpoint] index( params ){ return axios.get( 'https://music.com/api/v1/songs', { params: params }) }, ``` *Remember, we set up Axios to be global!* This method accepts a single parameter which should be a JSON object containing any filters, or ordering data that you wish to send. This variable allows you to pass filters to your resource module in the form of a JSON object that Axios will turn into a query string for your API. Remember, sending a [GET request with Axios](/blog/using-axios-to-make-api-requests-with-vuejs/), the second parameter of the Axios GET request is the configuration. This will build a nice query string to append to your request. Because of these filters, this tends to be the most complicated endpoint to create. Let's say you wanted to search for an artist and order by newest releases. You'd pass a JSON object like this: ```json [Example search request] { "artist": "Red Hot Chili Peppers", "order_by": "release_date", "order_direction": "DESC" } ``` Your query string will then be formatted like: `?artist=Red%20Hot%20Chili%20Peppers&order_by=release_date&order_direction=DESC` Let's build out the rest of our endpoints for our module and then we can show the power of this module within VueJS. ### The `show` Endpoint The `show()` method is very similar to the `index()` method since it is a `GET` request. However, the `show()` method should only return a **single** resource. In our example, this is a song. To implement this method simply add the following code: ```javascript [Example of calling a show() endpoint] show( id ){ return axios.get( 'https://music.com/api/v1/songs/'+id ); }, ``` Since we are limiting what this endpoint is returning to a single resource, we don't pass a `params` variable like we did to the `index()` method. The variable `id` allows us to load an individual resource based on it's unique identifier. ### The `create` Endpoint This endpoint will handle the creation of a resource on the API, thus using the `POST` method. As [discussed in the last section](/blog/post-put-patch-requests-with-vuejs-and-axios/), there are two ways to send data to a server with Axios, through JSON and through Form Data. Our API wrapper will account for this by accepting whatever version you throw at it. Let's add the endpoint like this: ```javascript [Example of calling a create() endpoint] create( data ){ return axios.post( 'https://music.com/api/v1/songs', data ); }, ``` The parameter `data` will contain either a JSON object or a FormData object depending on if you need to send files. Either one works! When we implement this module in Step 3 & 4 you can see how fluid this will be to submit data to a server. ### The `update` Endpoint Similar to the `create` endpoint, the `update` endpoint sends data to the API. However, since we are updating a resource, we need an additional parameter of `id` to identify the specific resource we are updating. Depending on the [use case,](/blog/post-put-patch-requests-with-vuejs-and-axios/) you should use `PUT` or `PATCH` for the method. Let's say we are using `PUT`. Our wrapper method should look like this: ```javascript [Example of calling an update() endpoint] update( id, data ){ return axios.put( 'https://music.com/api/v1/songs/'+id, data ); }, ``` We build the URL to update the specific song defined by our `id` parameter. Similar to the `create` endpoint, the `data` parameter can be either JSON or FormData. HOWEVER, if it is FormData, you will have to modify your method in certain circumstances (like with Laravel), to look like this: ```javascript [Using PUT with FormData] update( id, data ){ data._method = 'PUT'; return axios.post( 'https://music.com/api/v1/songs/'+id, data ); }, ``` I know Laravel specifically requires you to `POST` any FormData to the server, but if you add `_method` and set it to `PUT` you can keep your resource controllers standardized and can respond to the request. ### The `delete` Endpoint This is the last endpoint we will be implementing. It simply deletes a resource from the system. The only parameter it accepts is the `id` of the resource we wish to delete: ```javascript [Example of calling a DELETE endpoint] delete( id ){ return axios.delete( 'https://music.com/api/v1/songs/' + id ) } ``` There we go! This is what our final API Wrapper should look like: ```javascript [Final API Wrapper] export default { index( params ){ return axios.get( 'https://music.com/api/v1/songs', { params: params }) }, show( id ){ return axios.get( 'https://music.com/api/v1/songs/'+id ); }, update( id, data ){ return axios.put( 'https://music.com/api/v1/songs/'+id, data ); }, create( data ){ return axios.post( 'https://music.com/api/v1/songs', data ); }, delete( id ){ return axios.delete( 'https://music.com/api/v1/songs/' + id ) } } ``` Now, let's get to the part where our hard work pays off. Using the module within a VueJS component and Vuex module! You will be able to use these methods anywhere you feel is necessary making the code standardized and extremely re-usable! ## Step 3: Using Your API Wrapper in a VueJS Component Now it's time to reap the rewards of our hard work! We can re-use our module in any component or page within our VueJS App! The API Wrapper comes in handy whenever you need to interface with an API. Sometimes, this could be in a parent page (like a layout) as well where you need to load data globally. All you need to do inside of your component is include the API module you created. Let's say we have a page that loads all of the songs from our music app by the `Red Hot Chili Peppers`. We would have a component that looks like this: ```vue [Using our API Wrapper in a Vue component] <template> <div class="songs-page"> </div> </template> <script> import SongsAPI from '../path/to/api/songs.js'; export default { data(){ return { songs: [] } }, mounted(){ SongsAPI.index({ "artist": "Red Hot Chili Peppers", "order_by": "release_date", "order_direction": "DESC" }).then(function( response ){ this.songs = response.data; }.bind(this)); } } </script> ``` All we needed to do is `import` our API module within our component and then call the method in our ` ---