Skip to content

Commit bca6596

Browse files
committed
feat: enable type hint for apollo client ids
1 parent fa2019f commit bca6596

File tree

7 files changed

+66
-49
lines changed

7 files changed

+66
-49
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,5 @@
5353
"resolutions": {
5454
"@nuxtjs/apollo": "link:."
5555
},
56-
"packageManager": "pnpm@8.10.0"
56+
"packageManager": "pnpm@8.15.2"
5757
}

playground/nuxt.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default defineNuxtConfig({
1818
wsEndpoint: 'wss://nuxt-gql-server-2gl6xp7kua-ue.a.run.app/query',
1919
httpLinkOptions: {
2020
headers: {
21-
'X-CUSTOM-HEADER': 123
21+
'X-CUSTOM-HEADER': '123'
2222
}
2323
}
2424
}

pnpm-lock.yaml

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/module.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Ref } from 'vue'
44
import { defu } from 'defu'
55
import { useLogger, addPlugin, addImports, addTemplate, createResolver, defineNuxtModule } from '@nuxt/kit'
66
import GraphQLPlugin from '@rollup/plugin-graphql'
7+
import type { PluginOption } from 'vite'
78
import { name, version } from '../package.json'
89
import type { ClientConfig, NuxtApolloConfig, ErrorResponse } from './types'
910
import { serializeConfig } from './serialize'
@@ -18,7 +19,7 @@ async function readConfigFile (path: string): Promise<ClientConfig> {
1819

1920
export type ModuleOptions = NuxtApolloConfig
2021

21-
export default defineNuxtModule<NuxtApolloConfig<any>>({
22+
export default defineNuxtModule<ModuleOptions>({
2223
meta: {
2324
name,
2425
version,
@@ -95,18 +96,22 @@ export default defineNuxtModule<NuxtApolloConfig<any>>({
9596
filename: 'apollo.d.ts',
9697
getContents: () => [
9798
'import type { ClientConfig } from "@nuxtjs/apollo"',
98-
'declare const clients: Record<string, ClientConfig>',
99-
'declare const clientAwareness: boolean',
100-
'declare const proxyCookies: boolean',
101-
'declare const cookieAttributes: ClientConfig[\'cookieAttributes\']',
102-
'export default { clients, clientAwareness, proxyCookies, cookieAttributes }'
99+
'declare module \'#apollo\' {',
100+
` export type ApolloClientKeys = '${Object.keys(clients).join('\' | \'')}'`,
101+
' export const NuxtApollo: {',
102+
' clients: Record<ApolloClientKeys, ClientConfig>',
103+
' clientAwareness: boolean',
104+
' proxyCookies: boolean',
105+
' cookieAttributes: ClientConfig[\'cookieAttributes\']',
106+
' }',
107+
'}'
103108
].join('\n')
104109
})
105110

106111
addTemplate({
107112
filename: 'apollo.mjs',
108113
getContents: () => [
109-
'export default {',
114+
'export const NuxtApollo = {',
110115
` proxyCookies: ${options.proxyCookies},`,
111116
` clientAwareness: ${options.clientAwareness},`,
112117
` cookieAttributes: ${serializeConfig(options.cookieAttributes)},`,
@@ -154,7 +159,7 @@ export default defineNuxtModule<NuxtApolloConfig<any>>({
154159
config.optimizeDeps.exclude.push('@vue/apollo-composable')
155160

156161
config.plugins = config.plugins || []
157-
config.plugins.push(GraphQLPlugin())
162+
config.plugins.push(GraphQLPlugin() as PluginOption)
158163

159164
if (!nuxt.options.dev) { config.define = { ...config.define, __DEV__: false } }
160165
})
@@ -191,7 +196,7 @@ export default defineNuxtModule<NuxtApolloConfig<any>>({
191196

192197
export const defineApolloClient = (config: ClientConfig) => config
193198

194-
export interface RuntimeModuleHooks {
199+
export interface ModuleRuntimeHooks {
195200
'apollo:auth': (params: { client: string, token: Ref<string | null> }) => void
196201
'apollo:error': (error: ErrorResponse) => void
197202
}
@@ -205,7 +210,7 @@ export interface ModulePublicRuntimeConfig {
205210
}
206211

207212
declare module '#app' {
208-
interface RuntimeNuxtHooks extends RuntimeModuleHooks {}
213+
interface RuntimeNuxtHooks extends ModuleRuntimeHooks {}
209214
}
210215

211216
declare module '@nuxt/schema' {

src/runtime/composables.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { hash } from 'ohash'
22
import { print } from 'graphql'
3-
import type { OperationVariables, QueryOptions, DefaultContext } from '@apollo/client'
3+
import type { ApolloClient, OperationVariables, QueryOptions, DefaultContext } from '@apollo/client'
44
import type { AsyncData, AsyncDataOptions, NuxtError } from 'nuxt/app'
5-
import type { NuxtAppApollo } from '../types'
5+
import type { RestartableClient } from './ws'
66
import { ref, isRef, reactive, useCookie, useNuxtApp, useAsyncData } from '#imports'
7+
import { NuxtApollo } from '#apollo'
8+
import type { ApolloClientKeys } from '#apollo'
79

810
type PickFrom<T, K extends Array<string>> = T extends Array<any> ? T : T extends Record<string, any> ? keyof T extends K[number] ? T : K[number] extends never ? T : Pick<T, K[number]> : T
911
type KeysOf<T> = Array<T extends T ? keyof T extends string ? keyof T : never : never>
@@ -14,7 +16,7 @@ type TAsyncQuery<T> = {
1416
key?: string
1517
query: TQuery<T>
1618
variables?: TVariables<T>
17-
clientId?: string
19+
clientId?: ApolloClientKeys
1820
context?: DefaultContext
1921
cache?: boolean
2022
}
@@ -33,7 +35,7 @@ export function useAsyncQuery <
3335
PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
3436
DefaultT = null,
3537
NuxtErrorDataT = unknown
36-
> (query: TQuery<T>, variables?: TVariables<T>, clientId?: string, context?: DefaultContext, options?: AsyncDataOptions<T, DataT, PickKeys, DefaultT>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | null>
38+
> (query: TQuery<T>, variables?: TVariables<T>, clientId?: ApolloClientKeys, context?: DefaultContext, options?: AsyncDataOptions<T, DataT, PickKeys, DefaultT>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | null>
3739

3840
export function useAsyncQuery <T> (...args: any[]) {
3941
const { key, fn, options } = prep<T>(...args)
@@ -68,7 +70,7 @@ const prep = <T> (...args: any[]) => {
6870
let variables: TVariables<T>
6971

7072
let cache: boolean = true
71-
let clientId: string | undefined
73+
let clientId: ApolloClientKeys | undefined
7274
let context: DefaultContext
7375

7476
let options: AsyncDataOptions<T, T, KeysOf<T>, null> = {}
@@ -80,7 +82,7 @@ const prep = <T> (...args: any[]) => {
8082
cache = args?.[0]?.cache ?? true
8183
context = args?.[0]?.context
8284
clientId = args?.[0]?.clientId
83-
85+
8486
if (typeof args?.[1] === 'object') {
8587
options = args?.[1]
8688
}
@@ -99,7 +101,7 @@ const prep = <T> (...args: any[]) => {
99101
if (!query) { throw new Error('@nuxtjs/apollo: no query provided') }
100102

101103
if (!clientId || !clients?.[clientId]) {
102-
clientId = clients?.default ? 'default' : Object.keys(clients!)?.[0]
104+
clientId = (clients?.default ? 'default' : Object.keys(clients!)?.[0]) as ApolloClientKeys
103105

104106
if (!clientId) { throw new Error('@nuxtjs/apollo: no client found') }
105107
}
@@ -108,7 +110,7 @@ const prep = <T> (...args: any[]) => {
108110
variables = isRef(variables) ? variables : reactive(variables)
109111

110112
options.watch = options.watch || []
111-
options.watch.push(variables)
113+
options.watch.push(variables)
112114
}
113115

114116
const key = args?.[0]?.key || hash({ query: print(query), variables, clientId })
@@ -123,10 +125,20 @@ const prep = <T> (...args: any[]) => {
123125
return { key, query, clientId, variables, fn, options }
124126
}
125127

126-
export const useApollo = () => {
127-
const nuxtApp = useNuxtApp() as NuxtAppApollo
128+
export function useApollo (): {
129+
clients: Record<ApolloClientKeys, ApolloClient<any>> | undefined
130+
getToken: (client?: ApolloClientKeys) => Promise<string | null | undefined>
131+
onLogin: (token?: string, client?: ApolloClientKeys, skipResetStore?: boolean) => Promise<void>
132+
onLogout: (client?: ApolloClientKeys, skipResetStore?: boolean) => Promise<void>
133+
}
134+
135+
export function useApollo () {
136+
const nuxtApp = useNuxtApp() as {
137+
_apolloClients?: Record<ApolloClientKeys, ApolloClient<any>>;
138+
_apolloWsClients?: Record<ApolloClientKeys, RestartableClient>;
139+
}
128140

129-
const getToken = async (client?: string) => {
141+
const getToken = async (client?: ApolloClientKeys) => {
130142
client = client || 'default'
131143

132144
const conf = NuxtApollo?.clients?.[client]
@@ -140,7 +152,7 @@ export const useApollo = () => {
140152

141153
return conf?.tokenStorage === 'cookie' ? useCookie(tokenName).value : (process.client && localStorage.getItem(tokenName)) || null
142154
}
143-
type TAuthUpdate = {token?: string, client?: string, mode: 'login' | 'logout', skipResetStore?: boolean}
155+
type TAuthUpdate = {token?: string, client?: ApolloClientKeys, mode: 'login' | 'logout', skipResetStore?: boolean}
144156
const updateAuth = async ({ token, client, mode, skipResetStore }: TAuthUpdate) => {
145157
client = client || 'default'
146158

@@ -169,6 +181,7 @@ export const useApollo = () => {
169181

170182
if (skipResetStore) { return }
171183

184+
// eslint-disable-next-line no-console
172185
await nuxtApp?._apolloClients?.[client].resetStore().catch(e => console.log('%cError on cache reset', 'color: orange;', e.message))
173186
}
174187

@@ -192,14 +205,14 @@ export const useApollo = () => {
192205
* @param {string} client - Name of the Apollo client. Defaults to `default`.
193206
* @param {boolean} skipResetStore - If `true`, the cache will not be reset.
194207
* */
195-
onLogin: (token?: string, client?: string, skipResetStore?: boolean) => updateAuth({ token, client, skipResetStore, mode: 'login' }),
208+
onLogin: (token?: string, client?: ApolloClientKeys, skipResetStore?: boolean) => updateAuth({ token, client, skipResetStore, mode: 'login' }),
196209

197210
/**
198211
* Remove the auth token from the Apollo client, and optionally reset it's cache.
199212
*
200213
* @param {string} client - Name of the Apollo client. Defaults to `default`.
201214
* @param {boolean} skipResetStore - If `true`, the cache will not be reset.
202215
* */
203-
onLogout: (client?: string, skipResetStore?: boolean) => updateAuth({ client, skipResetStore, mode: 'logout' })
216+
onLogout: (client?: ApolloClientKeys, skipResetStore?: boolean) => updateAuth({ client, skipResetStore, mode: 'logout' })
204217
}
205218
}

src/runtime/plugin.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,28 @@ import createRestartableClient from './ws'
1010
import { useApollo } from './composables'
1111
import { ref, useCookie, defineNuxtPlugin, useRequestHeaders } from '#imports'
1212

13-
import NuxtApollo from '#apollo'
13+
import { NuxtApollo } from '#apollo'
14+
import type { ApolloClientKeys } from '#apollo'
1415

1516
export default defineNuxtPlugin((nuxtApp) => {
1617
const requestCookies = (process.server && NuxtApollo.proxyCookies && useRequestHeaders(['cookie'])) || undefined
1718

18-
const clients: { [key: string]: ApolloClient<any> } = {}
19+
const clients = {} as Record<ApolloClientKeys, ApolloClient<any>>
1920

2021
for (const [key, clientConfig] of Object.entries(NuxtApollo.clients)) {
2122
const getAuth = async () => {
22-
const token = ref<string | null>()
23+
const token = ref<string | null>(null)
2324

2425
await nuxtApp.callHook('apollo:auth', { token, client: key })
2526

2627
if (!token.value) {
2728
if (clientConfig.tokenStorage === 'cookie') {
2829
if (process.client) {
29-
token.value = useCookie(clientConfig.tokenName!).value
30+
const t = useCookie(clientConfig.tokenName!).value
31+
if (t) { token.value = t }
3032
} else if (requestCookies?.cookie) {
31-
token.value = requestCookies.cookie.split(';').find(c => c.trim().startsWith(`${clientConfig.tokenName}=`))?.split('=')?.[1]
33+
const t = requestCookies.cookie.split(';').find(c => c.trim().startsWith(`${clientConfig.tokenName}=`))?.split('=')?.[1]
34+
if (t) { token.value = t }
3235
}
3336
} else if (process.client && clientConfig.tokenStorage === 'localStorage') {
3437
token.value = localStorage.getItem(clientConfig.tokenName!)
@@ -82,6 +85,8 @@ export default defineNuxtPlugin((nuxtApp) => {
8285
wsLink = new GraphQLWsLink(wsClient)
8386

8487
nuxtApp._apolloWsClients = nuxtApp._apolloWsClients || {}
88+
89+
// @ts-ignore
8590
nuxtApp._apolloWsClients[key] = wsClient
8691
}
8792

@@ -109,7 +114,7 @@ export default defineNuxtPlugin((nuxtApp) => {
109114

110115
const cache = new InMemoryCache(clientConfig.inMemoryCacheOptions)
111116

112-
clients[key] = new ApolloClient({
117+
clients[key as ApolloClientKeys] = new ApolloClient({
113118
link,
114119
cache,
115120
...(NuxtApollo.clientAwareness && { name: key }),
@@ -121,7 +126,7 @@ export default defineNuxtPlugin((nuxtApp) => {
121126
})
122127

123128
if (!clients?.default && !NuxtApollo?.clients?.default && key === Object.keys(NuxtApollo.clients)[0]) {
124-
clients.default = clients[key]
129+
clients.default = clients[key as ApolloClientKeys]
125130
}
126131

127132
const cacheKey = `_apollo:${key}`

src/types.d.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
import type { ClientOptions } from 'graphql-ws'
2-
import type { ApolloClient, HttpOptions, DefaultOptions, InMemoryCacheConfig } from '@apollo/client'
3-
import type { CookieOptions } from 'nuxt/dist/app/composables'
4-
import type { RestartableClient } from './runtime/ws'
2+
import type { HttpOptions, DefaultOptions, InMemoryCacheConfig } from '@apollo/client'
3+
import type { CookieOptions } from 'nuxt/app'
54
export type { ErrorResponse } from '@apollo/client/link/error'
65

76
type CookieAttributes = Omit< CookieOptions, 'encode' | 'decode' | 'expires' | 'default'>;
87

9-
export type NuxtAppApollo = Partial<{
10-
_apolloClients?: Record<string, ApolloClient<any>>;
11-
_apolloWsClients?: Record<string, RestartableClient>;
12-
}>;
13-
148
export type ClientConfig = {
159
/**
1610
* The GraphQL endpoint.
@@ -108,7 +102,7 @@ export type ClientConfig = {
108102
cookieAttributes?: CookieAttributes;
109103
};
110104

111-
export interface NuxtApolloConfig<T = ClientConfig> {
105+
export interface NuxtApolloConfig<T = false> {
112106
/**
113107
* Determine if vue-apollo composables should be automatically imported.
114108
* @type {boolean}
@@ -119,7 +113,7 @@ export interface NuxtApolloConfig<T = ClientConfig> {
119113
/**
120114
* Configuration of the Apollo clients.
121115
**/
122-
clients?: Record< string, T extends boolean ? string | ClientConfig : ClientConfig >;
116+
clients?: Record< string, T extends false ? string | ClientConfig : ClientConfig >;
123117

124118
/**
125119
* Default options to be applied to all Apollo clients.

0 commit comments

Comments
 (0)