Skip to content

Commit bc7bf8a

Browse files
committed
Update nuxt docs
1 parent f8f6eff commit bc7bf8a

File tree

2 files changed

+497
-35
lines changed

2 files changed

+497
-35
lines changed

docs/common-patterns.md

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,288 @@ export default new Vuex.Store({
507507
## Enable Debug Logging
508508

509509
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.
510+
511+
## Full nuxt example
512+
513+
In this example we will create a nuxt configuration with all the features discribed in the [Nuxt Section](./nuxt.md).
514+
515+
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.
516+
517+
```js
518+
// nuxt.config.js
519+
export default {
520+
env: {
521+
API_URL: process.env.API_URL || 'http://localhost:3030'
522+
},
523+
router: {
524+
middleware: [
525+
'feathers'
526+
]
527+
},
528+
plugins: [
529+
{ src: '~/plugins/feathers-vuex.js' }
530+
],
531+
modules: [
532+
'nuxt-client-init-module'
533+
],
534+
build: {
535+
transpile: [
536+
'feathers-vuex'
537+
]
538+
}
539+
}
540+
```
541+
542+
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.
543+
544+
```js
545+
// ~/middleware/feathers.js
546+
export default function ({ store, redirect, route }) {
547+
const { auth } = store.state
548+
if (auth.publicPages.length > 0 && !auth.publicPages.includes(route.name) && !auth.payload) {
549+
return redirect('login')
550+
} else if (auth.publicPages.length > 0 && auth.publicPages.includes(route.name) && auth.payload) {
551+
return redirect('feed')
552+
}
553+
}
554+
```
555+
556+
The `admin` middleware will redirect any user that is not loged in or do not have the `admin` permission to a `not-permited` page.
557+
558+
```js
559+
// ~/middleware/admin.js
560+
export default function ({ store, redirect }) {
561+
const { auth } = store.state
562+
const permissions = auth.payload.permissions
563+
if (
564+
!auth.payload ||
565+
!permissions.includes('admin')
566+
) {
567+
return redirect('/not-permited')
568+
}
569+
}
570+
```
571+
Add the `admin` middleware to the administrative pages
572+
573+
```js
574+
// ~/pages/**
575+
export default {
576+
577+
...
578+
579+
middleware: ['admin']
580+
581+
...
582+
583+
}
584+
```
585+
586+
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.
587+
588+
```js
589+
// ~/plugins/feathers.js
590+
import feathers from '@feathersjs/feathers'
591+
import rest from '@feathersjs/rest-client'
592+
import axios from 'axios'
593+
import socketio from '@feathersjs/socketio-client'
594+
import auth from '@feathersjs/authentication-client'
595+
import io from 'socket.io-client'
596+
import { CookieStorage } from 'cookie-storage'
597+
import { iff, discard } from 'feathers-hooks-common'
598+
import feathersVuex, { initAuth, hydrateApi } from 'feathers-vuex'
599+
// Get the api url from the environment variable
600+
const apiUrl = process.env.API_URL
601+
let socket
602+
let restClient
603+
// We won't use socket to comunicate from server to server
604+
if (process.client) {
605+
socket = io(apiUrl, { transports: ['websocket'] })
606+
} else {
607+
restClient = rest(apiUrl)
608+
}
609+
const transport = process.client ? socketio(socket) : restClient.axios(axios)
610+
const storage = new CookieStorage()
611+
612+
const feathersClient = feathers()
613+
.configure(transport)
614+
.configure(auth({ storage }))
615+
.hooks({
616+
before: {
617+
all: [
618+
iff(
619+
context => ['create', 'update', 'patch'].includes(context.method),
620+
discard('__id', '__isTemp')
621+
)
622+
]
623+
}
624+
})
625+
626+
export default feathersClient
627+
628+
// Setting up feathers-vuex
629+
const { makeServicePlugin, makeAuthPlugin, BaseModel, models, FeathersVuex } = feathersVuex(
630+
feathersClient,
631+
{
632+
serverAlias: 'api', // optional for working with multiple APIs (this is the default value)
633+
idField: '_id', // Must match the id field in your database table/collection
634+
whitelist: ['$regex', '$options'],
635+
enableEvents: process.client // Prevent memory leak
636+
}
637+
)
638+
639+
export { makeAuthPlugin, makeServicePlugin, BaseModel, models, FeathersVuex, initAuth, hydrateApi }
640+
```
641+
642+
I prefere install the `FeathersVuex` plugin in a separate file, it's more consistent with nuxt patterns.
643+
644+
```js
645+
// ~/plugins/feathers-vuex.js
646+
import Vue from 'vue'
647+
import { FeathersVuex } from './feathers'
648+
649+
Vue.use(FeathersVuex)
650+
```
651+
652+
Configure any service you want on `~/store/services/*.js`.
653+
654+
```js
655+
// ~/store/services/users.js
656+
import feathersClient, { makeServicePlugin, BaseModel } from '~/plugins/feathers'
657+
658+
class User extends BaseModel {
659+
constructor(data, options) {
660+
super(data, options)
661+
}
662+
// Required for $FeathersVuex plugin to work after production transpile.
663+
static modelName = 'User'
664+
// Define default properties here
665+
static instanceDefaults() {
666+
return {
667+
email: '',
668+
password: '',
669+
permissions: []
670+
}
671+
}
672+
}
673+
const servicePath = 'users'
674+
const servicePlugin = makeServicePlugin({
675+
Model: User,
676+
service: feathersClient.service(servicePath),
677+
servicePath
678+
})
679+
680+
// Setup the client-side Feathers hooks.
681+
feathersClient.service(servicePath).hooks({
682+
before: {
683+
all: [],
684+
find: [],
685+
get: [],
686+
create: [],
687+
update: [],
688+
patch: [],
689+
remove: []
690+
},
691+
after: {
692+
all: [],
693+
find: [],
694+
get: [],
695+
create: [],
696+
update: [],
697+
patch: [],
698+
remove: []
699+
},
700+
error: {
701+
all: [],
702+
find: [],
703+
get: [],
704+
create: [],
705+
update: [],
706+
patch: [],
707+
remove: []
708+
}
709+
})
710+
711+
export default servicePlugin
712+
```
713+
714+
Create your nuxt store with the right nuxt pattern, exporting an Vuex store will be deprecated on nuxt 3.
715+
716+
```js
717+
// ~/store/index.js
718+
import { makeAuthPlugin, initAuth, hydrateApi, models } from '~/plugins/feathers'
719+
const auth = makeAuthPlugin({
720+
userService: 'users',
721+
state: {
722+
publicPages: [
723+
'login',
724+
'signup'
725+
]
726+
},
727+
actions: {
728+
onInitAuth ({ state, dispatch }, payload) {
729+
if (payload) {
730+
dispatch('authenticate', { strategy: 'jwt', accessToken: state.accessToken })
731+
.then((result) => {
732+
// handle success like a boss
733+
console.log('loged in')
734+
})
735+
.catch((error) => {
736+
// handle error like a boss
737+
console.log(error)
738+
})
739+
}
740+
}
741+
}
742+
})
743+
744+
const requireModule = require.context(
745+
// The path where the service modules live
746+
'./services',
747+
// Whether to look in subfolders
748+
false,
749+
// Only include .js files (prevents duplicate imports`)
750+
/.js$/
751+
)
752+
const servicePlugins = requireModule
753+
.keys()
754+
.map(modulePath => requireModule(modulePath).default)
755+
756+
export const modules = {
757+
// Custom modules
758+
}
759+
760+
export const state = () => ({
761+
// Custom state
762+
})
763+
764+
export const mutations = {
765+
// Custom mutations
766+
}
767+
768+
export const actions = {
769+
// Custom actions
770+
nuxtServerInit ({ commit, dispatch }, { req }) {
771+
return initAuth({
772+
commit,
773+
dispatch,
774+
req,
775+
moduleName: 'auth',
776+
cookieName: 'feathers-jwt'
777+
})
778+
},
779+
nuxtClientInit ({ state, dispatch, commit }, context) {
780+
781+
hydrateApi({ api: models.api })
782+
783+
if (state.auth.accessToken) {
784+
return dispatch('auth/onInitAuth', state.auth.payload)
785+
}
786+
}
787+
}
788+
789+
export const getters = {
790+
// Custom getters
791+
}
792+
793+
export const plugins = [ ...servicePlugins, auth ]
794+
```

0 commit comments

Comments
 (0)