Repository: feathersjs-ecosystem/feathers-vuex
Branch: master
Commit: 8643bd7b0cd0
Files: 101
Total size: 756.0 KB
Directory structure:
gitextract_asa44tit/
├── .babelrc
├── .editorconfig
├── .github/
│ ├── FUNDING.yml
│ ├── contributing.md
│ ├── issue_template.md
│ └── pull_request_template.md
├── .gitignore
├── .istanbul.yml
├── .npmignore
├── .travis.yml
├── .vscode/
│ └── launch.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs/
│ ├── .vuepress/
│ │ └── config.js
│ ├── 2.0-major-release.md
│ ├── 3.0-major-release.md
│ ├── api-overview.md
│ ├── auth-plugin.md
│ ├── common-patterns.md
│ ├── composition-api.md
│ ├── data-components.md
│ ├── example-applications.md
│ ├── feathers-vuex-forms.md
│ ├── feathervuex-in-vuejs3-setup.md
│ ├── getting-started.md
│ ├── index.md
│ ├── mixins.md
│ ├── model-classes.md
│ ├── nuxt.md
│ ├── service-plugin.md
│ └── vue-plugin.md
├── mocha.opts
├── notes.old.md
├── package.json
├── src/
│ ├── FeathersVuexCount.ts
│ ├── FeathersVuexFind.ts
│ ├── FeathersVuexFormWrapper.ts
│ ├── FeathersVuexGet.ts
│ ├── FeathersVuexInputWrapper.ts
│ ├── FeathersVuexPagination.ts
│ ├── auth-module/
│ │ ├── auth-module.actions.ts
│ │ ├── auth-module.getters.ts
│ │ ├── auth-module.mutations.ts
│ │ ├── auth-module.state.ts
│ │ ├── make-auth-plugin.ts
│ │ └── types.ts
│ ├── index.ts
│ ├── make-find-mixin.ts
│ ├── make-get-mixin.ts
│ ├── service-module/
│ │ ├── global-clients.ts
│ │ ├── global-models.ts
│ │ ├── make-base-model.ts
│ │ ├── make-service-module.ts
│ │ ├── make-service-plugin.ts
│ │ ├── service-module.actions.ts
│ │ ├── service-module.events.ts
│ │ ├── service-module.getters.ts
│ │ ├── service-module.mutations.ts
│ │ ├── service-module.state.ts
│ │ └── types.ts
│ ├── useFind.ts
│ ├── useGet.ts
│ ├── utils.ts
│ └── vue-plugin/
│ └── vue-plugin.ts
├── stories/
│ ├── .npmignore
│ ├── FeathersVuexFormWrapper.stories.js
│ └── FeathersVuexInputWrapper.stories.js
├── test/
│ ├── auth-module/
│ │ ├── actions.test.js
│ │ └── auth-module.test.ts
│ ├── auth.test.js
│ ├── fixtures/
│ │ ├── fake-data.js
│ │ ├── feathers-client.js
│ │ ├── server.js
│ │ ├── store.js
│ │ └── todos.js
│ ├── index.test.ts
│ ├── make-find-mixin.test.ts
│ ├── service-module/
│ │ ├── make-service-plugin.test.ts
│ │ ├── misconfigured-client.test.ts
│ │ ├── model-base.test.ts
│ │ ├── model-instance-defaults.test.ts
│ │ ├── model-methods.test.ts
│ │ ├── model-relationships.test.ts
│ │ ├── model-serialize.test.ts
│ │ ├── model-temp-ids.test.ts
│ │ ├── model-tests.test.ts
│ │ ├── service-module.actions.test.ts
│ │ ├── service-module.getters.test.ts
│ │ ├── service-module.mutations.test.ts
│ │ ├── service-module.reinitialization.test.ts
│ │ ├── service-module.test.ts
│ │ └── types.ts
│ ├── test-utils.ts
│ ├── use/
│ │ ├── InstrumentComponent.js
│ │ ├── find.test.ts
│ │ └── get.test.ts
│ ├── utils.test.ts
│ └── vue-plugin.test.ts
├── tsconfig.json
└── tsconfig.test.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"plugins": [ "add-module-exports" ],
"presets": [
[ "env", { "modules": false } ],
"es2015",
"stage-2"
]
}
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: marshallswain
================================================
FILE: .github/contributing.md
================================================
# Contributing to Feathers
Thank you for contributing to Feathers! :heart: :tada:
This repo is the main core and where most issues are reported. Feathers embraces modularity and is broken up across many repos. To make this easier to manage we currently use [Zenhub](https://www.zenhub.com/) for issue triage and visibility. They have a free browser plugin you can install so that you can see what is in flight at any time, but of course you also always see current issues in Github.
## Report a bug
Before creating an issue please make sure you have checked out the docs, specifically the [FAQ](https://docs.feathersjs.com/help/faq.html) section. You might want to also try searching Github. It's pretty likely someone has already asked a similar question.
If you haven't found your answer please feel free to join our [slack channel](http://slack.feathersjs.com), create an issue on Github, or post on [Stackoverflow](http://stackoverflow.com) using the `feathers` or `feathersjs` tag. We try our best to monitor Stackoverflow but you're likely to get more immediate responses in Slack and Github.
Issues can be reported in the [issue tracker](https://github.com/feathersjs/feathers/issues). Since feathers combines many modules it can be hard for us to assess the root cause without knowing which modules are being used and what your configuration looks like, so **it helps us immensely if you can link to a simple example that reproduces your issue**.
## Report a Security Concern
We take security very seriously at Feathers. We welcome any peer review of our 100% open source code to ensure nobody's Feathers app is ever compromised or hacked. As a web application developer you are responsible for any security breaches. We do our very best to make sure Feathers is as secure as possible by default.
In order to give the community time to respond and upgrade we strongly urge you report all security issues to us. Send one of the core team members a PM in [Slack](http://slack.feathersjs.com) or email us at hello@feathersjs.com with details and we will respond ASAP.
For full details refer to our [Security docs](https://docs.feathersjs.com/SECURITY.html).
## Pull Requests
We :heart: pull requests and we're continually working to make it as easy as possible for people to contribute, including a [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) and a [common test suite](https://github.com/feathersjs/feathers-service-tests) for database adapters.
We prefer small pull requests with minimal code changes. The smaller they are the easier they are to review and merge. A core team member will pick up your PR and review it as soon as they can. They may ask for changes or reject your pull request. This is not a reflection of you as an engineer or a person. Please accept feedback graciously as we will also try to be sensitive when providing it.
Although we generally accept many PRs they can be rejected for many reasons. We will be as transparent as possible but it may simply be that you do not have the same context or information regarding the roadmap that the core team members have. We value the time you take to put together any contributions so we pledge to always be respectful of that time and will try to be as open as possible so that you don't waste it. :smile:
**All PRs (except documentation) should be accompanied with tests and pass the linting rules.**
### Code style
Before running the tests from the `test/` folder `npm test` will run ESlint. You can check your code changes individually by running `npm run lint`.
### ES6 compilation
Feathers uses [Babel](https://babeljs.io/) to leverage the latest developments of the JavaScript language. All code and samples are currently written in ES2015. To transpile the code in this repository run
> npm run compile
__Note:__ `npm test` will run the compilation automatically before the tests.
### Tests
[Mocha](http://mochajs.org/) tests are located in the `test/` folder and can be run using the `npm run mocha` or `npm test` (with ESLint and code coverage) command.
### Documentation
Feathers documentation is contained in Markdown files in the [feathers-docs](https://github.com/feathersjs/feathers-docs) repository. To change the documentation submit a pull request to that repo, referencing any other PR if applicable, and the docs will be updated with the next release.
## External Modules
If you're written something awesome for Feathers, the Feathers ecosystem, or using Feathers please add it to the [showcase](https://docs.feathersjs.com/why/showcase.html). You also might want to check out the [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) that can be used to scaffold plugins to be Feathers compliant from the start.
If you think it would be a good core module then please contact one of the Feathers core team members in [Slack](http://slack.feathersjs.com) and we can discuss whether it belongs in core and how to get it there. :beers:
## Contributor Code of Conduct
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
================================================
FILE: .github/issue_template.md
================================================
### Steps to reproduce
(First please check that this issue is not already solved as [described
here](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#report-a-bug))
- [ ] Tell us what broke. The more detailed the better.
- [ ] If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc.
### Expected behavior
Tell us what should happen
### Actual behavior
Tell us what happens instead
### System configuration
Tell us about the applicable parts of your setup.
**Module versions** (especially the part that's not working):
**NodeJS version**:
**Operating System**:
**Browser Version**:
**React Native Version**:
**Module Loader**:
================================================
FILE: .github/pull_request_template.md
================================================
### Summary
(If you have not already please refer to the contributing guideline as [described
here](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#pull-requests))
- [ ] Tell us about the problem your pull request is solving.
- [ ] Are there any open issues that are related to this?
- [ ] Is this PR dependent on PRs in other repos?
If so, please mention them to keep the conversations linked together.
### Other Information
If there's anything else that's important and relevant to your pull
request, mention that information here. This could include
benchmarks, or other information.
Your PR will be reviewed by a core team member and they will work with you to get your changes merged in a timely manner. If merged your PR will automatically be added to the changelog in the next release.
If your changes involve documentation updates please mention that and link the appropriate PR in [feathers-docs](https://github.com/feathersjs/feathers-docs).
Thanks for contributing to Feathers! :heart:
================================================
FILE: .gitignore
================================================
.DS_Store
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# Commenting this out is preferred by some people, see
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules
# Users Environment Variables
.lock-wscript
# The compiled/babelified modules
lib/
# Editor directories and files
.idea
.vscode/settings.json
*.suo
*.ntvs*
*.njsproj
*.sln
/dist
================================================
FILE: .istanbul.yml
================================================
verbose: false
instrumentation:
root: ./src/
excludes:
- lib/
include-all-sources: true
reporting:
print: summary
reports:
- html
- text
- lcov
watermarks:
statements: [50, 80]
lines: [50, 80]
functions: [50, 80]
branches: [50, 80]
================================================
FILE: .npmignore
================================================
.editorconfig
.jshintrc
.travis.yml
.istanbul.yml
.babelrc
.idea/
test/
!lib/
================================================
FILE: .travis.yml
================================================
language: node_js
node_js: node
cache: yarn
addons:
code_climate:
repo_token: 'your repo token'
firefox: "51.0"
services:
- xvfb
notifications:
email: false
================================================
FILE: .vscode/launch.json
================================================
{
// Use IntelliSense to learn about possible Node.js debug attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "TS - Mocha Tests",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"--require",
"ts-node/register",
"-u",
"bdd",
"--timeout",
"999999",
"--colors",
"--recursive",
"${workspaceFolder}/test/**/*.ts"
],
"env": {
"TS_NODE_PROJECT": "tsconfig.test.json"
},
"internalConsoleOptions": "openOnSessionStart"
},
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceRoot}/lib/",
"cwd": "${workspaceRoot}"
},
{
"type": "node",
"request": "attach",
"name": "Attach to Process",
"port": 5858
}
]
}
================================================
FILE: CHANGELOG.md
================================================
# Change Log
## [v1.0.0-pre.3](https://github.com/feathersjs/feathers-vuex/tree/v1.0.0-pre.3) (2017-08-26)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v1.0.0-pre.2...v1.0.0-pre.3)
**Closed issues:**
- \[Proposal\] Allow existing store modules to provide additional properties like the customActions, etc. [\#36](https://github.com/feathersjs/feathers-vuex/issues/36)
## [v1.0.0-pre.2](https://github.com/feathersjs/feathers-vuex/tree/v1.0.0-pre.2) (2017-08-24)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v1.0.0-pre.1...v1.0.0-pre.2)
## [v1.0.0-pre.1](https://github.com/feathersjs/feathers-vuex/tree/v1.0.0-pre.1) (2017-08-22)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.8.0...v1.0.0-pre.1)
**Closed issues:**
- Update mutation not changing store or backend [\#38](https://github.com/feathersjs/feathers-vuex/issues/38)
- namespaces cannot be arrays for nested modules \(supported by vuex\) [\#34](https://github.com/feathersjs/feathers-vuex/issues/34)
- using deep-assign package [\#15](https://github.com/feathersjs/feathers-vuex/issues/15)
## [v0.8.0](https://github.com/feathersjs/feathers-vuex/tree/v0.8.0) (2017-08-18)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.7.2...v0.8.0)
**Closed issues:**
- How to setup vuex and vue-router to redirect when a store value is not set? [\#37](https://github.com/feathersjs/feathers-vuex/issues/37)
- OAuth with Google and feathers-vuex [\#33](https://github.com/feathersjs/feathers-vuex/issues/33)
**Merged pull requests:**
- Support array namespaces for module nesting [\#35](https://github.com/feathersjs/feathers-vuex/pull/35) ([jskrzypek](https://github.com/jskrzypek))
## [v0.7.2](https://github.com/feathersjs/feathers-vuex/tree/v0.7.2) (2017-08-04)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.7.1...v0.7.2)
**Closed issues:**
- Mapped Action is not a function [\#31](https://github.com/feathersjs/feathers-vuex/issues/31)
- Real time updates stops working sometimes depending on the query [\#28](https://github.com/feathersjs/feathers-vuex/issues/28)
**Merged pull requests:**
- Return Promise.reject for action path of services when catching an er… [\#32](https://github.com/feathersjs/feathers-vuex/pull/32) ([phenrigomes](https://github.com/phenrigomes))
## [v0.7.1](https://github.com/feathersjs/feathers-vuex/tree/v0.7.1) (2017-07-08)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.7.0...v0.7.1)
**Closed issues:**
- errorOnAuthenticate is not reset [\#29](https://github.com/feathersjs/feathers-vuex/issues/29)
- Hoping to support server paging parameters [\#23](https://github.com/feathersjs/feathers-vuex/issues/23)
**Merged pull requests:**
- Fix 29 [\#30](https://github.com/feathersjs/feathers-vuex/pull/30) ([mgesmundo](https://github.com/mgesmundo))
## [v0.7.0](https://github.com/feathersjs/feathers-vuex/tree/v0.7.0) (2017-06-27)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.6.0...v0.7.0)
**Merged pull requests:**
- Remove “feathers” module completely [\#27](https://github.com/feathersjs/feathers-vuex/pull/27) ([marshallswain](https://github.com/marshallswain))
## [v0.6.0](https://github.com/feathersjs/feathers-vuex/tree/v0.6.0) (2017-06-27)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.5.0...v0.6.0)
**Merged pull requests:**
- Allow customizing a service’s default store [\#26](https://github.com/feathersjs/feathers-vuex/pull/26) ([marshallswain](https://github.com/marshallswain))
## [v0.5.0](https://github.com/feathersjs/feathers-vuex/tree/v0.5.0) (2017-06-27)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.4.2...v0.5.0)
**Closed issues:**
- Are feathers-reactive and RxJS dependencies necessary? [\#22](https://github.com/feathersjs/feathers-vuex/issues/22)
- Weird / destructive behaviour using mapActions service find between components [\#21](https://github.com/feathersjs/feathers-vuex/issues/21)
- removeItems action added after create when upgrading version from version 0.4.0 to 0.4.1 [\#19](https://github.com/feathersjs/feathers-vuex/issues/19)
**Merged pull requests:**
- Remove all non-serializable data from the state [\#25](https://github.com/feathersjs/feathers-vuex/pull/25) ([marshallswain](https://github.com/marshallswain))
- Opt in to `autoRemove` [\#24](https://github.com/feathersjs/feathers-vuex/pull/24) ([marshallswain](https://github.com/marshallswain))
## [v0.4.2](https://github.com/feathersjs/feathers-vuex/tree/v0.4.2) (2017-06-07)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.4.1...v0.4.2)
**Closed issues:**
- What is the benefit to convert data in feathers to vuex instead of accessing feathers services directly? [\#18](https://github.com/feathersjs/feathers-vuex/issues/18)
- items not being removed from the list - fix proposal [\#12](https://github.com/feathersjs/feathers-vuex/issues/12)
**Merged pull requests:**
- QuickFix: Use idField for removal [\#20](https://github.com/feathersjs/feathers-vuex/pull/20) ([cmeissl](https://github.com/cmeissl))
## [v0.4.1](https://github.com/feathersjs/feathers-vuex/tree/v0.4.1) (2017-05-26)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.4.0...v0.4.1)
**Closed issues:**
- Is possible to upload a file using feathers-vuex? [\#11](https://github.com/feathersjs/feathers-vuex/issues/11)
- Integration with Nuxt [\#8](https://github.com/feathersjs/feathers-vuex/issues/8)
**Merged pull requests:**
- Bugfix - add params to patch action service call [\#14](https://github.com/feathersjs/feathers-vuex/pull/14) ([ndamjan](https://github.com/ndamjan))
- fix item removal in addOrUpdateList \(\#12\) [\#13](https://github.com/feathersjs/feathers-vuex/pull/13) ([ndamjan](https://github.com/ndamjan))
## [v0.4.0](https://github.com/feathersjs/feathers-vuex/tree/v0.4.0) (2017-05-01)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.3.1...v0.4.0)
## [v0.3.1](https://github.com/feathersjs/feathers-vuex/tree/v0.3.1) (2017-05-01)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.3.0...v0.3.1)
## [v0.3.0](https://github.com/feathersjs/feathers-vuex/tree/v0.3.0) (2017-05-01)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.2.2...v0.3.0)
**Merged pull requests:**
- Node: Keep functions out of Vuex state [\#9](https://github.com/feathersjs/feathers-vuex/pull/9) ([marshallswain](https://github.com/marshallswain))
## [v0.2.2](https://github.com/feathersjs/feathers-vuex/tree/v0.2.2) (2017-04-28)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.2.1...v0.2.2)
## [v0.2.1](https://github.com/feathersjs/feathers-vuex/tree/v0.2.1) (2017-04-18)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.2.0...v0.2.1)
**Closed issues:**
- `clearList` mutation behaves unexpectedly if `current` isn't defined [\#5](https://github.com/feathersjs/feathers-vuex/issues/5)
**Merged pull requests:**
- Fix clearList unexpected behavior. Closes \#5 [\#6](https://github.com/feathersjs/feathers-vuex/pull/6) ([silvestreh](https://github.com/silvestreh))
## [v0.2.0](https://github.com/feathersjs/feathers-vuex/tree/v0.2.0) (2017-04-18)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.1.1...v0.2.0)
**Merged pull requests:**
- Actions [\#4](https://github.com/feathersjs/feathers-vuex/pull/4) ([marshallswain](https://github.com/marshallswain))
## [v0.1.1](https://github.com/feathersjs/feathers-vuex/tree/v0.1.1) (2017-04-18)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.1.0...v0.1.1)
## [v0.1.0](https://github.com/feathersjs/feathers-vuex/tree/v0.1.0) (2017-04-18)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.0.5...v0.1.0)
**Merged pull requests:**
- Service tests clear list [\#3](https://github.com/feathersjs/feathers-vuex/pull/3) ([marshallswain](https://github.com/marshallswain))
- add before-install script [\#2](https://github.com/feathersjs/feathers-vuex/pull/2) ([marshallswain](https://github.com/marshallswain))
## [v0.0.5](https://github.com/feathersjs/feathers-vuex/tree/v0.0.5) (2017-04-15)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.0.4...v0.0.5)
**Merged pull requests:**
- Update readme.md [\#1](https://github.com/feathersjs/feathers-vuex/pull/1) ([marshallswain](https://github.com/marshallswain))
## [v0.0.4](https://github.com/feathersjs/feathers-vuex/tree/v0.0.4) (2017-04-12)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.0.3...v0.0.4)
## [v0.0.3](https://github.com/feathersjs/feathers-vuex/tree/v0.0.3) (2017-03-15)
[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.0.2...v0.0.3)
## [v0.0.2](https://github.com/feathersjs/feathers-vuex/tree/v0.0.2) (2017-03-15)
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 Feathers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Feathers-Vuex
[](https://travis-ci.org/feathersjs-ecosystem/feathers-vuex)
[](https://david-dm.org/feathersjs-ecosystem/feathers-vuex)
[](https://www.npmjs.com/package/feathers-vuex)
[](https://greenkeeper.io/)
`Feathers-Vuex` is a first class integration of FeathersJS and Vuex. It implements many Redux best practices under the hood, eliminates _a lot_ of boilerplate code with flexible data modeling, and still allows you to easily customize the Vuex store.

## Demo & Documentation
[Demo](https://codesandbox.io/s/xk52mqm7o)
See [https://vuex.feathersjs.com](https://vuex.feathersjs.com) for full documentation.
## Installation
```bash
npm install feathers-vuex --save
```
```bash
yarn add feathers-vuex
```
IMPORTANT: Feathers-Vuex is (and requires to be) published in ES6 format for full compatibility with JS classes. If your project uses Babel, it must be configured properly. See the [Project Configuration](https://vuex.feathersjs.com/getting-started.html#project-configuration) section for more information.
## Contributing
This repo is pre-configured to work with the Visual Studio Code debugger. After running `yarn install`, use the "Mocha Tests" debug script for a smooth debugging experience.
## License
Copyright (c) Forever and Ever, or at least the current year.
Licensed under the [MIT license](https://github.com/feathersjs-ecosystem/feathers-vuex/blob/master/LICENSE).
================================================
FILE: docs/.vuepress/config.js
================================================
module.exports = {
title: 'FeathersVuex',
description: 'Integration of FeathersJS, Vue, and Nuxt for the artisan developer',
head: [['link', { rel: 'icon', href: '/favicon.ico' }]],
theme: 'default-prefers-color-scheme',
themeConfig: {
repo: 'feathersjs-ecosystem/feathers-vuex',
docsDir: 'docs',
editLinks: true,
sidebar: [
'/api-overview.md',
'/3.0-major-release.md',
'/getting-started.md',
'/example-applications.md',
'/vue-plugin.md',
'/service-plugin.md',
'/auth-plugin.md',
'/model-classes.md',
'/common-patterns.md',
'/composition-api.md',
'/mixins.md',
'/data-components.md',
'/feathers-vuex-forms.md',
'/nuxt.md',
'/2.0-major-release.md',
],
serviceWorker: {
updatePopup: true
}
}
}
================================================
FILE: docs/2.0-major-release.md
================================================
---
title: 2.0 Breaking Changes
sidebarDepth: 3
---
# 2.0 Breaking Changes
The biggest change in Feathers-Vuex 2.0 is that it has been refactored with TypeScript! (It's mostly ES6, still)
Your project does NOT require to be written in TypeScript. The `dist` is compiled to ES6.
## Fixing the build for older Vue-CLI Apps
As of October 2019, up-to-date Vue-CLI apps are able to properly build and use `feathers-vuex` after adding `transpileDependencies`. For apps based on older modules, I've found the simplest solution to be to copy the `feathers-vuex` module into `src/libs` inside your project. Create a `copy-deps.sh` file in the root of your project and setup the following:
```bash
rm -rf src/libs
mkdir src/libs
# feathers-vuex
cp -r node_modules/feathers-vuex/dist src/libs/feathers-vuex
```
Then in `package.json`, create a script that uses the `copy-deps.sh` file:
```json
{
"copy": ". ./copy-deps.sh",
"serve": "npm run copy && vue-cli-service serve",
"build": "npm run copy && vue-cli-service build",
"postinstall": "npm run copy"
}
```
It's not the prettiest solution, but it works well and is the simplest for older apps.
## Here's what's new in `feathers-vuex`
Check out the tests for the best documentation. They've been reorganized. This is still a Work in Progress.
## Changes to Initialization
1. To assist in connecting with multiple FeathersJS API servers, a new `serverAlias` option is now required. This requires a simple addition to the initial options.
2. The exports have changed.
- (a) A new `BaseModel` is available. This is the base `FeathersVuexModel` which contains the model methods. Feel free to extend it and make it fit your awesome services!
- (b) The `service` method has been renamed to `makeServicePlugin`.
- (c) The `auth` method is now called `makeAuthPlugin`
- (d) The `models` object is now exported, so you can access them from anywhere. They are keyed by `serverAlias`.
- (e) A new `clients` object is available. The intention is to allow working with multiple FeathersJS API servers.
3. You no longer pass a `servicePath` to create a service-plugin. Instead, pass the actual Feathers service.
4. Since you can customize the Model, you also pass the extended Model into the `makeServicePlugin` method.
Below is an all-in-one example of a the basic configuration steps. See the next section for how to setup a project.
```js
// ./src/store/store.js
import feathers from './feathers-client'
import Vuex from 'vuex'
import feathersVuex from 'feathers-vuex'
const {
BaseModel, // (2a)
makeServicePlugin, // (2b)
makeAuthPlugin, // (2c)
models, // (2d)
clients // (2e)
} = feathersVuex(feathers, {
idField: '_id',
serverAlias: 'myApi' // (1)
})
class Todo extends BaseModel {
// required
constructor (data, options) {
super(data, options)
}
// required
static modelName = 'Todo'
// optional, but useful
static instanceDefaults(data) {
return {
name: '',
isComplete: false,
userId: null,
user: null // populated on the server
}
}
// optional, but useful
static setupInstance(data) {
if (data.user) {
data.user = new models.myApi.User(data.user)
}
return data
}
// customize the model as you see fit!
}
const todosPlugin = makeServicePlugin({
Model: Todo, // (3)
service: feathers.service('todos') // (4)
})
const store = new Vuex.Store({
plugins: [
todosPlugin
]
})
```
## Feathers-Vuex Vue plugin changes
The Vue plugin is registered in exactly the same way. The difference comes when you try to find the Model classes in the `$FeathersVuex` object. Instead of finding models directly on the `$FeathersVuex` object, they are namespaced by the `serverAlias` you provided. This allows cleaner support for multiple APIs. Supposing you had this code in a component, previously...
```js
created () {
// The old way
const { Todo } = this.$FeathersVuex
}
```
Modify it to include the new `serverAlias`. Suppose you set a `serverAlias` of `myApi`, you'd put this in the new version:
```js
created () {
// The new way includes the `serverAlias` of '.myApi'
const { Todo } = this.$FeathersVuex.myApi
}
```
## Better default `idField` support
Since records are keyed by id, `feathers-vuex` needs to know what the `idField` is for each service. In the last version, the default was `id`, and you had to specify something different. This version supports `id` and `_id` with zero configuration. You only need to set `idField` when you're using something other than `id` or `_id`.
There's still a warning message when records don't have a property matching the `idField`. Just like in the last version, it only appears when you turn on `debug: true` in the options.
## Support for Temporary Records
Feathers-Vuex 2.0 supports tracking temporary items and automatically assigns a temporary id to new records. It also adds the records to `state.tempsById`. This is customizable using the `tempIdField` option.
Because of the new ability to handle temporary records, a message is only logged when assigning a temporary id to a record. The `checkId` utility function has been removed, since this was its main purpose.
## Getters Work with Temporary Records
The `find` getter has been updated to include records from `state.tempsById` when you pass `temps: true` in the params.
The `get` getter has also been updated to work with temp ids. Pass the tempId the way you normally would pass the id: `get(tempId)`
## The "currentItem" workflow is no longer supported
The `setCurrent` mutation and `currentId` state encouraged use of a very limiting API. It's much more common for apps to require more than one current record. The `createCopy`, `resetCopy` (formerly called `rejectCopy`), `commitCopy`, and `clearCopy` mutations (since v1.x) provide a more flexible solution for implementing the same functionality. As a result of this, following have been removed from the modules:
- state: `currentID`, `copy`
- getters: `current`, `getCopy`
- mutations: `setCurrent`, `clearCurrent`, `clearList`, `commitCopy`, `clearCopy`, `resetCopy`
## The `diffOnPatch` option has been removed
(See the next section for its replacement.)
I have not been able to find a diffing algorithm that works equally well acroos all schemas. It's especially difficult for nested schemas. Because of this, `diffOnPatch` is no longer a global option. It is being replaced by the `diffOnPatch` static Model method. See the next section.
## Model Classes: BYOD (Bring Your Own Diffing)
> Note: As of `feathers-vuex@3.9.0`, you can also pass `params.data` to the patch object to implement partial patching on objects. You might choose to use `params.data` instead of `diffOnPatch`.
First, why do any diffing? On the API server, an `update` request replaces an entire object, but a `patch` request only overwrites the attributes that are provided in the data. For services with simple schemas, it doesn't really matter. But if your schema grows really large, it can be supportive to only send the updates instead of the entire object.
A new `diffOnPatch` method is available to override in your extended models. `diffOnPatch` gets called just before sending the data to the API server. It gets called with the data and must return the diffed data. By default, it is set to `diffOnPatch: data => data`.
Below is an example of how you might implement `diffOnPatch`. You would only ever use this with a cloned instance, otherwise there's nothing to diff.
```js
import { diff } from 'deep-object-diff'
const { makeServicePlugin, BaseModel } = feathersVuex(feathers, { serverAlias: 'myApi' })
class Todo extends BaseModel {
public constructor (data, options?) {
super(data, options)
}
public static modelName = 'Todo'
public static diffOnPatch (data) {
const originalObject = Todo.store.state.keyedById[data._id]
return diff(originalObject, data)
}
}
const store = new Vuex.Store({
plugins: [
makeServicePlugin({
Model: Todo,
service: feathers.service(servicePath)
})
]
})
```
## The `modelName` option has moved
While the original intent was to completely remove the `modelName` option, it's still required after transpiling to ES5. This is because during transpilation, the class name gets stripped and can't be put back into place. Since ES5 is the default target for most build environments, the `modelName` is still required to be specified, but it has been moved. Instead of being an option, it's required as a static property of each class.
Note: Once ES6 is the default target for most build systems, modelName will become optional. For future upgradability, it's recommended that you give your `modelName` the exact same name as your model class.
```js
const { makeServicePlugin, BaseModel } = feathersVuex(feathers, { serverAlias: 'myApi' })
class Todo extends BaseModel {
public constructor (data, options?) {
super(data, options)
}
public static modelName = 'Todo' // modelName is required on all Model classes.
public static exampleProp: string = 'Hello, World! (notice the comma, folks!)'
}
const store = new Vuex.Store({
plugins: [
makeServicePlugin({
Model: Todo,
service: feathers.service(servicePath)
})
]
})
```
## Options are no longer kept on the Model
The Model class no longer has an `options` property. You can access the same information through the `Model.store.state[Model.namespace]`.
## The 'apiPrefix' option has been removed
Feathers-Vuex now includes full support for communicating with multiple FeathersJS APIs. The `apiPrefix` option was a poorly-implemented, hacky, first attempt at this same feature. Since it didn't work as intended, it has been removed. See this example test for working with multiple APIs:
```js
import { assert } from 'chai'
import Vue from 'vue'
import Vuex from 'vuex'
import {
feathersRestClient as feathers,
makeFeathersRestClient
} from '../../test/fixtures/feathers-client'
import feathersVuex from './index'
it('works with multiple, independent Feathers servers', function() {
// Connect to myApi, create a Todo Model & Plugin
const feathersMyApi = makeFeathersRestClient('https://api.my-api.com')
const myApi = feathersVuex(feathersMyApi, {
idField: '_id',
serverAlias: 'myApi'
})
class Todo extends myApi.BaseModel {
public test: boolean = true
}
const todosPlugin = myApi.makeServicePlugin({
Model: Todo,
service: feathersMyApi.service('todos')
})
// Create a Task Model & Plugin on theirApi
const feathersTheirApi = makeFeathersRestClient('https://api.their-api.com')
const theirApi = feathersVuex(feathersTheirApi, {
serverAlias: 'theirApi'
})
class Task extends theirApi.BaseModel {
public test: boolean = true
}
const tasksPlugin = theirApi.makeServicePlugin({
Model: Task,
service: feathersTheirApi.service('tasks')
})
// Register the plugins
new Vuex.Store({
plugins: [todosPlugin, tasksPlugin]
})
const { models } = myApi
assert(models.myApi.Todo === Todo)
assert(!models.theirApi.Todo, `Todo stayed out of the 'theirApi' namespace`)
assert(models.theirApi.Task === Task)
assert(!models.myApi.Task, `Task stayed out of the 'myApi' namespace`)
assert.equal(
models.myApi.byServicePath[Todo.servicePath],
Todo,
'also registered in models.byServicePath'
)
assert.equal(
models.theirApi.byServicePath[Task.servicePath],
Task,
'also registered in models.byServicePath'
)
```
## Services are no longer set up, internally
You no longer just pass a servicePath. Instead, create the service, then pass the returned service object.
## Simplified Pending Mutations
Previously, there was a mutation for every single variety of method and set/unset pending. (`setFindPending`, `unsetFindPending`, etc.). There were a total of twelve methods for this simple operation. They are now combined into two methods: `setPending(method)` and `unsetPending(method)`. Here's the difference.
```js
// The old way
commit('setFindPending')
commit('unsetFindPending')
// The new way
commit('setPending', 'find')
commit('unsetPending', 'find')
```
## Simplified Error Mutations
The "error" mutations have been simplified similar to the "pending" mutations:
```js
// The old way
commit('setFindError', error)
commit('clearFindError')
// The new way
commit('setError', { method: 'find', error })
commit('clearError', 'find')
```
## `instanceDefaults` must be a function
In the previous version, you could specify instanceDefaults as an object. It was buggy and limiting. In this new version, `instanceDefaults` must always be a function. See the next section for an example.
## Getter and Setter props go on the Model classes
One of the great features about using Model classes is data-level computed properties. You get to specify computed properties directly on your data structures instead of inside components, which keeps a better separation of concerns. In `feathers-vuex@2.x`, since we have direct access to the Model classes, it's the perfect place to define the computed properties:
```js
import feathersClient, {
makeServicePlugin,
BaseModel
} from '../../feathers-client'
class User extends BaseModel {
constructor(data, options) {
super(data, options)
}
static modelName = 'User' // required
// Computed properties don't go on in the instanceDefaults, anymore.
static instanceDefaults() {
return {
firstName: '',
lastName: '',
email: '',
password: '',
isAdmin: false,
}
}
// Here's a computed getter
get fullName() {
return `${this.firstName} ${this.lastName}`
}
// Probably not something you'd do in real life, but it's an example of a setter.
set fullName(fullName) {
const [ firstName, lastName ] = fullName.split(' ')
Object.assign(this, { firstName, lastName })
}
}
const servicePath = 'users'
const servicePlugin = makeServicePlugin({
Model: User,
service: feathersClient.service(servicePath),
servicePath
})
```
## Relationships have been separated from `instanceDefaults`
Feathers-Vuex 2.0 has a new API for establishing relationships between data. Before we cover how it works, let's review the old API.
Feathers-Vuex 1.x allowed using the `instanceDefaults` API to both setup default values for Vue reactivity AND establishing relationships between services. It supported passing a string name that matched a model name to setup a relationship, as shown in this next example. This was a simple, but very limited API:
```js
// The old way
instanceDefaults: {
_id: '',
description: '',
isCompleted: false,
user: 'User'
}
```
Any instance data with a matching key would overwrite the same property in the instanceDefaults, which resulted in an inconsistent API.
In Feathers-Vuex 2.0, the `instanceDefaults` work the same for setting defaults with only one exception: They no longer setup the relationships. The new `setupInstance` function provides an API that is much more powerful.
As mentioned earlier, it MUST be provided as a function:
```js
// See the `model-instance-defaults.test.ts` file for example usage.
// This is a brief example.
instanceDefaults(data, { models, store}) {
return {
_id: '',
description: '',
isCompleted: false
// No user props, here.
}
}
```
Notice in the above example that we did not return `user`. Relationships are now handled in the `setupInstance` method.
Where `instanceDefaults` props get overwritten with instance data, the props returned from `setupInstance` overwrite the instance data. If it were using `Object.assign`, internally (it's not, but IF it were), it would look like the below example, where `data` is the original instance data passed to the constructor.
```js
Object.assign({}, instanceDefaults(data), data, setupInstance(data))
```
## Define Relationships and Modify Data with `setupInstance`
The new `setupInstance` method allows a lot of flexibility in creating new instances. It has the exact same API as the `instanceDefaults` method. The only difference is the order in which they are applied to the instance data.
Although it looks similar to `instanceDefaults`, it can't be used for default values. This is because it overwrites instance data. Having separate methods allows a clean separation between setting up defaults and establishing relationships with other constructors.
```js
// See the `model-relationships.test.ts` file for example usage.
// This is a brief example.
function setupInstance(data, { models, store }) {
const { User, Tag } = models.myServerAlias // Based on the serverAlias you provide, initially
// A single User instance
if (data.user) {
data.user = new User(data.user)
}
// An array of Tag instances
if (data.tags) {
data.tags = data.tags.map(t => new Tag(t))
}
// A JavaScript Date Object
if (data.createdAt) {
data.createdAt = new Date(data.createdAt)
}
return data
}
```
Or below is an example that does the exact same thing with one line per attribute:
```js
function setupInstance(data, { models, store }) {
const { User } = models.myServerAlias
Object.assign(data, {
...(data.user && { user: new User(data.user) }), // A single User instance
...(data.tags && { tags: data.tags.map(t => new Tag(t)) }), // An array of Tag instances
...(data.createdAt && { createdAt: new Date(data.createdAt) }) // A JavaScript Date Object
})
return data
}
```
Where `instanceDefaults` props get replaced by instance data, the props returned from `setupInstance` overwrite the instance data. If it were using `Object.assign`, internally (it's not, but IF it were), it would look like the below example, where `data` is the original instance data passed to the constructor.
```js
Object.assign({}, instanceDefaults(data), data, setupInstance(data))
```
## Preventing duplicate merge when extending BaseModel with a custom constructor
The BaseModel constructor calls `mergeWithAccessors(this, newData)`. This utility function correctly copies data between both regular objects and Vue.observable instances. If you create a class where you need to do your own merging, you probably don't want `mergeWithAccessors` to run twice. In this case, you can use the `merge: false` BaseModel ___instance option___ to prevent the internal merge. You can then access the `mergeWithAccessors` method by calling `MyModel.merge(this, newData)`. Here's an example:
```ts
const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {
serverAlias: 'myApiServer'
})
class Todo extends BaseModel {
public constructor(data, options?) {
options.merge = false // Prevent the internal merge from occurring.
super(data, options)
// ... your custom constructor logic happens here.
// Call the static merge method to do your own merging.
Todo.merge(this, data)
}
}
```
It's important to note that setting `merge: false` in the options will disable the `setupinstance` function. You need to manually call it, like this:
```ts
class Todo extends BaseModel {
public constructor(data, options?) {
options = options || {}
options.merge = false // Prevent the internal merge from occurring.
super(data, options)
// ... your custom construcor logic happens here.
// Call setupInstance manually
const { models, store } = Todo
// JavaScript fundamentals: if you're using `this` in `setupInstance`, use .call(this, ...)
const instanceData = Todo.setupInstance.call(this, data, { models, store })
// If you're not using `this, just call it like normal
const instanceData = Todo.setupInstance(data, { models, store })
// Call the static merge method to do your own merging.
Todo.merge(this, instanceData)
}
}
```
## Customizing the BaseModel
Because we have access to the BaseModel, we can extend it to do whatever custom stuff we need in our application. The `feathers-client.js` file is a great, centralized location for accomplishing this:
```js
// src/feathers-client.js
import feathers from '@feathersjs/feathers'
import socketio from '@feathersjs/socketio-client'
import authClient from '@feathersjs/authentication-client'
import io from 'socket.io-client'
import feathersVuex from 'feathers-vuex' // or '@/libs/feathers-vuex' if you're copying feathers-vuex as mentioned earlier.
// Setup the Feathers client
const host = process.env.VUE_APP_API_URL // or set a string here, directly
const socket = io(host, { transports: ['websocket'] })
const feathersClient = feathers()
.configure(socketio(socket))
.configure(authClient({ storage: window.localStorage }))
export default feathersClient
// Setup feathers-vuex
const {
makeServicePlugin,
makeAuthPlugin,
BaseModel,
models,
clients,
FeathersVuex
} = feathersVuex(feathersClient, {
serverAlias: 'api', // or whatever that makes sense for your project
idField: '_id' // `id` and `_id` are both supported, so this is only necessary if you're using something else.
})
// Note that if you want to
// extend the BaseClass for the rest of the app, this is a great place to do it.
// After you've extended the BaseClass with your CustomClass, export it, here.
class CustomBaseModel extends BaseModel {
// Optionally add custom functionality for all services, here.
}
// Export all of the utilities for the rest of the app.
export {
makeAuthPlugin,
makeServicePlugin,
BaseModel,
models,
clients,
FeathersVuex,
CustomBaseModel // Don't forget to export it for use in all other services.
}
```
## Auth plugin changes
With FeathersJS version 4, the user is returned in the authentication response, by default. This means that it's no longer required to provide a `userService` option to populate the user. 👍
If you would like to enable backwards compatibility with the previous version of Feathers, pass the below code in the makeAuthPlugin.
```js
makeAuthPlugin({
userService: 'users',
actions: {
responseHandler({ commit, state, dispatch }, response) {
if (response.accessToken) {
commit('setAccessToken', response.accessToken)
// Decode the token and set the payload, but return the response
return feathersClient.passport
.verifyJWT(response.accessToken)
.then(payload => {
commit('setPayload', payload)
let user = response[state.responseEntityField]
// If a user was returned in the authenticate response, use that user.
if (user) {
if (state.serverAlias && state.userService) {
const Model = Object.keys(models[state.serverAlias])
.map(modelName => models[state.serverAlias][modelName])
.find(model => model.servicePath === state.userService)
if (Model) {
user = new Model(user)
}
}
commit('setUser', user)
// Populate the user if the userService was provided
} else if (
state.userService &&
payload.hasOwnProperty(state.entityIdField)
) {
return dispatch(
'populateUser',
payload[state.entityIdField]
).then(() => {
commit('unsetAuthenticatePending')
return response
})
} else {
commit('unsetAuthenticatePending')
}
return response
})
// If there was not an accessToken in the response, allow the response to pass through to handle two-factor-auth
} else {
return response
}
}
}
```
The above code will override the `responseHandler` auth action to work with the Passport-based version of Feathers Authentication.
## Gotchas
### Don't Perform Queries (Side Effects) in Getters
Don't try to perform a query from within a getter like the example, below. It will result in an infinite loop:
```js
get user () {
if (this.userId) {
const user = Models.User.getFromStore(this.userId)
// Fetch the User record if we don't already have it
if (!user) {
Models.User.get(this.userId)
}
return user
} else {
return null
}
}
```
### Using custom query parameters
There are two places where the query operators have to be allowed.
- In the Feathers Client (for the actions): refer to the FeathersJS docs for `whitelist`ing operators.
- Inside feathers-vuex (for the getters): Check out the `paramsForServer` and `whitelist` options for `feathers-vuex`. Both accept an array of strings representing prop names, but now I can't remember why I determined that I needed both. :)
For the Feathers Client, follow the FeathersJS docs for your database adapter.
================================================
FILE: docs/3.0-major-release.md
================================================
---
title: What's New in 3.0
sidebarDepth: 3
---
# What's new in Feathers-Vuex 3.0
## Vue Composition API Support
Version 3.0 of Feathers-Vuex is the Vue Composition API release! There were quite a few disappointed (and misinformed:) developers in 2019 when the Vue.js team announced what is now called the Vue Composition API. From my perspective:
- It is the most powerful feature added to Vue since its first release.
- It improves the ability to create dynamic functionality in components.
- It greatly enhances organization of code in components.
- It encourages code re-use. Check out the [vue-use-web](https://tarektouati.github.io/vue-use-web/) collection for some great examples.
And now it has become the best way to perform queries with Feathers-Vuex. To find out how to take advantage of the new functionality in your apps, read the [Feather-Vuex Composition API docs](./composition-api.md).
## New `extend` option for `makeServicePlugin`
The `makeServicePlugin` now supports an `extend` method that allows customizing the store and gives access to the actual Vuex `store` object, as shown in this example:
```js
import { makeServicePlugin } from ‘feathers-vuex’
import { feathersClient } from ‘./feathers-client.js’
class Todo { /* truncated */ }
export default makeServicePlugin({
Model: Todo,
service: feathersClient.service(‘todos’),
extend({ store, module }) {
// Listen to other parts of the store
store.watch(/* truncated */)
return {
state: {},
getters: {},
mutations: {},
actions: {}
}
}
})
```
## Partial data on patch
As of version 3.9.0, you can provide an object as `params.data`, and Feathers-Vuex will use `params.data` as the patch data. This change was made to the service-module, itself, so it will work for `patch` across all of feathers-vuex. Here's an example of patching with partial data:
```js
import { models } from 'feathers-vuex'
const { Todo } = models.api
const todo = new Todo({ description: 'Do Something', isComplete: false })
todo.patch({ data: { isComplete: true } })
// also works for patching with instance.save
todo.save({ data: { isComplete: true } })
```
## FeathersVuexPagination Component
To assist with Server Side Pagination support, Feathers-Vuex now includes the `` component. It's a renderless component that removes the boilerplate behind handling pagination in the UI. Read about it in the [Composition API Docs](/composition-api.html#feathersvuexpagination).
## Custom Handling for Feathers Events
Version 3.1 of Feathers-Vuex enables ability to add custom handling for each of the FeathersJS realtime events. You can read more about it in the [Service Plugin: Events](./service-plugin.md#service-events) docs.
## Breaking Changes
Feathers-Vuex follows semantic versioning. There are two breaking changes in this release:
### Auth Plugin `user` Not Reactive
Due to changes in how reactivity is applied to service state (it's now using Vue.set under the hood), the `user` state of the `auth` module is no longer reactive. To fix this issue, two getters have been added to the `auth` state. They are available when a `userService` is provided to the `makeAuthPlugin` options.
- `user`: returns the reactive, logged-in user from the `userService` specified in the options.
- `isAuthenticated`: a easy to remember boolean attribute for if the user is logged in.
If you depend on a reactive, logged-in user in your apps, here is how to fix the reactivity:
- Replace any reference to `store.state.auth.user` with `store.getters['auth/user']`.
Because the `user` state is no longer reactive, it is logical for it to be removed in the next version. It will likely be replaced by a `userId` attribute in Feathers-Vuex 4.0.
### Server-Side Pagination Support is Off by Default
The `makeFindMixin` (and the new `useFind` utility) now have server-side pagination support turned off, by default. Real-time arrays of results are now the default setting. This really improves the development experience, especially for new users.
To migrate your app to version 3.0, you need to update any `params` where you are using server-side pagination. It will work as it has been in version 2.0 once you explicitly set `paginate: true` in the params, like this:
```js
import { makeFindMixin } from 'feathers-vuex'
export default {
name: 'MyComponent',
mixins: [ makeFindMixin({ service: 'users', watch: true })],
computed: {
usersParams() {
return {
query: {},
paginate: true // explicitly enable pagination, now.
}
}
}
}
```
This behavior exactly matches the new `useFind` utility.
## Deprecations
### The `keepCopiesInStore` Option
The `keepCopiesInStore` option is now deprecated. This was a part of the "clone and commit" API which basically disabled the reason for creating the "clone and commit" API in the first place.
If you're not familiar with the Feathers-Vuex "clone and commit" API, you can learn more about the [built-in data modeling](./model-classes.md) API and the section about [Working with Forms](./feathers-vuex-forms.md#the-clone-and-commit-pattern).
The `keepCopiesInStore` feature is set to be removed in Feathers-Vuex 4.0.
### Auth Plugin State: `user`
As described, earlier on this page, since the Auth Plugin's `user` state is no longer reactive and has been replaced by a `user` getter that IS reactive, the `user` state will be removed in the Feathers-Vuex 4.0.
### Renderless Data Components: `query`, `fetchQuery` and `temps`
To keep consistency with mixins and the composition API it's preferred to use `params` and `fetchParams` instead of the old `query` and `fetchQuery` for renderless data components. Also the `:temps="true"` is deprecated in favour of `:params="{ query: {}, temps: true }"`. This way additional params can be passed to the server if you need some more magic like `$populateParams`.
================================================
FILE: docs/api-overview.md
================================================
---
title: API Overview
sidebarDepth: 3
---
[](https://travis-ci.org/feathersjs-ecosystem/feathers-vuex)
[](https://david-dm.org/feathersjs-ecosystem/feathers-vuex)
[](https://www.npmjs.com/package/feathers-vuex)

> Integrate the Feathers Client into Vuex
`feathers-vuex` is a first class integration of the Feathers Client and Vuex. It implements many Redux best practices under the hood, eliminates *a lot* of boilerplate code, and still allows you to easily customize the Vuex store.
These docs are for version 2.x. For feathers-vuex@1.x, please go to [https://feathers-vuex-v1.netlify.com](https://feathers-vuex-v1.netlify.com).
## Features
- Fully powered by Vuex & Feathers
- Realtime By Default
- Actions With Reactive Data
- Local Queries
- Live Queries
- Feathers Query Syntax
- Vuex Strict Mode Support
- [Client-Side Pagination Support](./service-plugin.md#pagination-and-the-find-getter)
- Fall-Through Caching
- [`$FeathersVuex` Plugin for Vue](./vue-plugin.md)
- [Per-Service Data Modeling](./common-patterns.md#Basic-Data-Modeling-with-instanceDefaults)
- [Clone & Commit](./feathers-vuex-forms.md#the-clone-and-commit-pattern)
- Simplified Auth
- [Per-Record Defaults](./model-classes.md#instancedefaults)
- [Data Level Computed Properties](./2.0-major-release.md#getter-and-setter-props-go-on-the-model-classes)
- [Improved Relation Support](./2.0-major-release.md#define-relationships-and-modify-data-with-setupinstance)
- [Powerful Mixins](./mixins.md)
- [Renderless Data Components](./data-components.md)
- [Renderless Form Component](./feathers-vuex-forms.md#feathersvuexformwrapper) for Simplified Vuex Forms
- [Temporary (Local-only) Record Support](./2.0-major-release.md#support-for-temporary-records) *
- New `useFind` and `useGet` Vue Composition API super powers!
- [Server-Powered Pagination Support](./service-plugin.md#pagination-and-the-find-action) *
- [VuePress Dark Mode Support](https://tolking.github.io/vuepress-theme-default-prefers-color-scheme/) for the Docs
`** Improved in v3.0.0`
## License
Licensed under the [MIT license](LICENSE).
Feathers-Vuex is developed and maintained by [Marshall Thompson](https://www.github.com/marshallswain).
================================================
FILE: docs/auth-plugin.md
================================================
---
title: Auth Plugin
---
The Auth module assists setting up user login and logout.
## Setup
See the [Auth Setup](/getting-started.html#auth-plugin) section for an example of how to setup the Auth Plugin.
## Breaking Changes in 2.0
The following breaking changes were made between 1.x and 2.0:
- The `auth` method is now called `makeAuthPlugin`.
## Configuration
You can provide a `userService` in the auth plugin's options to automatically populate the user upon successful login.
## State
It includes the following state by default:
```js
{
accessToken: undefined, // The JWT
payload: undefined, // The JWT payload
userService: null, // Specify the userService to automatically populate the user upon login.
entityIdField: 'userId', // The property in the payload storing the user id
responseEntityField: 'user', // The property in the payload storing the user
user: null, // Deprecated: This is no longer reactive, so use the `user` getter. See below.
isAuthenticatePending: false,
isLogoutPending: false,
errorOnAuthenticate: undefined,
errorOnLogout: undefined
}
```
## Getters
Two getters are available when a `userService` is provided to the `makeAuthPlugin` options.
- `user`: returns the reactive, logged-in user from the `userService` specified in the options. Returns `null` if not logged in.
- `isAuthenticated`: a easy to remember boolean attribute for if the user is logged in.
## Actions
The following actions are included in the `auth` module. Login is accomplished through the `authenticate` action. For logout, use the `logout` action. It's important to note that the records that were loaded for a user are NOT automatically cleared upon logout. Because the business logic requirements for that feature would vary from app to app, it can't be baked into Feathers-Vuex. It must be manually implemented. The recommended solution is to simply refresh the browser, which clears the data from memory.
- `authenticate`: use instead of `feathersClient.authenticate()`
- `logout`: use instead of `feathersClient.logout()`
If you provided a `userService` and have correctly configured your `entityIdField` and `responseEntityField` (the defaults work with Feathers V4 out of the box), the `user` state will be updated with the logged-in user. The record will also be reactive, which means when the user record updates (in the users service) the auth user will automatically update, as well.
> Note: The Vuex auth store will not update if you use the feathers client version of the above methods.
## Example
Here's a short example that implements the `authenticate` and `logout` actions.
```js
export default {
// ...
methods: {
login() {
this.$store.dispatch('auth/authenticate', {
email: '...',
password: '...'
})
}
// ...
logout() {
this.$store.dispatch('auth/logout')
}
}
// ...
}
```
Note that if you customize the auth plugin's `namespace` then the `auth/` prefix in the above example would change to the provided namespace.
================================================
FILE: docs/common-patterns.md
================================================
---
title: Common Patterns
---
## Set the `idField`
If you have a "WTF this isn't working" moment while setting up a new service, make sure you've set the `idField` property on your service. In `feathers-vuex@1.x`, the `id` is the default `idField`. You have to manually set `_id`. Starting in `feathers-vuex@2.x`, both the `id` and `_id` fields are supported without any configuration, so you only set the `idField` when your service uses something else.
## Enable debugging
You can set `debug: true` in the options to enable some logging to assist with debugging.
## Use the `` and `` components
Using the new `` and `` components provides concise access to the best features of `feathers-vuex`, including live queries, reactive lists, custom pagination tracking per component, and fall-through cacheing of local data in the Vuex store. Check out the [Renderless Data Components](./data-components.html) docs for more details.
## Use the `makeFindMixin` and `makeGetMixin` utilities
The mixin utilities provide the same functionality as the components, but with more power and flexibility. Check out the [Mixin docs](./mixins.html) for more details.
## Working with TypeScript
As of version 2.0, Feathers-Vuex has been rewritten in TypeScript.
See [this issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/114) for suggestions for with TypeScript helpers.
In version 3.0, support for the [Vue Composition API](https://github.com/vuejs/composition-api) was added in the form of the `useFind` and `useGet` utilities. These new utilities provide the best experience for working with TypeScript. This is due to two things:
1. Better general TypeScript support in the Vue Composition API.
1. Consistent object-key naming and types in the new utilities.
Read more about how to use them in the [Feathers-Vuex Composition API Docs](./composition-api.md)
## Clearing data upon user logout
The best solution is to simply refresh to clear memory. The alternative to refreshing would be to perform manual cleanup of the service stores. Refreshing is much simpler, so it's the officially supported solution. Feel free to read [this issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/10) for more suggestions.
## Accessing the store from hooks
Because the service's Model [is available](./service-plugin.html#The-FeathersClient-Service) at `service.FeathersVuexModel`, you can access the store inside hooks. This is especially handy if you have some custom attributes in a paginated server response.
As an example, this `speeding-tickets` service has a `summary` attribute that comes back in the response. We can
```js
import { makeServicePlugin, BaseModel } from '../feathers-client'
class SpeedingTicket extends BaseModel {
constructor(data, options) {
super(data, options)
}
// Required for $FeathersVuex plugin to work after production transpile.
static modelName = 'SpeedingTicket'
// Define default properties here
static instanceDefaults() {
return {
vin: '',
plateState: ''
}
}
}
const servicePath = 'speeding-tickets'
const servicePlugin = makeServicePlugin({
Model: SpeedingTicket,
service: feathersClient.service(servicePath),
servicePath,
mutations: {
handleSummaryData (state, summaryData) {
state.mostRecentSummary = summaryData
}
}
})
feathersClient.service(servicePath)
.hooks({
after: {
find: [
context => {
const { service, result } = context
if (result.summary) {
service.FeathersVuexModel.store.commit('handleSummaryData', result.summary)
}
}
]
}
})
```
## Handling custom server responses
Sometimes your server response may contain more attributes than just database records and pagination data. You could handle this directly in a component, if it's only needed in that one component, But, if you need it in multiple components, there are better options.
Depending on what you need to do, you may be able to solve this by [accessing the store from hooks](#Accessing-the-store-from-hooks). But that solution won't handle a scenario where you need the response data to be already populated in the store.
If you need the response data to already be in the store, you can use the [`afterFind` action](./service-plugin.html#afterFind-response). Here's what this looks like:
```js
import { makeServicePlugin, BaseModel } from '../feathers-client'
class SpeedingTicket extends BaseModel {
constructor(data, options) {
super(data, options)
}
// Required for $FeathersVuex plugin to work after production transpile.
static modelName = 'SpeedingTicket'
// Define default properties here
static instanceDefaults() {
return {
vin: '',
plateState: ''
}
}
}
const servicePath = 'speeding-tickets'
const servicePlugin = makeServicePlugin({
Model: SpeedingTicket,
service: feathersClient.service(servicePath),
servicePath,
actions: {
afterFind ({ commit, dispatch, getters, state }, response) {
if (response.summary) {
commit('handleSummaryData', response.summary)
}
}
},
mutations: {
handleSummaryData (state, summaryData) {
state.mostRecentSummary = summaryData
}
}
})
```
## Reactive Lists with Live Queries
Using Live Queries will greatly simplify app development. The `find` getter enables this feature. Here is how you might setup a component to take advantage of them. The next example shows how to setup two live-query lists using two getters.
```js
import { mapState, mapGetters, mapActions } from 'vuex'
export default {
name: 'some-component',
computed: {
...mapState('appointments', { areAppointmentsLoading: 'isFindPending' }),
...mapGetters('appointments', { findAppointmentsInStore: 'find' } ),
// Query for future appointments
queryUpcoming () {
return { date: { $gt: new Date() }}
},
// Query for past appointments
queryPast () {
return { date: { $lt: new Date() }}
},
// The list of upcoming appointments.
upcomingAppointments () {
return this.findAppointmentsInStore({ query: this.queryUpcoming }).data
},
// The list of past appointments
pastAppointments () {
return this.findAppointmentsInStore({ query: this.queryPast }).data
}
},
methods: {
...mapActions('appointments', { findAppointments: 'find' })
},
created () {
// Find all appointments. We'll use the getters to separate them.
this.findAppointments({ query: {} })
}
}
```
in the above example of component code, the `upcomingAppointments` and `pastAppointments` will automatically update. If a new item is sent from the server, it will get added to one of the lists, automatically. `feathers-vuex` listens to socket events automatically, so you don't have to manually wire any of this up!
## Organizing the services in your project
You can use the file system to organize each service into its own module. This is especially useful in organizing larger-sized projects. Here's an example `store.js`. It uses Webpack's require.context feature save repetitive imports.
See it [here](/getting-started.html#auth-plugin)
With the `store.js` file in place, we can start adding services to the `services` folder.
- [Learn how to setup the feathers-client.js file](/getting-started.html#feathers-client-feathers-vuex)
- [Learn how to setup a Vuex plugin for a Feathers service](/getting-started.html#service-plugins)
- [Learn how to setup the auth plugin](/getting-started.html#auth-plugin)
## Actions return reactive store records
Previously, when you directly used the response from an action, the individual records were not reactive. This meant that these plain objects wouldn't update when you updated the matching record in the store.
```js
methods: {
...mapActions('todos', { findTodos: 'find' })
},
created () {
this.findTodos({ query: {} })
.then(response => {
const todos = response.data || response
// Suppose firstTodo has an id of 'todo-1'
const firstTodo = todos[0]
// Now, when you update the data in the store...
this.$store.state.todos.keyedById['todo-1'].description = 'Updated description'
// ... the instance in the `find` response also updates. Yay!
console.log(firstTodo.description) // --> 'Updated description'
})
}
```
This is a super convenient feature, and it works with all actions (except remove, of course) But be aware that **only the individual records returned are reactive**. The lists, themselves, are not reactive. So if another record comes in from the server that matches the query, the list will not update. For reactive lists, you must use the `find` getter, as shown in the following example.
```js
computed: {
...mapGetters('todos', { findTodosInStore: 'find' })
todos () {
return this.findTodosInStore({ query: {} }).data
}
},
methods: {
...mapActions('todos', { findTodos: 'find' })
},
created () {
this.findTodos({ query: {} })
.then(response => {
// In the find action, the 'todos' array is not a reactive list, but the individual records are.
const todos = response.data || response
})
}
```
In the above example, the computed `todos` will be a reactive list. This means that when new records are added to the store, the list of todos will automatically update in the UI to include the new data.
In summary, you can plan on individual records in the action response data to be reactive, but if you need the actual arrays to be reactive to live queries, use the 'find' getter.
## Basic Data Modeling with `instanceDefaults`
See the [instanceDefaults API](./model-classes.html#instancedefaults)
## Handling Non-Reactive Data
If you are encountering a scenario where certain properties in your records are not reactive, it's probably because they
1. Are not defined in the `instanceDefaults`.
2. Are getting added to the record after it gets added to the Vuex store.
There are two ways to solve this:
1. Add the property to the `instanceDefaults` (see the previous section, above) This tends to be the simplest solution.
2. Make sure the property is added in the responses from the API server.
## Model-Specific Computed Properties
You may find yourself in a position where model-specific computed properties would be very useful. [github issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/163). In Feathers-Vuex 1.7, these could be specified in the `instanceDefaults`. As of 2.0, they are specified directly on each Model class:
```js
class Post extends BaseModel {
// Required for $FeathersVuex plugin to work after production transpile.
static modelName = 'Post'
// Define default properties here
static instanceDefaults() {
return {
description: '',
isComplete: false,
comments: [],
}
}
// Specify computed properties as regular class properties
get numberOfCommenters () {
// Put your logic here.
},
set someOtherProp () {
// Setters also work
}
}
```
## Relationships for Populated Data
If you're looking for a great solution for populating data to work with Feathers-Vuex, check out [feathers-graph-populate](https://feathers-graph-populate.netlify.app/).
A common task with almost any API is properly handling relationships between endpoints. Imagine an API where you have `/todos` and `/users` services. Each todo record can belong to a single user, so a todo has a `userId`.
```js
// GET todos/1
{
id: 1,
description: 'Learn about the health benefits of a low-carb diet.',
isComplete: false,
userId: 5
}
```
And a user response looks like this:
```js
// GET users/5
{
id: 5,
name: 'Marshall',
username: 'marshallswain'
email: 'marshall@ilovehealthy.com'
}
```
Suppose a requirement is put on the `/todos` service to populate the `user` in the response. (As a super handy side note, this task is pretty easy when using [Matt Chaffe's](https://github.com/mattchewone) magical, efficient [feathers-shallow-populate hook](https://www.npmjs.com/package/feathers-shallow-populate)) So now the todo response looks like this:
```js
{
id: 1,
description: 'Learn about the health benefits of a low-carb diet.',
isComplete: false,
userId: 5,
user: {
id: 5,
name: 'Marshall',
username: 'marshallswain'
email: 'marshall@ilovehealthy.com'
}
}
```
Can you see the problem that will occur with this response? When this record is put into the `/todos` store, it will contain a copy of the user record. But we already have the user record in the `/users` store. And what happens when the user data changes? Now it's out of sync. To keep it in sync, you might have to manually listen for `users updated` & `users patched` events. Then you might have to write a custom mutation to update the user record attached to every applicable `todo` record. This gets messy, fast!
There's an easier way to solve this problem. Use the new [`setupInstance` method on Model classes](/model-classes.html#setupinstance).
```js
import feathersClient, { makeServicePlugin, BaseModel } from '../feathers-client'
class Todo extends BaseModel {
// Required for $FeathersVuex plugin to work after production transpile.
static modelName = 'Todo'
// Define default properties here
static instanceDefaults() {
return {
email: '',
password: ''
}
}
// Updates `data.user` to be an instance of the `User` class.
static setupInstance(data, { models }) {
if (data.user) {
data.user = new models.api.User(data.user)
}
return data
}
}
const servicePath = 'todos'
const servicePlugin = makeServicePlugin({
Model: Todo,
service: feathersClient.service(servicePath),
servicePath
})
```
When this record is instantiated, the `user` attribute will first be turned into a User [model instance](./model-classes.html), stored properly in the `/users` store. The `todo.user` attribute will be a reference to that user. No more duplicate data! Here's an example of how to set this up.
There's another amazing benefit from these relationships. Because `feathers-vuex` listens to real-time events and keeps data up to date, when the user record changes, the `todo.user` automatically updates!
### Handling Sequelize Joins
See [this issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/404) for a discussion on how to handle joins with Sequelize. It's important to specify `raw: false`, as shown in [this comment](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/404#issuecomment-571774598).
### Workflow for Saving Model Associations
A great issue was opened on GitHub about the [Workflow for clone and save Model with associations](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/278). That's a great issue to read to get familiar with the workflow.
## Form Binding
Use the Model classes to reduce the boilerplate required to work with forms and Vuex, even in strict mode! Every model instance has a `.clone()` method which can be used to get a fully-reactive copy of the record in the store. Here is a very simple version of how you could bind to a form and submit new data to the server.
```vue
Create Todo
```
## Multiple Copies
The previous version of `feathers-vuex` was hard-coded to allow for a single `current` record and one copy. It was pretty easy to hit that limit. This new release allows for keeping many more copies, one copy per stored record. To make it easier to comply with Vuex's `strict` mode, copies are not kept in the store by default, but are instead kept on `Model.copiesById`. You can make changes to the copies without having to make custom mutations, then you can commit them back into the store:
```js
const { Todo } = this.$FeathersVuex
// Create two records in the store (since they have ids, they get stored)
const todo = new Todo({ id: 1, description: 'Become more aware of others.'})
const todo2 = new Todo({ id: 2, description: 'Heal one ailments through healthy eating.'})
// Create a deep-cloned copies in Todo.copiesById
const todoCopy = todo.clone()
const todoCopy2 = todo2.clone()
// Try to clone a copy, and fail.
todoCopy.clone() // --> Error: You cannot clone a copy.
todoCopy2.clone() // --> Error: You cannot clone a copy.
// Modify the copies.
todoCopy.description.replace('others', 'self')
todoCopy2.description.replace('one', 'all')
// and update the original records
todoCopy.commit()
todoCopy2.commit()
```
You can use the `keepCopiesInStore` option to make this service keep all of its copies in `state.copiesById`. Remember that to comply with Vuex `strict` mode (if that's a concern for you), you'll have to write custom mutations. If it's not a concern (maybe you're the sole developer or whatever reason), you could technically turn off `strict` mode, enable `keepCopiesInStore`, and modify them however you desire, ignoring custom mutations.
```js
import Vue from 'vue'
import Vuex from 'vuex'
import feathersVuex from 'feathers-vuex'
import feathersClient from './feathers-client'
const { service, auth, FeathersVuex } = feathersVuex(feathersClient, { idField: '_id' })
Vue.use(FeathersVuex)
Vue.use(Vuex)
export default new Vuex.Store({
plugins: [
service('todos', {
keepCopiesInStore: true,
instanceDefaults: {
description: '',
complete: false
}
})
]
})
```
## Enable Debug Logging
If items aren't not getting added to the store properly, try setting the `debug` option on the `makeServicePlugin` to `true`. It enables some additional logging that may be useful for troubleshooting.
## Full nuxt example
In this example we will create a nuxt configuration with all the features discribed in the [Nuxt Section](./nuxt.md).
Our application is hosted in 2 different endpoints, one for the feathers api and another with our nuxt SSR, and we will also implement a permission system that will prevent users without an `admin` permission to get into some pages. For that you will need to [customize your payload](https://docs.feathersjs.com/cookbook/authentication/stateless.html#customizing-the-payload) in your feathers api.
```js
// nuxt.config.js
export default {
env: {
API_URL: process.env.API_URL || 'http://localhost:3030'
},
router: {
middleware: [
'feathers'
]
},
plugins: [
{ src: '~/plugins/feathers-vuex.js' }
],
modules: [
'nuxt-client-init-module'
],
build: {
transpile: [
'feathers-vuex'
]
}
}
```
The `feathers` middleware will redirect any user in a non public page to the login, but will also redirect a loged user away from any public pages.
```js
// ~/middleware/feathers.js
export default function ({ store, redirect, route }) {
const { auth } = store.state
if (auth.publicPages.length > 0 && !auth.publicPages.includes(route.name) && !auth.payload) {
return redirect('/login')
} else if (auth.publicPages.length > 0 && auth.publicPages.includes(route.name) && auth.payload) {
return redirect('/feed')
}
}
```
The `admin` middleware will redirect any user that is not loged in or do not have the `admin` permission to a `not-permited` page.
```js
// ~/middleware/admin.js
export default function ({ store, redirect }) {
const { auth } = store.state
const permissions = auth.payload.permissions
if (
!auth.payload ||
!permissions.includes('admin')
) {
return redirect('/not-permited')
}
}
```
Add the `admin` middleware to the administrative pages
```js
// ~/pages/**
export default {
...
middleware: ['admin']
...
}
```
In the feathers client configuration we will use a different transport for the nuxt-server > api comunication and the nuxt-client > api. When we are making request on the server we do not need the socket io realtime messaging system so we can use an rest configuration for better performance.
```js
// ~/plugins/feathers.js
import feathers from '@feathersjs/feathers'
import rest from '@feathersjs/rest-client'
import axios from 'axios'
import socketio from '@feathersjs/socketio-client'
import auth from '@feathersjs/authentication-client'
import io from 'socket.io-client'
import { CookieStorage } from 'cookie-storage'
import { iff, discard } from 'feathers-hooks-common'
import feathersVuex, { initAuth, hydrateApi } from 'feathers-vuex'
// Get the api url from the environment variable
const apiUrl = process.env.API_URL
let socket
let restClient
// We won't use socket to comunicate from server to server
if (process.client) {
socket = io(apiUrl, { transports: ['websocket'] })
} else {
restClient = rest(apiUrl)
}
const transport = process.client ? socketio(socket) : restClient.axios(axios)
const storage = new CookieStorage()
const feathersClient = feathers()
.configure(transport)
.configure(auth({ storage }))
.hooks({
before: {
all: [
iff(
context => ['create', 'update', 'patch'].includes(context.method),
discard('__id', '__isTemp')
)
]
}
})
export default feathersClient
// Setting up feathers-vuex
const { makeServicePlugin, makeAuthPlugin, BaseModel, models, FeathersVuex } = feathersVuex(
feathersClient,
{
serverAlias: 'api', // optional for working with multiple APIs (this is the default value)
idField: '_id', // Must match the id field in your database table/collection
whitelist: ['$regex', '$options'],
enableEvents: process.client // Prevent memory leak
}
)
export { makeAuthPlugin, makeServicePlugin, BaseModel, models, FeathersVuex, initAuth, hydrateApi }
```
I prefere install the `FeathersVuex` plugin in a separate file, it's more consistent with nuxt patterns.
```js
// ~/plugins/feathers-vuex.js
import Vue from 'vue'
import { FeathersVuex } from './feathers'
Vue.use(FeathersVuex)
```
Configure any service you want on `~/store/services/*.js`.
```js
// ~/store/services/users.js
import feathersClient, { makeServicePlugin, BaseModel } from '~/plugins/feathers'
class User extends BaseModel {
constructor(data, options) {
super(data, options)
}
// Required for $FeathersVuex plugin to work after production transpile.
static modelName = 'User'
// Define default properties here
static instanceDefaults() {
return {
email: '',
password: '',
permissions: []
}
}
}
const servicePath = 'users'
const servicePlugin = makeServicePlugin({
Model: User,
service: feathersClient.service(servicePath),
servicePath
})
// Setup the client-side Feathers hooks.
feathersClient.service(servicePath).hooks({
before: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
after: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
error: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
})
export default servicePlugin
```
Create your nuxt store with the right nuxt pattern, exporting an Vuex store will be deprecated on nuxt 3.
```js
// ~/store/index.js
import { makeAuthPlugin, initAuth, hydrateApi, models } from '~/plugins/feathers'
const auth = makeAuthPlugin({
userService: 'users',
state: {
publicPages: [
'login',
'signup'
]
},
actions: {
onInitAuth ({ state, dispatch }, payload) {
if (payload) {
dispatch('authenticate', { strategy: 'jwt', accessToken: state.accessToken })
.then((result) => {
// handle success like a boss
console.log('loged in')
})
.catch((error) => {
// handle error like a boss
console.log(error)
})
}
}
}
})
const requireModule = require.context(
// The path where the service modules live
'./services',
// Whether to look in subfolders
false,
// Only include .js files (prevents duplicate imports`)
/.js$/
)
const servicePlugins = requireModule
.keys()
.map(modulePath => requireModule(modulePath).default)
export const modules = {
// Custom modules
}
export const state = () => ({
// Custom state
})
export const mutations = {
// Custom mutations
}
export const actions = {
// Custom actions
nuxtServerInit ({ commit, dispatch }, { req }) {
return initAuth({
commit,
dispatch,
req,
moduleName: 'auth',
cookieName: 'feathers-jwt'
})
},
nuxtClientInit ({ state, dispatch, commit }, context) {
hydrateApi({ api: models.api })
if (state.auth.accessToken) {
return dispatch('auth/onInitAuth', state.auth.payload)
}
}
}
export const getters = {
// Custom getters
}
export const plugins = [ ...servicePlugins, auth ]
```
================================================
FILE: docs/composition-api.md
================================================
---
title: Composition API
sidebarDepth: 3
---
# Feathers-Vuex Composition API
In addition to the Renderless Components and the Mixins, Feathers-Vuex includes utilities that let you take advantage of the [Vue Composition API](https://github.com/vuejs/composition-api).
It's important to note that the `@vue/composition-api` plugin must be registered BEFORE you import `App.vue`. This means that you need to modify your `main.js` file to look like this:
```js
import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
// Register the Composition API plugin BEFORE you import App.vue
Vue.use(VueCompositionApi)
import App from './App.vue'
import router from './router'
import store from './store/store'
import './plugins/plugins'
Vue.config.productionTip = true
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
```
If you forget to register the plugin, first, you will see a console warning from Vue stating `[Vue warn]: Error in data(): "Error: [vue-composition-api] must call Vue.use(plugin) before using any function.". To fix this, make sure to register the plugin BEFORE you call any Composition utility functions, as shown above.
## Setup
Before you can use the `useFind` and `useGet` composition functions, you'll need to [install the Vue Composition API](https://github.com/vuejs/composition-api#Installation) plugin.
## Detour: Reading a TypeScript interface
The next few sections show various TypeScript interfaces, which are basically shorthand descriptions of the types of data that make up a variable. In this case, they're used to show the `options` object which can be passed to each of the composition api utilities. If this is your first time with interfaces, here's a quick primer as an alternative to reading the [TypeScript interface docs](https://www.typescriptlang.org/docs/handbook/interfaces.html):
- In the [first interface example](#options), below, `UseFindOptions` is the name of the interface, similar to naming any other variable. When using TypeScript, you can import and pass interfaces around like variables.
- Each line of the interface describes a property.
- The part before the `:` is the name of the property.
- The part after the `:` describes what type of variable it can be.
- You can look at any `|` after the `:` as a conditional "or"
- Any property followed by a `?` is optional.
- Any property not followed by a `?` is required.
## useFind
The `useFind` utility reduces boilerplate for querying with fall-through cache and realtime updates. To get started with it you provide a `model` class and a computed `params` object.
Let's use the example of creating a User Guide, where we need to pull in the various `Tutorial` records from our `tutorials` service. We'll keep it simple in the template and just show a list of the names.
```html
{{ tutorial.name }}
```
Let's review each of the numbered comments, above:
1. Get a reference to the model class. With the Vue Composition API, there's no `this` object. It has been replaced by the context object. So, only when using the composition API, the `$FeathersVuex` object is found at `context.root.$FeathersVuex`
2. Create a computed property for the params. Return an object with a nested `query` object.
### Options
Since we learned earlier [how to read a TypeScript interface](#detour-reading-a-typescript-interface), let's look at the TypeScript definition for the `UseFindOptions` interface.
```ts
interface UseFindOptions {
model: Function
params: Params | Ref
fetchParams?: Params | Ref
queryWhen?: Ref
qid?: string
immediate?: boolean
}
```
And here's a look at each individual property:
- `model` must be a Feathers-Vuex Model class. The Model's `find` and `findInStore` methods are used to query data.
- `params` is a FeathersJS Params object OR a Composition API `ref` (or `computed`, since they return a `ref` instance) which returns a Params object.
- When provided alone (without the optional `fetchParams`), this same query is used for both the local data store and the API requests.
- Explicitly returning `null` will prevent an API request from being made.
- You can use `params.qid` to dynamically specify the query identifier for any API request. The `qid` is used for tracking pagination data and enabling the fall-through cache across multiple queries.
- Set `params.paginate` to `true` to turn off realtime updates for the results and defer pagination to the API server. Pagination works the same as for the [makeFindMixin pagination](./mixins.html#pagination-with-fall-through-cacheing).
- Set `params.debounce` to an integer and the API requests will automatically be debounced by that many milliseconds. For example, setting `debounce: 1000` will assure that the API request will be made at most every 1 second.
- Set `params.temps` to `true` to include temporary (local-only) items in the results. Temporary records are instances that have been created but not yet saved to the database.
- Set `params.copies` to `true` to include cloned items in the results. The queried items get replaced with the corresponding copies from `copiesById`
- `fetchParams` This is a separate set of params that, when provided, will become the params sent to the API server. The `params` will then only be used to query data from the local data store.
- Explicitly returning `null` will prevent an API request from being made.
- `queryWhen` must be a `computed` property which returns a `boolean`. It provides a logical separation for preventing API requests *outside* of the `params`.
- `qid` allows you to specify a query identifier (used in the pagination data in the store). This can also be set dynamically by returning a `qid` in the params.
- `immediate`, which is `true` by default, determines if the internal `watch` should fire immediately. Set `immediate: false` and the query will not fire immediately. It will only fire on subsequent changes to the params.
### Returned Attributes
Notice the `tutorialsData` in the previous example. You can see that there's an `tutorialsData.items` property, which is returned at the bottom of the `setup` method as `tutorials`. There are many more attributes available in the object returned from `useFind`. We can learn more about the return values by looking at its TypeScript interface, below.
```ts
interface UseFindData {
items: Ref
paginationData: Ref