Using Vue Composition API with Pinia - vue.js

I have a Vue 3 project and I'm working with Composition API. I'm communicating with my backend using Urql which is a graphql library which allows me to wrap API requests as composables.
I'm new to Pinia, but after a bit of time working with Vue 2 + Vuex I can tell that one of the most common scenarios of writing actions was making API requests and updating the state (asynchronously) with the response. I'm trying to adopt the same technique in my current tech stack and facing some issues.
My problem is that I can't just use the old fashioned fetch/axios/got libraries to execute requests whenever I want, I should first register the composable somewhere. I've seen that one option is to call the composable's use* function in the store's state section, but it seems weird to me to have such thing in there as it has nothing to do with the state. I've tried executing the use* method directly in an action, but it seems to fail.
I wonder if I'm missing some best-practice way to work with Urql and Pinia, as things are getting more and more complex even though my usecase is pretty common and simple. Should I use Urql's Client directly? Any other good solution to make gql requests from within my store actions?
export const useUsersStore = defineStore('app', {
actions: {
setUser() {
const response = (await useUsers()).data // this wont work
},
}
);

Related

How to test Vue "Services" mounted to root, accessed via Vue.prototype

First, I'd like to explain that I have a Vue component repository that is responsible for displaying data retrieved from an http service. Rather than the component itself managing the same data retrieval per instance and spamming the client with network requests, I've managed to find a solution which allows another component to be mounted to the root directly (which I've dubbed as a "Service" due to its similarity to Angular) to manage the data those components need instead. This works great and other components can access it via Vue.prototype (via this.$TestService.value). It has some caveats but for the most part it accomplishes exactly what I needed. This may be uncommon, but those that use Vuex are using a similar methodology and I don't want to use the store paradigm.
I've made a very simple Vue JsFiddle to show this in action...
https://jsfiddle.net/spronkets/8v31tcfd/18
Now, to the point... I'm using #testing-library/vue, #vue/test-utils, and Jest to test the components and get test coverage and now I get errors anytime I run the tests due to the service not existing on the Vue.prototype during the test execution. I don't want to mock out the functionality of the "Service" layer, so does anyone have a solution to test these root-mounted components? I've tried manually exporting the services (unmounted and mounted) and including them in the mock section as well as importing the files directly into the test files but the "Service" is always undefined when the component is trying to retrieve the value and ONLY during test execution...
I've also created a simple repository modelled after the Vue component repository I am working with below...
https://github.com/kcrossman/VueServiceExample
To get started, clone the repo and follow the README.md included in the repo. Thanks!
I would go against using the real service if it is asyncronous, but if you just want to register it to be available you can follow the mock instructions but instead of mocking with an object just import the real service. Although after seeing your TestService implementation you will need to separate the real service from the service registration and export it to be able to register it in local vue.
You need to create and prepare your custom Vue instance in your tests in order to use any custom functionalities in your unit tests (like stores, routers, and anything else). (You can use your real modules with the custom instance, don't have to mock anything.)
In your case you should create a new Vue instance with "createLocalVue" function from '#vue/test-utils' and apply your custom prototype functionalities on that. After that you can write proper test cases accessing that custom features as well.
Update:
For those that might be referring to this in the future, Vue Plugins might be a better solution for this kind of functionality.
I stumbled along this issue in GitHub and that led me to the fix I made below:
https://github.com/testing-library/vue-testing-library/issues/113
Specifically, this comment by user nikravi:
ok, I found the fix. The trick was to add
import Vue from "vue";
import Vuetify from "vuetify";
Vue.use(Vuetify);
and then the render() works without warnings.
After I manually imported Vue and set Vue.prototype.$TestService = TestService directly in the unit test, it got passed that error. Personally, I think this is pretty silly, but it worked.
After this worked, I also found that you can access the Vue instance directly within the render callback (from #testing-library/vue), so I finished on this code instead of importing Vue:
render(TestComponent, {}, vue => {
vue.prototype.$TestService = TestService;
});
I've included all the commits to solve my issue in the repo I posted previously:
https://github.com/kcrossman/VueServiceExample
Some of the tests were malformed but once I made those changes, the tests started to work and I updated some other files to be a bit nicer for people to refer to.

Prefetching API Data with Apollo for Nuxt.js SSR

I've got what is hopefully a very simple question about prefetching data from within an apollo/nuxt configuration.
I am requesting data from a GraphQL API and want to prefetch the data to use server-side rendering. Reading the docs it seems like I should simply be able to set prefetch: true on my apollo query but this is not working – it is always sending the request from the client side which is causing all kinds of issues.
Here is the code in my component:
apollo: {
concept: {
prefetch: true,
query: conceptStatements,
variables () {
return { id: this.$route.params.id }
}
}
}
I feel like it will have something to do with when this.$route.params.id is evaluated?
Ultimately I would like to create a Vuex store in the store/index.js which takes all of my apollo queries and renders the data in them accessible across the application but the documentation is very vague on how I might do this. Any help would be much appreciated!
Are you really sure your data is not prefetched?
Prefetch is for the first rendering (in SSR mode) ; Nuxt/Apollo call your graphql api, generate the page and send it to the browser. If you just navigate to your page from another route, nuxt call your api from the browser.
You can check this behavior in devtools/network, select xhr. Refresh your page, there is not xhr call. Go to another route and refresh your page, then navigate to your route where is your apollo query and you should see a xhr call to your graphql api.
Also, you can configure this behavior in your apollo config with fetchPolicy.
You don't need Vuex to store response of your apollo queries:
From vue-apollo doc:
When you perform GraphQL queries with Apollo, the results of API calls
will be stored in Apollo cache. Now imagine you also need to store
some kind of a local application state and make it available for
different components. Usually, in Vue application we can achieve this
with Vuex. But having both Apollo and Vuex will mean you store your
data in two different places so you have two sources of truth.
So, if your perform same query from different page or component, Apollo don't call your api each time, but retrieve data from the Apollo cache. Apollo is just magic!

Is possible get which component call some action Vuex?

Is possible to get which component call some action Vuex ?
I return a promise from vuex actions and take some decisions in component as set errors messages on respective fields but I would like to set on component.$validator.errors asap I receive http response (in action method)
Is that possible and a good approach ?
In Vuex when you make a call to a mutation or action you can send an object alongside your dispatch call, if you wanted to track the component which called an action you could also send that as part of the object. Better yet you could have all objects sent via Vuex extend a specific class if your using typescript. Though note that finding which component called an action or mutation is not native behavior for Vuex.
Consider the following:
try{
let out = await this.$store.dispatch('someActionHandler', {referingComponent: this.$options.name, someParam:[1,2,3]})
} catch (e){
// Lets deal with the problem
}
In here we are sending the name of the component as a parameter, so it can be checked inside our action handler or alternatively you could just pass this straight to a mutation, though I think the former is a more likely case if you plan to build logic into this.
As for if this is a good approach, the answer to that is fairly subjective, I personally don't see any problems with the above approach. Though I would think it was an anti pattern if the majority of components are never checked or the added data ends up becoming meaningless fluff passed alongside every call.

Flux without data caching?

Almost all examples of flux involve data cache on the client side however I don't think I would be able to do this for a lot of my application.
In the system I am thinking about using React/Flux, a single user can have 100's of thousands of the main piece of data we store (and 1 record probably has at least 75 data properties). Caching this much data on the client side seems like a bad idea and probably makes things more complex.
If I were not using Flux, I would just have a ORM like system that can talk to a REST API in which case a request like userRepository.getById(123) would always hit the API regardless if I requested that data in the last page. My idea is to just have the store have these methods.
Does Flux consider it bad that if I were to make request for data, that it always hit the API and never pulls data from a local cache instance? Can I use Flux in a way were a majority of the data retrieval requests are always going to hit an API?
The closest you can sanely get to no caching is to reset any store state to null or [] when an action requesting new data comes in. If you do this you must emit a change event, or else you invite race conditions.
As an alternative to flux, you can simply use promises and a simple mixin with an api to modify state. For example, with bluebird:
var promiseStateMixin = {
thenSetState: function(updates, initialUpdates){
// promisify setState
var setState = this.setState.bind(this);
var setStateP = function(changes){
return new Promise(function(resolve){
setState(changes, resolve);
});
};
// if we have initial updates, apply them and ensure the state change happens
return Promise.resolve(initialUpdates ? setStateP(initialUpdates) : null)
// wait for our main updates to resolve
.then(Promise.params(updates))
// apply our unwrapped updates
.then(function(updates){
return setStateP(updates);
}).bind(this);
}
};
And in your components:
handleRefreshClick: function(){
this.thenSetState(
// users is Promise<User[]>
{users: Api.Users.getAll(), loading: false},
// we can't do our own setState due to unlikely race conditions
// instead we supply our own here, but don't worry, the
// getAll request is already running
// this argument is optional
{users: [], loading: true}
).catch(function(error){
// the rejection reason for our getUsers promise
// `this` is our component instance here
error.users
});
}
Of course this doesn't prevent you from using flux when/where it makes sense in your application. For example, react-router is used in many many react projects, and it uses flux internally. React and related libraries/patters are designed to only help where desired, and never control how you write each component.
I think the biggest advantage of using Flux in this situation is that the rest of your app doesn't have to care that data is never cached, or that you're using a specific ORM system. As far as your components are concerned, data lives in stores, and data can be changed via actions. Your actions or stores can choose to always go to the API for data or cache some parts locally, but you still win by encapsulating this magic.

How do you pass data to react components from express or koa without renderToString?

I'm unable to use React's server side rendering due to my use of client side libraries such as reqwest. I would like to pass some data to my react components, however. Is there a way to do this?
The easiest way to do this is by having api-client.js and api.js. In your browserify/webpack config you set up a client side version. For browserify put this in your package.json (feel free to edit and add webpack).
"browser": {
"./path/to/api.js": "path/to/api-client.js"
}
The second option is better in my opinion, but more difficult to implement. You create an abstract representation of your API that works like this:
var comments = require('./api').get('comments');
comments.getById('7').then(function(comment){ ... });
comments.create({...}).then(...);
On the server api.js simply calls the correct functions, which all return promises. On the client it returns a promise, makes an ajax request to the server, which calls these functions, and sends back the response, and the api client resolves/rejects its promise.
This allows the api to automatically work, and allows you to do additional things like track unfulfilled promises, and pre-populate state on the client, etc. (see react-async for example).