Feathers-vuex: "Organizing the services in your project" example code is not SSR compatible

Created on 14 May 2018  路  7Comments  路  Source: feathersjs-ecosystem/feathers-vuex

I believe it's not a bug in Feathers-Vuex itself and just a problem with the example code.

Expected behavior

The guide provides a code example for Organizing the services in your project with the use of require.context.

https://feathers-plus.github.io/v1/feathers-vuex/common-patterns.html#Organizing-the-services-in-your-project

Actual behavior

It works fine in the browser but fails on the server when used in SSR mode.

When you run the server, then open the page in browser and hit refresh, the server tries to redefine store property on the services managed by this feature. The result is:

{ TypeError: Cannot redefine property: store
    at Function.defineProperties (<anonymous>)
    at setupStore (/home/gusto/rpg/services-test/node_modules/feathers-vuex/lib/service-module/service-module.js:128:14)

Using service('users') without require.context behaves properly.

I did try to fix it but failed.

Repro

https://github.com/gustojs/feathers-vuex-services-test

System configuration

Linux Mint
Feathers-Vuex 1.3
Nuxt 1.4
Node 10.1
npm 6.0.1

documentation nuxt

Most helpful comment

Just ran into this same problem and now have a minimal solution.

Documentation code issues:

Issue 1 is that the main file exports new Vuex.Store(...) directly but should be exporting a function, createStore that returns the instance. (Also the Nuxt docs say it should be called index.js instead of store.js but maybe there is another way to set it up that I haven't run into yet)

Issue 2 is calling the service function outside of that createStore function and reusing the first instance on a subsequent call.

Solution:
1) Wrap the main export new Vuex.Store(...) to a createStore function and export default createStore instead
2) In the service file, change:
const servicePlugin = service(servicePath, {
to this:
const servicePlugin = () => service(servicePath, {
3) in the main file, change:
modulePath => requireModule(modulePath).default
to this:
modulePath => requireModule(modulePath).default()
4) Move const servicePlugins = line into the createStore function that's exported so the calls to service functions only happen during the execution of createStore

image

image

I am green here so if any of this is problematic in some other way, please let me know.

Hope it helps!

All 7 comments

This is a bug with Nuxt. I believe Nuxt is literally running the code twice, but I still have to fire up a debugger to confirm.

The workaround is to put everything inside the main store index.

EDIT: Now I start to believe that you meant exactly that by "put everything inside the main store index" so the whole post is redundant. ;)

I made it work with SSR using a combo of few changes. I believe the point is to wait with calling service() until the very last moment, when new Vuex.Store() does it.

First, the code that keeps the closest to your original example:

const servicePlugins = () => {
  return requireModule.keys().map((modulePath) => {
    let { servicePath, serviceStore } = requireModule(modulePath);
    return service(servicePath, serviceStore);
  });
};

Notice two important changes here:
1) our servicePlugins is now a function so we need to use it with ...servicePlugins() now
2) we call service() inside of this function instead of each module file

Our module file looks like this at this point:

import feathersClient from '../../feathers-client';

export const servicePath = 'messages';
export const serviceStore = {
  instanceDefaults: {}
};
feathersClient.service(servicePath).hooks({});

Like I said, this requires the least changes from the original example code. It does the trick and solves the issue.

But we can go further as well:

// extract serviceName from `./name.js`
const serviceNames = requireModule.keys().map((key) => key.substring(2, key.length - 3));
const servicePlugins = () => {
  return serviceNames.map((name) => {
    let { store, hooks } = require('~/store/services/' + name);
    // register service hooks while we're at it
    feathersClient.service(name).hooks(hooks);
    return service(name, store);
  });
};

which leaves us with a module file:

export const store = {};
export const hooks = {};

That's the basic version. The code I actually use is a bit more complex though since I add some features like scaffolding all the boilerplate and manage the configuration through nuxt.config.js. I'll share the link on the chat when the nuxt module is ready.

Just ran into this same problem and now have a minimal solution.

Documentation code issues:

Issue 1 is that the main file exports new Vuex.Store(...) directly but should be exporting a function, createStore that returns the instance. (Also the Nuxt docs say it should be called index.js instead of store.js but maybe there is another way to set it up that I haven't run into yet)

Issue 2 is calling the service function outside of that createStore function and reusing the first instance on a subsequent call.

Solution:
1) Wrap the main export new Vuex.Store(...) to a createStore function and export default createStore instead
2) In the service file, change:
const servicePlugin = service(servicePath, {
to this:
const servicePlugin = () => service(servicePath, {
3) in the main file, change:
modulePath => requireModule(modulePath).default
to this:
modulePath => requireModule(modulePath).default()
4) Move const servicePlugins = line into the createStore function that's exported so the calls to service functions only happen during the execution of createStore

image

image

I am green here so if any of this is problematic in some other way, please let me know.

Hope it helps!

Thanks, @James0x57!!!

In nuxt@3 classic vuex usage new Vuex.Store will be deprecated. There has to be another solution...

@DreaMinder they've updated the docs. You'll still be able to register plugins in modules mode.

I believe this can be closed now that we have a full nuxt example in the documentation. Please reopen if I'm mistaken.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RubyRubenstahl picture RubyRubenstahl  路  3Comments

samburgers picture samburgers  路  7Comments

daenuprobst picture daenuprobst  路  5Comments

kshitizshankar picture kshitizshankar  路  7Comments

HarisHashim picture HarisHashim  路  6Comments