Open lopermo opened 4 years ago
I'm seeing this too. Impossible to get access to the error object directly. It's locked as a string now, of this format Error: GraphQL error: {...}
.
apollo: {
errorHandler: "~/plugins/apollo-error-handler.js",
clientConfigs: {
default: "~/plugins/apollo-config-default.js"
}
}
But error handler apollo-error-handler.js
is this:
export default (
{ graphQLErrors, networkError, operation, forward },
nuxtContext
) => {
console.log("Global error handler")
console.log(graphQLErrors, networkError, operation, forward)
}
I'm hoping once this works, I can catch a network error and handle a token refresh call. If anyone has any tips on a better way to handle seamless token refresh I'd love to hear it.
Errors...
Would you mind explaining how did you set it up?
Would you mind explaining how did you set it up?
I'm trying to use the 'apollo' module on nuxt, but I have two problems. Whenever I try to make a query it returns the value in the SSR, and twice in the client
This query that I'm trying to access is limited, I give an apollo error with access denied. Breaking AI leaves this error more.
vue.runtime.esm.js?2b0e:619 [Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render.
<script> import gql from 'graphql-tag' export default { name: 'Teste', apollo: { account: { query: gql { account { id login real_name email telefone zipcode create_time status availDt last_play cash mileage avatar capa pais roles } } , update(data) { console.log(data) return data.account }, deep: false, prefetch: true, fetchPolicy: 'network-only' } }, data() { return { account: null } } } </script>
Sorry for any typing mistakes, I am Brazilian and I will use the Google translator.
But then, you can use asyncData and call a query using the this function. $ Apollo.query (...) It works normally, so there are errors with then / catch.
Believe or solve problems in error handling in SSR,
@rospirski I think your errors are unrelated to the error handler.
I made a sandbox showing the error handler not working: https://codesandbox.io/s/apollo-broken-error-handler-499o7
Hi, try to add error handler to plugins section in your nuxt.config too. It works for me when using apollo module in component like you have in sandbox.
@dmitrijt9 Can you share your config? Weird you need to define it in 2 places.
@drewbaker I's weird for me either. Here it is:
require('dotenv').config()
module.exports = {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [
'~/assets/css/tailwind.css',
'@fortawesome/fontawesome-svg-core/styles.css'
],
tailwindcss: {
cssPath: '~/assets/css/tailwind.css',
},
/*
** Plugins to load before mounting the App
*/
plugins: [
'~/plugins/fontawesome.js',
'~/plugins/apollo-error-handler.js'
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
// Doc: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module',
// Doc: https://github.com/nuxt-community/nuxt-tailwindcss
'@nuxtjs/tailwindcss',
// Doc: https://github.com/nuxt-community/dotenv-module
'@nuxtjs/dotenv',
],
// dotenv options
dotenv: {
path: '../../' // point to global .env file
},
eslint: {
fix: true
},
router: {
middleware: ['auth']
},
proxy: {
'/api': {
target: 'http://localhost:4000',
pathRewrite: {'^/api': '/'}
},
'/api/playground': {
target: 'http://localhost:4000/playground',
pathRewrite: {'^/api': '/'}
}
},
/*
** Nuxt.js modules
*/
modules: [
'@nuxtjs/apollo',
'@nuxtjs/proxy',
'@nuxtjs/toast',
[
'nuxt-i18n',
{
defaultLocale: 'en',
locales: [
{ code: 'cs', iso: 'cs-CZ', file: 'cs.json'},
{ code: 'en', iso: 'en-Us', file: 'en.json'}
],
lazy: true,
langDir: 'translations/',
parsePages: false,
pages: {
about: {
cs: '/o-aplikaci',
en: '/about'
},
app: {
cs: '/app',
en: '/app',
},
'app/dashboard': {
cs: '/app/nastenka',
en: '/app/dashboard'
},
'app/calendar': {
cs: '/app/kalendar',
en: '/app/calendar'
},
'app/tasks': {
cs: '/app/ukoly',
en: '/app/tasks'
},
'app/team': {
cs: '/app/tym',
en: '/app/team'
},
'app/discussion': {
cs: '/app/diskuze',
en: '/app/discussion'
},
}
}
]
],
// Apollo config
apollo: {
tokenName: 'apollo-token',
cookieAttributes: {
secure: process.env.ENV !== 'dev',
expires: 365,// cookie expiration 1 year
path: '/'
},
clientConfigs: {
default: {
httpEndpoint: 'http://localhost:4000',
browserHttpEndpoint: '/api'
}
},
errorHandler: '~/plugins/apollo-error-handler.js'
},
toast: {
position: 'top-right',
duration: 5000,
action: {
text: 'X',
onClick : (e, toastObject) => {
toastObject.goAway(0);
},
class: 'notification'
},
containerClass: 'theme-light',
className: 'notification'
},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend (config, ctx) {
}
}
}
@rospirski I think your errors are unrelated to the error handler.
I made a sandbox showing the error handler not working: https://codesandbox.io/s/apollo-broken-error-handler-499o7
I did a minimal here If I access the link '/ test1' it works normally because there is no error Now if I access '/ teste2 /' as it generates an ApolloError in the API, this error simply appears on the console, I can treat it as you ordered, but even so it still generates the error.
Remembering that it is necessary to access the page and give an F5, the rendering needs to be on the Server, not just on the client side.
And as you can be the logs are always duplicated.
Github with the project I used. https://github.com/rospirski/Apoll-Nuxt-Problem
As a solution I am using nuxt's asyncData ... but no solution yet?
@drewbaker @rospirski So, I've mistaken previously. You don't have to mention apollo-error-handler in plugins in nuxt config, it's enough to write it in apollo config.
But I noticed that apollo-error-handler triggers only on client side... And it triggers only when using apollo smart query.
Which is quite weird and not sure, that this is correct. There is one positive thing about it, that you can immediately show some error notification to the user etc. But I still think it should be triggering on server.
@drewbaker @rospirski So, I've mistaken previously. You don't have to mention apollo-error-handler in plugins in nuxt config, it's enough to write it in apollo config.
But I noticed that apollo-error-handler triggers only on client side... And it triggers only when using apollo smart query.
Which is quite weird and not sure, that this is correct. There is one positive thing about it, that you can immediately show some error notification to the user etc. But I still think it should be triggering on server.
So, if I use nuxt's asyncData, I can use the context error, resize the page for the error layout. An alternative would be to use both. SmartQuery and AsyndData, however it would be two requests.
I'll look somewhere to avoid showing the error
a if(process.client)
@Akryum help pliz 👍
@rospirski thnaks! I can get the error handler to be used like you have it, but only in smart queries, not in mutations using this.$apollo.mutate()
, then it will use the generic error handler (which makes it impossible to do things like "${error.details.field} input not provided" messages.
Is there no solution for catching 400 errors from Apollo? Even something as simple as an email validation on a mutation only throws a global error. As @drewbaker mentioned, the ability to read the body of error messages on a 400 would be ideal.
@drewbaker Hello, you can handle a token refresh call this way:
// nuxt.config.ts
'apollo': {
'clientConfigs': {
'default': '~/apollo/client-configs/default.ts',
},
},
// apollo/client-configs/default.ts
async function fetchNewAccessToken (ctx: Context): Promise<string | undefined> {
await ctx.store.dispatch('auth/fetchAuthToken');
return ctx.store.state.auth.authToken;
}
function errorHandlerLink (ctx: Context): any {
return ApolloErrorHandler({
isUnauthenticatedError (graphQLError: GraphQLError): boolean {
const { extensions } = graphQLError;
return extensions?.exception?.message === 'Unauthorized';
},
'fetchNewAccessToken': fetchNewAccessToken.bind(undefined, ctx),
'authorizationHeaderKey': 'X-MyService-Auth',
});
}
export default function DefaultConfig (ctx: Context): unknown {
return {
'link': ApolloLink.from([errorHandlerLink(ctx)]),
'httpEndpoint': ctx.env.GRAPHQL_URL,
};
}
// apollo/error-handler.ts
export default function ApolloErrorHandler ({
isUnauthenticatedError,
fetchNewAccessToken,
authorizationHeaderKey,
} : Options): any {
return onError(({
graphQLErrors,
networkError,
forward,
operation,
}) => {
if (graphQLErrors) {
for (const error of graphQLErrors) {
if (isUnauthenticatedError(error)) {
return new Observable(observer => {
fetchNewAccessToken()
.then(newAccessToken => {
if (!newAccessToken) {
throw new Error('Unable to fetch new access token');
}
operation.setContext(({ headers = {} }: any) => ({
'headers': {
...headers,
[authorizationHeaderKey]: newAccessToken || undefined,
},
}));
})
.then(() => {
const subscriber = {
'next': observer.next.bind(observer),
'error': observer.error.bind(observer),
'complete': observer.complete.bind(observer),
};
forward(operation).subscribe(subscriber);
})
.catch(fetchError => {
observer.error(fetchError);
});
});
}
}
} else if (networkError) {
// ...
}
});
}
@Akryum Is there no solution to this problem? How can we manage errors? Especially when we have 401 errors I have to redirect the user to the login page
Yes, same here... Some have found a workaround to be able to intercept the graphql errors?
Yes, same here... Some have found a workaround to be able to intercept the graphql errors?
hi I handled it using apollo-link-error in apollo-config.js source: https://v4.apollo.vuejs.org/guide-composable/error-handling.html#network-errors
Seems good but, how I can do that on nuxt?
Seems good but, how I can do that on nuxt?
Oh I see! using the client config... thanks
Yes, same here... Some have found a workaround to be able to intercept the graphql errors?
hi I handled it using apollo-link-error in apollo-config.js source: https://v4.apollo.vuejs.org/guide-composable/error-handling.html#network-errors
How did you manage to redirect to the login page? It tells me that "router is not defined" when i try to do a router.push
Yes, same here... Some have found a workaround to be able to intercept the graphql errors?
hi I handled it using apollo-link-error in apollo-config.js source: https://v4.apollo.vuejs.org/guide-composable/error-handling.html#network-errors
How did you manage to redirect to the login page? It tells me that "router is not defined" when i try to do a router.push
you most use redirect method
in plugins/apollo-config.js :
export default function ({ redirect }) { redirect('/auth/login') }
Yes, same here... Some have found a workaround to be able to intercept the graphql errors?
hi I handled it using apollo-link-error in apollo-config.js source: https://v4.apollo.vuejs.org/guide-composable/error-handling.html#network-errors
How did you manage to redirect to the login page? It tells me that "router is not defined" when i try to do a router.push
you most use redirect method in plugins/apollo-config.js :
export default function ({ redirect }) { redirect('/auth/login') }
Thanks!!!
@SebasEC96 move your code into the function before return
export default function ({ redirect }) {
const link = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message }) => {
if (`${message}` === 'Unauthenticated.') {
redirect('/login')
// Do Something
localStorage.setItem('logged', false)
}
})
}
if (networkError) {
console.log(`[Network error]: ${networkError}`)
}
})
return {
defaultHttpLink: false,
link: ApolloLink.from([link, createHttpLink({
credentials: 'include',
uri: 'http://localhost:8000/graphql',
fetch: (uri, options) => {
options.headers['X-XSRF-TOKEN'] = Cookies.get('XSRF-TOKEN')
return fetch(uri, options)
}
})]),
cache: new InMemoryCache()
}
}`
FYI for gave up on Apollo and switched to this, works way better with Nuxt in my opinion: https://github.com/Gomah/nuxt-graphql-request
@SebasEC96 move your code into the function before return
export default function ({ redirect }) { const link = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) { graphQLErrors.map(({ message }) => { if (`${message}` === 'Unauthenticated.') { redirect('/login') // Do Something localStorage.setItem('logged', false) } }) } if (networkError) { console.log(`[Network error]: ${networkError}`) } }) return { defaultHttpLink: false, link: ApolloLink.from([link, createHttpLink({ credentials: 'include', uri: 'http://localhost:8000/graphql', fetch: (uri, options) => { options.headers['X-XSRF-TOKEN'] = Cookies.get('XSRF-TOKEN') return fetch(uri, options) } })]), cache: new InMemoryCache() } }`
Yes, it took me a while to realize it, that's why I deleted the message, thanks!
FYI for gave up on Apollo and switched to this, works way better with Nuxt in my opinion: https://github.com/Gomah/nuxt-graphql-request
It depends on your needs, it does not use the cache among other things, but I will save it in case i need it at any time, thanks!
After finding this issue and searching through the source code of this library, vue-apollo
and subscriptions-transport-ws
, I was able to come up with a a way to handle token refreshes (only logging out in my example) and network errors from sockets and requests, on the server and the client. I was very close to taking @drewbaker's advice and switching libraries.
It's not super pretty and does duplicate some code in this library, but it shows how to completely customize the Apollo client. https://gist.github.com/KazW/2b5e4cb8f43566a69d3917ee7f30dbcc
you can set the errorPolicy property in the apollo query to catch errors https://www.apollographql.com/docs/react/data/error-handling/
query: contentQuery, errorPolicy: 'all', variables () { return { Page: 'test' } },
Thanks @KazW I ended up using a version of your code to get my refresh tokens working using Nuxt-Apollo, Nuxt-Auth (dev), Hasura and Auth0.
// apollo-client-config.js (set this as the default client config for apollo in nuxt.config.js)
import { from, split } from 'apollo-link'
import { setContext } from 'apollo-link-context'
import { WebSocketLink } from 'apollo-link-ws'
import { createUploadLink } from 'apollo-upload-client'
import { getMainDefinition } from 'apollo-utilities'
import { SubscriptionClient } from 'subscriptions-transport-ws'
import * as ws from 'ws'
export default (context) => {
let link
const httpLink = createUploadLink({
uri: process.env.APOLLO_ENDPOINT,
})
link = from([httpLink])
const getAuthToken = async () => {
const auth = context.$auth.strategy
if (await auth.token.status().expired()) {
// eslint-disable-next-line no-console
console.log('Token expired, refreshing')
await auth.refreshController.handleRefresh()
}
return await auth.token.get()
}
const authLink = setContext(async (_, { headers }) => {
const Authorization = await getAuthToken()
const authorizationHeader = Authorization ? { Authorization } : {}
return {
headers: {
...headers,
...authorizationHeader,
},
}
})
link = authLink.concat(link)
const wsClient = new SubscriptionClient(
process.env.APOLLO_WSS_ENDPOINT,
{
reconnect: true,
lazy: true,
connectionParams: async () => {
const Authorization = await getAuthToken()
return Authorization
? { Authorization, headers: { Authorization } }
: {}
},
},
process.server ? ws : WebSocket
)
const wsLink = new WebSocketLink(wsClient)
if (process.server) {
link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink,
link
)
} else {
link = from([wsLink])
}
return {
defaultHttpLink: false,
link,
}
}
Seems the error for errorHandler is not resolved
I still think its weird that the error-handler that you can pass to the apollo config isn't fired when doing this.$apollo.query/mutate. Only on smart queries. Makes no sense to me.
How is this still not fixed? It's a breaking bug that makes Apollo completely unusable with Nuxt as you cannot handle an expired token any other way that I've seen.
any plans to fix this issue?
Sorry everyone for late reponse. Currently, I have some changed in my job, that make me not working with coding for a while. I'm not sure why custom errorHandler is not firing.
For now i'm not sure if it vue-apollo
workflow or maybe lodash template
use in nuxt module.
https://github.com/nuxt-community/apollo-module/blob/master/lib/templates/plugin.js#L95-L113
const vueApolloOptions = Object.assign(providerOptions, {
...
errorHandler (error) {
<% if (options.errorHandler) { %>
return require('<%= options.errorHandler %>').default(error, ctx)
<% } else { %>
console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
<% } %>
}
})
const apolloProvider = new VueApollo(vueApolloOptions)
About the refreshToken. I did some research about refreshToken recently, as the latest update of vue-apollo-plugin-cli
we have an option call preAuthLinks
https://github.com/Akryum/vue-cli-plugin-apollo/pull/243
if (preAuthLinks.length) {
link = from(preAuthLinks).concat(authLink)
}
You guy may can try that options, combine with https://github.com/newsiberian/apollo-link-token-refresh maybe?
I'm looking for a solution for this, and if anyone know what is happening, a PR for this is more wellcome :)
It seems the vue-apollo doesn't use anymore the ApolloClient.errorHandler() to handle errors, which is still used on @nuxtjs/apollo... The way I used to fix this problem:
// apollo-config.js
import { onError } from "@apollo/client/link/error";
export default function(context) {
const httpEndpoint = "http://localhost:4000/local/graphql";
const link = onError(({ graphQLErrors }) => {
graphQLErrors.forEach(err => {
// do things
});
});
return {
link,
httpEndpoint,
};
}
// nuxt.config.js
{
apollo: {
clientConfigs: {
default: "~/plugins/apollo-config.js"
},
},
};
@mellson thanks for this. I'm also using Hasura! I've found one improvement to your code that was a bug for me. When you have:
const wsLink = new WebSocketLink(wsClient)
if (process.server) {
link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink,
link
)
} else {
link = from([wsLink])
}
I've found that I have some requests where I set custom Hasura headers (x-hasura-whatever
) for my auth rules. So I've updated that part to:
if (process.server) {
link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink,
link
)
} else {
link = split(
({ query, getContext }) => {
const { kind, operation } = getMainDefinition(query)
const { headers } = getContext()
const hasHasuraHeaders = Object.keys(headers).some(header =>
header.toLowerCase().includes('x-hasura')
)
return !hasHasuraHeaders
},
wsLink,
link
)
}
It's basically if on the client side and we have custom hasura headers, send a http (where custom headers are pushed through) and not a websocket.
Hope this helps!
Oh one more amendment! This is to ensure the Auth header isn't sent through after the user has explicitly logged out:
const authLink = setContext(async (_, { headers }) => {
const Authorization = await getAuthToken()
const authorizationHeader = Authorization ? { Authorization } : {}
// delete the existing Authorization header if they have logged out
if (!context.$auth.loggedIn) delete headers.Authorization
return {
headers: {
...headers,
// overwrite the Authorization header with the new one
...authorizationHeader,
},
}
})
@toddheslin nice, thanks 🙏🏻
It seems, let alone refreshing the token, this module is completely broken if you can't at least call onLogout() when it's expired....
@KazW I'm not sure how you got this to work as error handlers are not supposed to return a promise, and it basically throws on retriedResult.subscribe
if they do because of this in node_modules/@apollo/client/link/error/error.cjs.js
:
retriedResult = errorHandler({
graphQLErrors: result.errors,
response: result,
operation: operation,
forward: forward,
});
if (retriedResult) {
retriedSub = retriedResult.subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
});
return;
}
I'm wondering: is there a specific reason why no one tried to fix this for everyone by submitting a PR since march ?
@kieusonlam @KazW @toddheslin @mellson please, you all seem to have spent a while on this: any chance you could contribute to fix this once and for all ?
I'm not sure I understand the whole issue, but FYI the only working workaround on my end is this in a dedicated apollo-config.js
using cookie-universal and dotenv-module to load BASE_URL on client-side (I know it's ugly as hell, but it works until I find a better option):
export default function ({ redirect, app, env }) {
const httpEndpoint = `${env.BASE_URL}/graphql`
const link = onError(({ graphQLErrors, networkError, operation, forward }) => {
console.log(graphQLErrors)
if (graphQLErrors && graphQLErrors[0].message.includes('UNAUTHORIZED')) {
app.$cookies.remove('apollo-token')
redirect('/')
}
return forward(operation)
})
return {
link,
httpEndpoint,
}
}
Apollo is on v3 and whatever is used for the config is using the old v2. vue-apollo
is also on v4 now and this apollo-module uses vue-apollo@3
.. can we please get this sorted out??? The issue has been live since March (8 months). I would like to handle custom apollo errors in my nuxt app please
I feel your pain @dylanmcgowan
I noticed that my solution above has another flaw: the subscription connectionParams()
function is only being called when the subscription is initialized but not after login. So it's not reconnecting after I login with the new credentials. The core plugin only handles refreshing the connection if you pass in the wsURL
:
https://github.com/nuxt-community/apollo-module/blob/v4.0.1-rc.5/lib/templates/plugin.js#L138
Here is my current setup for anyone who needs the fix immediately. I'll look at a PR that might allow a more flexible creation.
plugins/apollo/config.js
import { from, split } from 'apollo-link'
import { setContext } from 'apollo-link-context'
import { WebSocketLink } from 'apollo-link-ws'
import { createUploadLink } from 'apollo-upload-client'
import { getMainDefinition } from 'apollo-utilities'
import { SubscriptionClient } from 'subscriptions-transport-ws'
import { SentryLink } from 'apollo-link-sentry'
import * as ws from 'ws'
/**
* https://github.com/nuxt-community/apollo-module
* https://github.com/nuxt-community/apollo-module/issues/315#issuecomment-711156190
*/
// eslint-disable-next-line import/no-mutable-exports
export let wsClient
export default context => {
// See options: https://www.npmjs.com/package/apollo-link-sentry
const sentryLink = new SentryLink()
const WS_URL = context.$config.BASE_URL.replace('http', 'ws')
let link
const httpLink = createUploadLink({
uri: `${context.$config.BASE_URL}/gql`,
})
link = from([sentryLink, httpLink])
const getAuthToken = async () => {
const auth = context.$auth.strategy
if (await auth.token.status().expired()) {
// eslint-disable-next-line no-console
context.$sentry.addBreadcrumb({
category: 'auth',
message: 'Token expired, refreshing',
level: context.Sentry.Severity.Info,
})
await auth.refreshController.handleRefresh()
}
return auth.token.get()
}
const authLink = setContext(async (_, { headers }) => {
const Authorization = await getAuthToken()
const authorizationHeader = Authorization ? { Authorization } : {}
// delete the existing Authorization header if they have logged out
if (!context.$auth.loggedIn) delete headers.Authorization
return {
headers: {
...headers,
// overwrite the Authorization header with the new one
...authorizationHeader,
},
}
})
link = authLink.concat(link)
wsClient = new SubscriptionClient(
`${WS_URL}/gql`,
{
reconnect: true,
lazy: true,
connectionParams: async () => {
const Authorization = await getAuthToken()
return Authorization
? { Authorization, headers: { Authorization } }
: {}
},
},
process.server ? ws : WebSocket
)
const wsLink = new WebSocketLink(wsClient)
if (process.server) {
link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink,
link
)
} else {
link = split(
({ getContext }) => {
const { headers } = getContext()
const hasHasuraHeaders = Object.keys(headers).some(header =>
header.toLowerCase().includes('x-hasura')
)
return !hasHasuraHeaders
},
wsLink,
link
)
}
return {
defaultHttpLink: false,
wsClient,
link,
}
}
plugins/apollo/plugin.js
import {
provide,
onGlobalSetup,
defineNuxtPlugin,
} from '@nuxtjs/composition-api'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
import { wsClient } from './config'
/**
* This plugin will connect @nuxt/apollojs with @vue/apollo-composable
*/
export default defineNuxtPlugin(({ app, $apolloHelpers }) => {
const defaultOnLogin = $apolloHelpers.onLogin
onGlobalSetup(() => {
provide(DefaultApolloClient, app.apolloProvider.defaultClient)
$apolloHelpers.onLogin = function modifiedOnLogin() {
defaultOnLogin()
restartWebsockets(wsClient)
}
})
})
nuxt.config.js
export default {
plugins: ['~/plugins/apollo/plugin.js'],
apollo: {
clientConfigs: {
default: '~/plugins/apollo/config.js',
},
cookieAttributes: {
httpOnly: false,
sameSite: 'Strict',
secure: true,
},
},
}
All, I'm not sure about the redirect issue, as my login redirects are automatically handled with @nuxtjs/auth-next. However, I was able to get the output of the 400 errors using the following:
In nuxt.config.apollo.js
:
import { onError } from '@apollo/client/link/error'
export default ({ $config }) => {
const link = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) => {
console.error(`[GraphQL error]: Message: ${message}`);
console.error(`[GraphQL error]: Locations: ${JSON.stringify(locations)}`);
console.error(`[GraphQL error]: Path: ${path}`);
});
}
if (networkError) console.error(`[Network error]: ${networkError}`)
})
return {
link,
httpEndpoint: 'blah'
}
}
And then setting up in nuxt.config.js
:
apollo: {
clientConfigs: {
default: '~/nuxt.config.apollo.js'
}
},
I know someone had already mentioned this earlier in this giant thread but hopefully for the few brave souls venturing this far, I can confirm this solution at least allows visibility on Apollo errors like 400.
have problem with reading docs, it says to put
errorHandler
inside of apolloConfig but then it says that you can't edit new config anymore. Adding it in apollo config didn't call the error, only adding it to plugins array did the trick.
could anyone call error
function from ctx
in the error handler? error handler seems to work but error
function cannot be called. i need to change error status code in circumstances. any help? thanks.
~~/nuxt.config.ts
:
apollo: {
clientConfigs: {
default: '~~/apollo/default.ts',
},
errorHandler: '~~/apollo/error.ts',
},
~~/apollo/error.ts
:
import type { Context } from '@nuxt/types'
import type { ErrorResponse } from 'apollo-link-error'
import consola from 'consola'
const logger = consola.withTag('apollo')
const errorHandler: (resp: ErrorResponse, ctx: Context) => void = (
{ graphQLErrors, networkError },
{ error },
) => {
if (process.server) {
if (graphQLErrors) {
graphQLErrors.forEach((err) => {
// Works.
logger.error(err)
})
const message = graphQLErrors
.map((err) => `GraphQL error: ${err.message} @ ${err.path.join('/')}`)
.join(', ')
// This doesn't works.
error({
statusCode: 400,
message,
})
}
if (networkError) {
logger.error(networkError)
// This doesn't works.
error({
statusCode: 500,
message: `${networkError.name}: ${networkError.message}`,
})
}
}
}
export default errorHandler
just found errorPolicy: 'all'
could avoid the issue above. this makes me allow to call error()
and nuxt shows the error page. although it causes hydration error which doesn't show the error page properly in production build... 😭
it causes hydration error which doesn't show the error page properly in production build... 😭
For me it shows an unresponsive empty screen with the navbar. Dev environment works fine. Is that the same as for you? I noticed that setting prefetch: false
will fix the error component not being shown, but my pages need to prefetch in order to get the page's head ready for services that use the open graph protocol.
Edit: the unresponsive page is caused by the issue below
I actually get an error message in the console when it should show the error component:
b5562e0.js:2 DOMException: Failed to execute 'appendChild' on 'Node': This node type does not support this method.
at Object.appendChild (http://localhost:4000/_nuxt/b5562e0.js:2:41273)
at y (http://localhost:4000/_nuxt/b5562e0.js:2:54196)
at http://localhost:4000/_nuxt/b5562e0.js:2:53453
at h (http://localhost:4000/_nuxt/b5562e0.js:2:53685)
at _ (http://localhost:4000/_nuxt/b5562e0.js:2:54282)
at D (http://localhost:4000/_nuxt/b5562e0.js:2:57668)
at l.__patch__ (http://localhost:4000/_nuxt/b5562e0.js:2:58080)
at l.t._update (http://localhost:4000/_nuxt/b5562e0.js:2:35019)
at l.r (http://localhost:4000/_nuxt/b5562e0.js:2:65483)
at bn.get (http://localhost:4000/_nuxt/b5562e0.js:2:27195)
Edit: I tried to track down the issue by adding breakpoints. For some reason this error is shown when this component get appended. If I comment the opening and closing tag the error isn't shown and I'm still able to navigate the website.
Version
v4.0.0-rc.19
Reproduction link
https://jsfiddle.net/
Steps to reproduce
Add option to nuxt.config.js -> errorHandler: '~/plugins/apollo-error-handler.js', Create file and print error.
What is expected ?
It should print errors on the console
What is actually happening?
It doesn't print anything when an error happens.
Additional comments?
I'm trying to catch errors when the connection to the server is lost and there's a subscription ongoing. But I can't even catch and log when the server isn't connected and I try to run a query. It's like if the file in "errorHandler" option is ignored.