Nuxt.js: Token and auth in own DTO (axios based)

Created on 31 Dec 2017  路  18Comments  路  Source: nuxt/nuxt.js

Hi,

I write my own DTO (Data Transef Object) based on axios router. I need create an instance of my DTO client and set token per request/user on server and i want use that client on both (client and server).

import { Client } from '@caloriosa/dto'

// I need available request, document and app here (on server-side request and on client-side document)

let client = new Client({
 url: "my.api.url",
 token: req.cookies.token || document.cookies.token
}

app.$client = client;
  • In nuxtServerInit i have a request, it's not triggered on client side ($client not instantiated on client side)
  • In plugins I instantiate it, inject to contex, but it's global. Every request overwrites token for client? Or plugin initialization on server is per request (isolated)?
  • Create $client in create or beforeCreate via mixin? Is request available here?
  • Other solution?

So I have no idea where to create my client instance. I need avaliable $client in application on client and on server per request (because token, user identification).

Thanks.

PS: I can't use auth module or axios module, i written own DTO service and I need use it in my project.

This question is available on Nuxt.js community (#c2157)

Most helpful comment

I attached my solution:

plugins/dto.js

import { createApiClient } from '@caloriosa/rest-dto'

const TOKEN_KEY = 'access_token' // Put the name of cookie with API token

function getCookie (cookieName, stringCookie) {
  let regexCookie = new RegExp('' + cookieName + '[^;]+').exec(stringCookie);
  let strCookie = regexCookie ? regexCookie[0] : null
  return strCookie ? unescape(strCookie ? strCookie.toString().replace(/^[^=]+./, '') : '') : null
}

export default ({ app, req }, inject) => {
  let api = createApiClient();
  api.token = getCookie(TOKEN_KEY, req ? req.headers.cookie : document.cookie)
  inject('api', api);
}

nuxt.config.js

module.exports = {
  loading: { color: '#3B8070' },
  build: {
    extend (config, ctx) {
      if (ctx.dev && ctx.isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  },
  plugins: [ 'plugins/dto' ] // This line injects plugin with DTO client. Must be outside of build node
}

And then we can works with $api in client or serverside:

Page or Vue component

// Server-Side

        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  },
  plugins: [ 'plugins/dto' ] // This line injects plugin with DTO client. Must be outside of build node
}

And then we can works with $api in client or serverside:

Page or Vue component

// Server-Side
asyncData({ app }) {
  console.log(app.$api);
  app.$api.users.fetchUsers();
},
// Client-Side
mounted() {
  console.log(this.$api)
  app.$api.users.fetchUsers();
}

All 18 comments

You can use middleware which will be called before each route in Client Side or SSR.

Then create client in only server side maybe sth like:

import { Client } from '@caloriosa/dto'

export default function ({ app, req }) {
  // ...
  if (process.server) {
    app.$client = client;
  }
}

Thanks, it works, but:

I fetched my data in page via asyncData, it works

asyncData({ app }) {
    app.$client.users.fetchUsers();
  }

But in component components/myComp.vue I got Cannot read property 'users' of undefined

mounted() {
    this.fillData(this.$client.users.fetchUsers(););
  }

My middleware middleware/dto.js

import { createApiClient } from '@caloriosa/rest-dto'

export default function ({ app, req }) {
  let client = createApiClient();
  if (process.server) {
    app.$client = client;
  }
}

In nuxt.config.js

router: {
  middleware: [
    'dto'
  ]
}

How i can access to $client in components? I tried use in middleware app.$client = client without that if, but still not works :(

PS: I can access to $client in store?

I suggest you to put client into store, so that it can be used in anywhere.

Still i got Cannot read property 'users' of undefined from component, but in the page it's works.

Middleware

export default function ({ store, req }) {
  let client = createApiClient();
  store.$client = client;
}

myComp

mounted() {
    this.fillData(this.$store.$client.users.fetchUsers())
  }

store is instance of Vuex, you need to declare the state and use $store.state.

https://nuxtjs.org/guide/vuex-store#__nuxt

I got Cannot read property 'fetchUsers' of undefined but in server parent of fetchUsers exists.

store

export const state = () => ({
  api: null
})

export const mutations = {
  setApiClient (state, api) {
    state.api = api
    console.log(state.api);
  }
}

middleware

import { createApiClient } from '@caloriosa/rest-dto'

export default function ({ store, req }) {
  let api = createApiClient();
  store.commit('setApiClient', api);
}

Vuexstore changes structure of my client object?

Dump from server:

API {
  domain: null,
  _events: {},
  _eventsCount: 0,
  _maxListeners: undefined,
  _client: 
   Client {
     _options: 
      { url: 'http://localhost:6060',
        token: null,
        appSignature: null,
        proxy: null },
     url: 'http://localhost:6060',
     _token: null,
     _appSignature: null,
     emiter: 
      EventEmitter {
        domain: null,
        _events: {},
        _eventsCount: 0,
        _maxListeners: undefined },
     defaultArgs: 
      { baseURL: 'http://localhost:6060',
        headers: [Object],
        proxy: null } },
  _services: {} }

Dump from client:

{鈥
domain
:
(...)
_client
:
(...)
_events
:
(...)
_eventsCount
:
(...)
_services
:
(...)
__ob__
:
Observer {value: {鈥, dep: Dep, vmCount: 0}
get domain
:
茠 reactiveGetter()
set domain
:
茠 reactiveSetter(newVal)
get _client
:
茠 reactiveGetter()
set _client
:
茠 reactiveSetter(newVal)
get _events
:
茠 reactiveGetter()
set _events
:
茠 reactiveSetter(newVal)
get _eventsCount
:
茠 reactiveGetter()
set _eventsCount
:
茠 reactiveSetter(newVal)
get _services
:
茠 reactiveGetter()
set _services
:
茠 reactiveSetter(newVal)
__proto__
:
Object

It's different.

Oh my mistake, sorry, you can remove the store and keep a empty store/index.js, and remove the process.server

  export default {
    asyncData({ app }) {
      app.$client.users.fetchUsers();
    },
    mounted() {
      console.log(this.$store.app.$client)
    }
  }
console.log(this.$store.app.$client)

returns undefined

Maybe I have something wrong with middleware?

export default function ({ app, req }) {
  let client = createApiClient();
  if (process.server) {
    app.$client = client;
  }
}

I removed store/index.js

Okay, I uploaded it to git repo: https://github.com/Caloriosa/reporter

Server data fetch is here: https://github.com/Caloriosa/reporter/blob/41e187fc3ef4e8c99a13f79d7883eae8f7653223/pages/index.vue#L14
Component data fetch on client: https://github.com/Caloriosa/reporter/blob/41e187fc3ef4e8c99a13f79d7883eae8f7653223/components/graphs/TemperatureGraph.vue#L22
Middleware: https://github.com/Caloriosa/reporter/blob/master/middleware/dto.js

I want fetch data to page (about users, devices and etc) - server-side - calls rest api from server
And generate graphs - client-side - in components, calls rest api from client

I calling rest users.fetchUsers() for a test if it's works.

Keep store/index.js as an empty file and remove if (process.server)

my store/index.js is empty https://github.com/Caloriosa/reporter/blob/master/store/index.js

and I removed process.server and stil it's not works, still got undefined (2x)

https://github.com/Caloriosa/reporter/commit/f11d084958442d652b07800f0360b5636be3186d

image

It because of middleware will be executed in server side at first access, so client will not get the $api, you can also add a ssr:false plugin to set $api in first access.

So I must copy code $api init to a plugin? I try it. Thanks.

Wtf? I got an error while build:

 TypeError: arguments[i].apply is not a function

  - Tapable.js:375 Compiler.apply
    [reporter]/[tapable]/lib/Tapable.js:375:16

  - webpack.js:33 webpack
    [reporter]/[webpack]/lib/webpack.js:33:19

  - builder.js:418 
    [reporter]/[nuxt]/lib/builder/builder.js:418:24

  - builder.js:417 Builder._callee5$
    [reporter]/[nuxt]/lib/builder/builder.js:417:22

  - builder.js:173 Builder._callee$
    [reporter]/[nuxt]/lib/builder/builder.js:173:16

nuxt.config.js

module.exports = {
  /*
  ** Headers of the page
  */
  head: {
    title: 'my-site',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: 'Nuxt.js project' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },
  router: {
    middleware: [
      'dto'
    ]
  },
  /*
  ** Customize the progress bar color
  */
  loading: { color: '#3B8070' },
  /*
  ** Build configuration
  */
  build: {
    plugins: [
      { src: '~/plugins/dto', ssr: false }
    ],
    /*
    ** Run ESLint on save
    */
    extend (config, ctx) {
      if (ctx.dev && ctx.isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  }
}

plugins/dto.js

import { createApiClient } from '@caloriosa/rest-dto'

export default ({ app }, inject) => {
  let api = createApiClient();
  app.$api = api;
}

Update: Sometimes I got (node:28119) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 2)

I'm sorry, it's my mistake, I had plugins in build. Now it works fine :)

Thank you so much for help! :+1:

I attached my solution:

plugins/dto.js

import { createApiClient } from '@caloriosa/rest-dto'

const TOKEN_KEY = 'access_token' // Put the name of cookie with API token

function getCookie (cookieName, stringCookie) {
  let regexCookie = new RegExp('' + cookieName + '[^;]+').exec(stringCookie);
  let strCookie = regexCookie ? regexCookie[0] : null
  return strCookie ? unescape(strCookie ? strCookie.toString().replace(/^[^=]+./, '') : '') : null
}

export default ({ app, req }, inject) => {
  let api = createApiClient();
  api.token = getCookie(TOKEN_KEY, req ? req.headers.cookie : document.cookie)
  inject('api', api);
}

nuxt.config.js

module.exports = {
  loading: { color: '#3B8070' },
  build: {
    extend (config, ctx) {
      if (ctx.dev && ctx.isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  },
  plugins: [ 'plugins/dto' ] // This line injects plugin with DTO client. Must be outside of build node
}

And then we can works with $api in client or serverside:

Page or Vue component

// Server-Side

        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  },
  plugins: [ 'plugins/dto' ] // This line injects plugin with DTO client. Must be outside of build node
}

And then we can works with $api in client or serverside:

Page or Vue component

// Server-Side
asyncData({ app }) {
  console.log(app.$api);
  app.$api.users.fetchUsers();
},
// Client-Side
mounted() {
  console.log(this.$api)
  app.$api.users.fetchUsers();
}

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jaredreich picture jaredreich  路  3Comments

bimohxh picture bimohxh  路  3Comments

bimohxh picture bimohxh  路  3Comments

shyamchandranmec picture shyamchandranmec  路  3Comments

mattdharmon picture mattdharmon  路  3Comments