μ΄μ λΌλ²¨
μλ¬Έ
λ΄κ° λ¬μ±νλ €λ μ νν μλ리μ€λ λ€μκ³Ό κ°μ΅λλ€.
https://github.com/apollographql/apollo-link/tree/master/packages/apollo-link-error#retrying -failed-requests
operation.setContext({
headers: {
...oldHeaders,
authorization: getNewToken(),
},
});
κ·Έλ¬λ ν ν°μ΄ λ§λ£λλ©΄ getNewToken
κ° μ ν¨ν μΈμ¦ ν ν°μ λ°νν μ μκΈ° μ μ λΉλκΈ° resfreshToken
λ₯Ό νΈμΆνκ³ "λκΈ°"ν΄μΌ ν©λλ€. μ μκ°μλ.
λ΄ μ§λ¬Έμ λΉλκΈ° resfreshToken
νΈμΆμ μννλ λ°©λ²μ
λλ€. await refreshToken()
(Promiseκ° μλ£λλ©΄ ν΄κ²°)λ₯Ό μλνμ§λ§ κΈ°λ‘λ μ€ν μΆμ μμ μ΄κ²μ΄ RxJSλ₯Ό μλΉν μλ§μΌλ‘ λ§λλ κ² κ°μ΅λλ€. μ λ RxJS n00bμ
λλ€. λμμ μ£Όμλ©΄ κ°μ¬νκ² μ΅λλ€!
Promiseμ λ μ΅μνλ€λ©΄ fromPromise
λμ°λ―Έλ₯Ό μ¬μ©ν μ μμ΅λλ€.
import { fromPromise } from 'apollo-link';
return fromPromise(refreshToken().then(token => {
operation.setContext({
headers: {
...oldHeaders,
authorization: token,
},
});
return forward(operation);
}))
@thymikee μ루μ μ μλνμ§λ§ λ€μ λ©μμ§μ ν¨κ» μ€ν¨ν©λλ€.
Uncaught (in promise) Error: Network error: Error writing result to store for query:
query UserProfile($id: ID!) {
UserProfile(id: $id) {
id
email
first_name
last_name
activated
created_at
updated_at
last_active
roles {
id
name
__typename
}
permissions {
name
value
__typename
}
profile {
address
secondary_email
phone {
id
number
type {
id
name
__typename
}
__typename
}
__typename
}
__typename
}
}
Cannot read property 'UserProfile' of undefined
at new ApolloError (ApolloError.js:43)
at QueryManager.js:327
at QueryManager.js:759
at Array.forEach (<anonymous>)
at QueryManager.js:758
at Map.forEach (<anonymous>)
at QueryManager.webpackJsonp../node_modules/apollo-client/core/QueryManager.js.QueryManager.broadcastQueries (QueryManager.js:751)
at QueryManager.js:254
μΆκ° κ²μ¬λ₯Ό ν΅ν΄ μμ μ½λλ₯Ό μ¬μ©ν λ Apollo λ§ν¬μ onError
κ° λ λ² νΈμΆλ¨μ μ μ μμ΅λλ€. refresh token
μ½μμ ν λ²λ§ μ€ννλλ‘ μ νν΄λ μ€λ₯κ° μμ λμ§ μμ΅λλ€.
λ¬΄μ¨ μΌμ΄ μΌμ΄λλμ§:
1) μ΄κΈ° 쿼리 μ€ν
2) μ€ν¨νκ³ apolloμ λ§ν¬ onError
μΌ) ?? apolloμ λ§ν¬ onError
λ€μ μ€νν©λλ€.
4) onError
μμ ν ν° μλ‘ κ³ μΉ¨μ μ½μνκ³ μ€νμ λλ΄κ³ ν΄κ²°λ©λλ€.
5) (νλΌλ―Έμ€ μ±κ³΅ ν μ΄κΈ° 쿼리λ λ λ² μ€νλμ§ μμ)
6) μ΄κΈ° 쿼리λ μ μλμ§ μμ data
λ₯Ό ν¬ν¨νλ κ²°κ³Όλ₯Ό λ°νν©λλ€.
λκ΅°κ° μ΄ λ¬Έμ μ λν ν΄κ²°μ± μ μ°ΎκΈ°λ₯Ό λ°λΌλ κ²μ λλ€. κ·Έλ μ§ μμΌλ©΄ λ§λ£ μ κ°±μ νμ§ μκ³ μλͺ μ΄ κΈ΄ μ‘μΈμ€ ν ν°μ μ¬μ©νλλ‘ λλλ €μΌ ν©λλ€.
ν ν° κ²μ λ
Όλ¦¬κ° μ ννλ©΄ onError
λ ν λ²λ§ νΈμΆλμ΄μΌ ν©λλ€. ν ν° μΏΌλ¦¬μ λ¬Έμ κ° μλ κ² κ°μ΅λλ€.
@thymikee λλ―Έ μ½μμΌλ‘ λΉλκΈ° μμ²μ μ ννμ΅λλ€. μμ λ©μμ§μ ν¨κ» μ¬μ ν μ€ν¨νκ³ μ΄κΈ° μΏΌλ¦¬κ° λ λ² μ€νλμ§ μμ΅λλ€. λͺ¨λ ν ν°μ ν μ€νΈ μμ μ μ ν¨ν©λλ€.
μνΈ:
return fromPromise(
new Promise((resolve) => {
let headers = {
//readd old headers
...operation.getContext().headers,
//switch out old access token for new one
authorization: `Bearer mynewaccesstoken`,
};
operation.setContext({
headers
});
return resolve(forward(operation));
})
)
νΈμ§: fromPromise
μ κ±°νκ³ μ¬λ°λ₯΄κ² μλν©λλ€. μ΄μ¨λ κ²°κ³Όλ₯Ό λ°ννκΈ° μ μ λ§ν¬ μ€νμ μ²λ¦¬κ° λλμ forward(operation)
κ° μ€νλμ§ μμ΅λλ€.
fromPromise
μ½λμ μ»€λ° #172 λ₯Ό λΆμν ν fromPromise
λ Apollo Link κ°μ²΄μ μΆμΈ‘μΌλ‘λ§ μ¬μ©ν μ μμ΅λλ€.
μ루μ μ μ°κ΅¬νλ©΄μ λ§μΉ¨λ΄ μ΄ νλ‘μ νΈλ₯Ό μ°μ°ν λ°κ²¬νμ΅λλ€: apollo-link-token-refresh
λ΄ ν΄λ‘ λ§ν¬ μ€νμ μ΄μ λ€μκ³Ό κ°μ΅λλ€.
[
refreshTokenLink,
requestLink,
batchHttpLink
]
refreshTokenLink
λ νμ graphql λμ μ λν μλ΅μ μ€ννκΈ° μ μ μ‘μΈμ€ ν ν°μ νμΈνκΈ° μν΄ νΈμΆλλ©° 맀λ ₯μ²λΌ μλν©λλ€.
λΆννλ μ΄κ²μ graphql λμ μ λν νΈμΆμ΄ νμ μΈμ¦λμ΄μΌ νλ€κ³ κ°μ ν©λλ€(μ κ²½μ°μλ μΈμ¦λ¨).
onError
μ½λ°±μ΄ aync
ν¨μ λλ Promise
λ°νμ νμ©νμ§ μλ κ² κ°μ΅λλ€. μ½λ μ°Έμ‘° https://github.com/apollographql/apollo-link/blob/59abe7064004b600c848ee7c7e4a97acf5d230c2/packages/apollo-link-error/src/index.ts#L60 -L74
μ΄ λ¬Έμ λ μ΄μ μ λ³΄κ³ λμμ΅λλ€: #190
apollo-link-retry
κ° μ¬κΈ°μ νλ κ²κ³Ό μ μ¬νκ² apollo-link-error
κ° Promise
μ²λ¦¬ν μ μμΌλ©΄ λ μ μλν κ²μ΄λΌκ³ μκ°ν©λλ€. #436
λμΌν λ¬Έμ κ° μκ³ λ°μ λ€μ΄ν°λΈμ ν¨κ» apolloλ₯Ό μ¬μ©νμ¬ AsyncStorage onErrorμμ μΌλΆ ν ν°μ μ κ±°ν΄μΌ λΉλκΈ° ν¨μμ¬μΌ ν©λλ€.
μ΄ μ루μ μ μ μκ² ν¨κ³Όμ μ΄μμ΅λλ€:
promiseToObservable.js
μ νΈλ¦¬ν°λ₯Ό λ§λ€μ΄ μ΄ λ¬Έμ λ₯Ό ν΄κ²°νμ΅λλ€.
import { Observable } from 'apollo-link';
export default promise =>
new Observable((subscriber) => {
promise.then(
(value) => {
if (subscriber.closed) return;
subscriber.next(value);
subscriber.complete();
},
err => subscriber.error(err)
);
return subscriber; // this line can removed, as per next comment
});
그리κ³
import { onError } from 'apollo-link-error';
import promiseToObservable from './promiseToObservable';
export default (refreshToken: Function) =>
onError(({
forward,
graphQLErrors,
networkError = {},
operation,
// response,
}) => {
if (networkError.message === 'UNAUTHORIZED') { // or whatever you want to check
// note: await refreshToken, then call its link middleware again!
return promiseToObservable(refreshToken()).flatMap(() => forward(operation));
}
});
@crazy4groovy μλ₯Ό λ€μ΄ μ£Όμ
μ κ°μ¬ν©λλ€. μ λ§ λμμ΄ λ©λλ€. κ·Έλ¬λ, κ·Έκ²μΌλ‘ μμ λ¬Έμ κ°μλ€ : subscriber
μ λ°λΌ μ ν¨ λ¦¬ν΄ κ°μ΄ Observable
typingsκ° : μ€νλ €ν΄μΌ ZenObservable.SubscriptionObserver
:
export declare type Subscriber<T> = ZenObservable.Subscriber<T>;
export declare const Observable: {
new <T>(subscriber: Subscriber<T>): Observable<T>;
};
export declare namespace ZenObservable {
interface SubscriptionObserver<T> {
closed: boolean;
next(value: T): void;
error(errorValue: any): void;
complete(): void;
}
type Subscriber<T> = (observer: SubscriptionObserver<T>) => void | (() => void) | Subscription;
}
μ¦, undefinedλ₯Ό λμ λ°ννλ κ²μ΄ μμ ν©λλ€. νλ‘μ νΈμ README νμΌμ μΈκΈλμ΄μΌ ν κ² κ°μ΅λλ€.
UPD: μ΄μ λν PRμ μΆκ°νμ΅λλ€: https://github.com/apollographql/apollo-link/pull/825
μ΄ λ¬Έμ λ Googleμμ λμ μμμ μμΌλ―λ‘ μΌλΆ μ¬λλ€μ λκΈ° μν΄ μ¬κΈ°μ λ΄ μ루μ μ 곡μ νκ³ μμ΅λλ€. https://gist.github.com/alfonmga/9602085094651c03cd2e270da9b2e3f7
κ·νμ μ루μ μ μλνμ§λ§ μλ‘μ΄ λ¬Έμ μ μ§λ©΄νμ΅λλ€.
Argument of type '(this: Observable<{}>, observer: Subscriber<{}>) => Observable<{}> | Promise<{}>' is not assignable to parameter of type '(this: Observable<{}>, subscriber: Subscriber<{}>) => TeardownLogic'.
Type 'Observable<{}> | Promise<{}>' is not assignable to type 'TeardownLogic'.
Type 'Observable<{}>' is not assignable to type 'TeardownLogic'.
Property 'unsubscribe' is missing in type 'Observable<{}>' but required in type 'Unsubscribable'
μ μΈμ¦ ν ν°μ΄ μλ‘ κ³ μ³μ§λ©΄ μ΄λ»κ² μ μ₯νκ³ κ³μλκΉ?
λ¬Όλ‘ μ¬μλ μμ²μμ μ ν€λλ₯Ό μ€μ ν μ μμ§λ§ μλ μ‘μΈμ€ ν ν°(μΏ ν€μ μ μ₯νκ³ μμ)μ μ λ°μ΄νΈλμ§ μμ΅λλ€. λ€μ μλ‘κ³ μΉ¨ν΄μΌ ν¨).
μ΄λ€ μ΄μ λ‘ μλ‘ κ³ μΉ¨ μ€μ μΏ ν€λ₯Ό μ λ°μ΄νΈνλ €κ³ ν λλ§λ€ λ€μ μ€λ₯ λ©μμ§κ° λνλ©λλ€( μ¬κΈ°μμ μ΄μ λν΄ μ λ¬Έμ λ₯Ό λ§λ€μμ΅λλ€).
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at setCookie (/root/SimplyTidyAdmin/node_modules/nookies/dist/index.js:98:17)
at /root/SimplyTidyAdmin/.next/server/static/CAhshxrRWHVF6Gzbce~pU/pages/_app.js:1273:63
at process._tickCallback (internal/process/next_tick.js:68:7)
@StupidSexyJake https://stackoverflow.com/questions/55356736/change-apollo-client-options-for-jwt-token ν ν°μ μ λ°μ΄νΈνλ λ°©λ²μ λν΄ λΉμ·ν λ¬Έμ κ° λ°μνμ΅λλ€.
μλ νμΈμ, @crazy4groovy κ°μ¬ν©λλ€. κ·νμ μ루μ μ μλνμ§λ§ μ¬μ ν λ¬Έμ κ° μμ΅λλ€. μ ν ν°μ΄ μμ²μΌλ‘ μ€μ λκΈ° μ μ graphql μμ²μ ν ν°μ μΆκ°νλ λ―Έλ€μ¨μ΄κ° νΈμΆλ©λλ€. λ°λΌμ ν€λμλ μ¬μ ν μ ν¨νμ§ μμ ν ν°μ΄ μμ΅λλ€.
μ½κ°μ λ°°κ²½ μ 보: ν ν°μ΄ μ ν¨νμ§ μμ κ²½μ° λ€νΈμν¬ μ€λ₯κ° λ°μνκ³ μλ‘ κ³ μΉ¨ ν ν°μ ν΅ν΄ μ ν ν°μ μ»κ³ λ€μ μλν μ μμ΅λλ€. κ·Έλ¬λ μλ‘ κ³ μΉ¨ ν ν°μ΄ μμ§λμ΄ λ‘컬 μ μ₯μλ‘ μ€μ λκΈ° μ μ λ―Έλ€μ¨μ΄κ° νΈμΆλκΈ° λλ¬Έμ μ¬μ ν μ ν¨νμ§ μμ ν ν°μ΄ μμ΅λλ€. μλ‘ κ³ μΉ¨ ν ν° λ Όλ¦¬λ μ μλν©λλ€. κ·Έλ° λ€μ λ§μ§λ§μ μ ν ν° μΈνΈλ₯Ό κ°μ Έμ€κΈ° λλ¬Έμ λλ€. λ¬Έμ λ₯Ό μ½κ° λλ²κΉ νμΌλ©° νμ΄λ°μ λ€μκ³Ό κ°μ΅λλ€.
onError
μμ promiseToObservable
λ
Όλ¦¬λ₯Ό ν΅ν΄ μ²λ¦¬λκ³ λ€μ μλλ©λλ€.onRefreshToken
Promiseμμ ν ν° κ°μ Έμ€κΈ°κ° μλ£λ λκΉμ§ λ―Έλ€μ¨μ΄λ μ΄λ―Έ μ΄μ ν ν°μΌλ‘ λ λ²μ§Έ μ€ν μ€μ
λλ€.λ€μμ μ΄λ¬ν λΆλΆμ μ€λν«μ λλ€(onRefreshtoken 건λλ°κΈ°. λΉλκΈ° ν¨μλ‘ Promiseλ₯Ό λ°νν¨).
const promiseToObservable = (promise: Promise<any>) =>
new Observable((subscriber: any) => {
promise.then(
value => {
console.log(subscriber);
if (subscriber.closed) return;
subscriber.next(value);
subscriber.complete();
},
err => subscriber.error(err)
);
});
const authMiddleware = setContext((operation: GraphQLRequest) => {
const token = localStorage.getItem('ca_token');
return {
headers: {
...(token && !isSkipHeader(operation)
? { authorization: `Bearer ${token}` }
: {})
}
};
});
const errorLink = onError(
({
networkError,
graphQLErrors,
operation,
forward
}: ErrorResponse): any => {
if (networkError) {
switch (networkError.statusCode) {
case 401:
console.warn('Refreshing token and trying again');
// await refreshToken, then call its link middleware again
return promiseToObservable(onRefreshToken(client.mutate)).flatMap(() => forward(operation));
default:
// Handle all other errors here. Irrelevant here.
}
}
if (graphQLErrors) {
// Handle gql errors, irrelevant here.
}
}
);
μ κ° μ¬κΈ°μ 무μμ λμΉκ³ μλμ§ λ§μν΄ μ£Όμκ² μ΅λκΉ? λ§μ κ°μ¬λ립λλ€...
λ€, νΌλμ λλ € μ£μ‘ν©λλ€.
λλ λ΅μ μ°Ύμκ³ μ¬κΈ°μ κ²μν ν λͺ μκ° λμ μ°Ύκ³ μ°Ύμ ν μ΄λ¦¬μμ λ΅λ³μ΄μμ΅λλ€. apollo ν΄λΌμ΄μΈνΈλ₯Ό μ΄κΈ°ννλ λμ λ―Έλ€μ¨μ΄μ μ€λ₯ λ§ν¬λ₯Ό λ°κΏ¨μ΅λλ€. μ΄μ μλν©λλ€. μ€λ₯ λ§ν¬κ° λ¨Όμ μμ΄μΌν©λλ€.
μ΄μ : link: from([authMiddleware, errorLink, /* others */])
μ κ·: link: from([errorLink, authMiddleware, /* others */])
λ€μνλ² μ£μ‘ν©λλ€..
μλ νμΈμ μ¬λ¬λΆ,
μλ‘ κ³ μΉ¨ ν ν°μ onErrorλ₯Ό μ¬μ©νμ¬ λ€μκ³Ό κ°μ λ¬Έμ κ° μμ΅λλ€. nextjsλ₯Ό μ¬μ©νλ SSRμ λͺ©μ μ μν΄ λͺ¨λ graphql 쿼리μμ λ°μ΄ν°λ₯Ό μμ§νκ³ μμ§λ§ μλ₯Ό λ€μ΄ 2κ°μ μΏΌλ¦¬κ° μκ³ jwt ν ν°μ΄ λ§λ£λμ΄ κ°κ° μ€λ₯κ° λ°μνλ©΄ μ΄λ»κ² λ©λκΉ? κ·Έλ° λ€μ onErrorλ₯Ό λ λ² μ€ννκ³ λΉμ©μ΄ λ§μ΄ λλ μλ‘ κ³ μΉ¨ ν ν°μ λν΄ λ λ² νΈμΆν©λλ€. λ¬Έμ κ° μ΄λμμ μ¬ μ μλμ§ μ μ μμ΅λλ€. λ€μμ λ΄κ° μ¬μ©νλ μ½λμ λλ€. λμμ£Όμκ² μ΄μ?
https://gist.github.com/shaxaaa/15817f1bcc7b479f3c541383d2e83650
μ΄ λ¬Έμ λ‘ μ μ μ¨λ¦νμ§λ§ λ§μΉ¨λ΄ μλνκ² λμμ΅λλ€. ν¨κ» κΎΈλ¬λ―Έλ₯Ό λμ‘μ΅λλ€.
https://github.com/baleeds/apollo-link-refresh-token
μ΄ ν¨ν€μ§μ apollo-link-token-refreshλΌλ ν¨ν€μ§μ μ£Όμ μ°¨μ΄μ μ μ΄ ν¨ν€μ§κ° μλ‘ κ³ μΉ¨μ μλνκΈ° μ μ λ€νΈμν¬ μ€λ₯λ₯Ό λκΈ°νλ€λ κ²μ λλ€.
λ³νμ λν μμ΄λμ΄κ° μμΌλ©΄ μλ €μ£Όμμμ€.
κΈ°λ³Έ μ¬μ©λ²μ λ€μκ³Ό κ°μ΅λλ€.
const refreshTokenLink = getRefreshTokenLink({
authorizationHeaderKey: 'Authorization',
fetchNewAccessToken,
getAccessToken: () => localStorage.getItem('access_token'),
getRefreshToken: () => localStorage.getItem('refresh_token'),
isAccessTokenValid: accessToken => isTokenValid(accessToken),
isUnauthenticatedError: graphQLError => {
const { extensions } = graphQLError;
if (
extensions &&
extensions.code &&
extensions.code === 'UNAUTHENTICATED'
) {
return true;
}
return false;
},
});
promiseToObservable.js
μ νΈλ¦¬ν°λ₯Ό λ§λ€μ΄ μ΄ λ¬Έμ λ₯Ό ν΄κ²°νμ΅λλ€.import { Observable } from 'apollo-link'; export default promise => new Observable((subscriber) => { promise.then( (value) => { if (subscriber.closed) return; subscriber.next(value); subscriber.complete(); }, err => subscriber.error(err) ); return subscriber; // this line can removed, as per next comment });
그리κ³
import { onError } from 'apollo-link-error'; import promiseToObservable from './promiseToObservable'; export default (refreshToken: Function) => onError(({ forward, graphQLErrors, networkError = {}, operation, // response, }) => { if (networkError.message === 'UNAUTHORIZED') { // or whatever you want to check // note: await refreshToken, then call its link middleware again! return promiseToObservable(refreshToken()).flatMap(() => forward(operation)); } });
λλ κ·Έκ²μ μ¬μ©νκ³ μλ‘ κ³ μΉ¨ ν ν° μμ² ν μ¬μ ν μ΄μ ν ν°μ μ¬μ©νλ κ²μ λ°κ²¬νμ΅λλ€. κ·Έλμ λλ λ€μκ³Ό κ°μ΄ μλν©λλ€.
return promiseToObservable(refreshToken()).flatMap((value) => {
operation.setContext(({ headers = {} }) => ({
headers: {
// re-add old headers
// ...headers,
Authorization: `JWT ${value.token}`
}
}));
return forward(operation)
});
μλν©λλ€.
κ·Έλ¬λ ...headers
(μ΄μ ν€λλ₯Ό λ€μ μΆκ°ν¨μ μλ―Έ)λ₯Ό μΆκ°νλ©΄ μ λ¬ μμ²μ΄ μ μ‘λκΈ° μ μ λκ° μλͺ»λ κ²μ΄ μλ€λ λ¬Έμ κ° μμ΅λλ€.
ERROR Error: Network error: Cannot read property 'length' of null
... ν€λμ κΆν λΆμ¬κ° μ κΆν λΆμ¬μ μΆ©λν μ μλ€κ³ μκ°ν©λλ€.
μμ λ¬Έμ λ apollo-angular "apollo-angular-link-http": "^1.6.0",
μ΄κ³ apollo-client "apollo-link-http": "^1.5.16",
μλ λ°λ©΄ link-errorλ "apollo-link-error": "^1.1.12",
λ€λ₯Έ ꡬ문 :λ:
import Vue from 'vue'
import { Observable } from 'apollo-link'
import { onError } from 'apollo-link-error'
const onGraphqlError = async ({ graphQLErrors = [], observer, operation, forward }) => {
// here you could call the refresh query in case you receive an expired error
for (let error of graphQLErrors)
observer.next(forward(operation)) // this line would retry the operation
}
const onNetworkError = async ({ observer, networkError, operation, forward }) => { }
export const errorHandler = opt => new Observable(async observer => {
try {
const payload = { ...opt, observer }
await Promise.all([onGraphqlError(payload), onNetworkError(payload)])
if (observer.closed) return
observer.complete()
} catch (error) {
observer.error(error)
}
})
μλ
! μ 체 μΉ μμΌ μ μ‘μ μ¬μ©νκ³ μμΌλ―λ‘ ν ν° μΏΌλ¦¬λ₯Ό μμ²ν΄μΌ ν©λλ€. μ΄λ»κ² ν μκ°μ΄ μμ΅λλ€.
μλ²κ° accessToken
κ° λ§λ£λμλ€κ³ μλ΅ν λ μμ μμ²μ νκ³ μΆμ΅λλ€.
import { onError } from "apollo-link-error";
import gql from 'graphql-tag'
// Client: VUE APOLLO
const q = {
query: gql`query token { token { accessToken } }`,
manual: true,
result({ data, loading }) {
if (!loading) {
console.log(data)
}
},
}
const link = onError(({ graphQLErrors, networkError, operation, response, forward }) => {
if (networkError) {
switch (networkError.message) {
case 'accessTokenExpired':
console.log('accessTokenExpired')
return forward(q) // NOT WORKS, NEED HELP
case 'unauthorized':
return console.log('unauthorized')
default:
return forward(operation)
}
}
return forward(operation)
})
export default link
@nikitamarcius μμ ν΄κ²° λ°©λ²μ κ²μνμ΅λλ€. κ΄μ°° κ°λ₯ν νλͺ©μ μ΄ν΄λ³΄μΈμ.
ν ν°μ μ λ°μ΄νΈν μ μμ΅λλ€. μμ μμ λ₯Ό μ 곡ν μ¬λμ΄ μμ΅λκΉ?
@ Ramyapriya24 μ¬κΈ°μ λ΄κ° μ¬μ©νλ μ½λκ° μμ΅λλ€.
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/link-context';
import AuthService from 'services/auth-service' // this is my implementation
const asyncAuthLink = setContext(async () => {
// this is an async call, it will be done before each request
const { token } = await AuthService.getCredentials();
return {
headers: {
authorization: token
},
};
},
);
const httpLink = new HttpLink({
uri: 'http://localhost:4000/graphql',
});
export const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
link: asyncAuthLink.concat(httpLink),
});
@adrianolsk μμ±λ μλΉμ€ μ½λλ₯Ό μ 곡ν μ μμ΅λκΉ?
import AuthService from 'services/auth-service' // μ΄κ²μ λ΄ κ΅¬νμ
λλ€.
const { ν ν° } = AuthService.getCredentials()λ₯Ό κΈ°λ€λ¦½λλ€.
μλΉμ€λ₯Ό κ°μ Έμ€λ €κ³ ν λ μ€λ₯κ° λ°μν©λλ€.
κ·Έκ²μ΄ λ΄ μλΉμ€μ λλ€. κ·Έκ²μ react-nativeμμ AsyncStorageλ₯Ό μ½μ΅λλ€. κ·Έλμ λ‘κ·ΈμΈ ν κ±°κΈ°μ κ°μ μ€μ νκ³ κ° μμ² μ μ μ½λκ° μ 보λ₯Ό κ°μ Έμ€κ³ ν€λμ μ€μ νλ©΄ λμΌν μμ μ μννκ±°λ localStorageλ₯Ό μ¬μ©ν μ μμ΅λλ€. μΉμ μμ΅λλ€.
μ¬μ©νλ €λ μ 보λ₯Ό μ΄λμ μ μ₯νκ³ μμ΅λκΉ?
λΉμ μ μ΄κ²μ μ¬μ©ν μ μμ΅λλ€
//save the token after login or when it refreshes
localStorage.setItem('token', yourToken);
κ·Έλ¦¬κ³ κ·Έκ²μ μ¬μ©
const asyncAuthLink = setContext(() => {
// grab token from localStorage
const token = localStorage.getItem('token');
return {
headers: {
authorization: token
},
};
},
);
@adrianolsk μ€λͺ κ°μ¬ν©λλ€λ§ κ°λλ₯Ό μ¬μ©νκ³ μμ΅λλ€. grapqh.module.ts νμΌμμ μλΉμ€λ₯Ό κ°μ Έμ¬ μ μμ΅λλ€. μλΉμ€λ₯Ό μ¬μ©ν λ μ€λ₯κ° λ°μν©λλ€.
ν΄λμ€μ μμ±μλ₯Ό μ¬μ©νμ§ μκ³ module.ts νμΌμμ μλΉμ€λ₯Ό μ¬μ©νλ λ°©λ²μ μλ μ¬λμ΄ μμ΅λκΉ?
κ°μ¬ ν΄μ
λΉλκΈ° μλ‘ κ³ μΉ¨ ν ν°μ fromPromise
λ₯Ό μ¬μ©νλ €κ³ ν©λλ€.
κΈ°λ³Έμ μΌλ‘ μ΄ κ²μλ¬Ό μ μΈ λ²μ§Έ μμλ₯Ό
ν ν°μ μ±κ³΅μ μΌλ‘ κ°μ Έμ€κ³ μ μ₯νκ³ μμ§λ§ catch
λλ filter
λλ flatMap
νΈμΆλμ§ μμ΅λλ€. μ΄κ²μ λλ²κΉ
νλ λ°©λ²μ μ λͺ¨λ₯΄κ² μΌλ―λ‘ λͺ κ°μ§ μ μμ΄ λμμ΄ λ κ²μ
λλ€.
if (token && refreshToken) {
return fromPromise(
getNewToken(client)
.then(({ data: { refreshToken } }) => {
console.log("Promise data: ", refreshToken);
localStorage.setItem("token", refreshToken.token);
localStorage.setItem("refreshToken", refreshToken.refreshToken);
return refreshToken.token;
})
.catch((error) => {
// Handle token refresh errors e.g clear stored tokens, redirect to login, ...
console.log("Error after setting token: ", error);
return;
})
)
.filter((value) => {
console.log("In filter: ", value);
return Boolean(value);
})
.flatMap(() => {
console.log("In flat map");
// retry the request, returning the new observable
return forward(operation);
});
}
@adrianolsk : κ·Έ μ κ·Ό λ°©μμ ν ν°μ΄ λ§λ£λκΈ° μ μλ νμ μλ‘κ³ μΉ¨νλ κ² κ°μ΅λλ€. μΌλΆ μΈμ¦ μλΉμ€(μ: Auth0μ checkSession )μ κ²½μ° λͺ¨λ GraphQL μμ²μ λν΄ λΆνμν Auth0 μλ² μ볡μ λ§λλλ€.
λΉλκΈ° μλ‘ κ³ μΉ¨ ν ν°μ
fromPromise
λ₯Ό μ¬μ©νλ €κ³ ν©λλ€.
κΈ°λ³Έμ μΌλ‘ μ΄ κ²μλ¬Ό μ μΈ λ²μ§Έ μμλ₯Όν ν°μ μ±κ³΅μ μΌλ‘ κ°μ Έμ€κ³ μ μ₯νκ³ μμ§λ§
catch
λλfilter
λλflatMap
νΈμΆλμ§ μμ΅λλ€. μ΄κ²μ λλ²κΉ νλ λ°©λ²μ μ λͺ¨λ₯΄κ² μΌλ―λ‘ λͺ κ°μ§ μ μμ΄ λμμ΄ λ κ²μ λλ€.if (token && refreshToken) { return fromPromise( getNewToken(client) .then(({ data: { refreshToken } }) => { console.log("Promise data: ", refreshToken); localStorage.setItem("token", refreshToken.token); localStorage.setItem("refreshToken", refreshToken.refreshToken); return refreshToken.token; }) .catch((error) => { // Handle token refresh errors e.g clear stored tokens, redirect to login, ... console.log("Error after setting token: ", error); return; }) ) .filter((value) => { console.log("In filter: ", value); return Boolean(value); }) .flatMap(() => { console.log("In flat map"); // retry the request, returning the new observable return forward(operation); }); }
μ€λ₯μ μμΈμ΄ 무μμΈμ§ μ°Ύμμ΅λλ€. μμ μ½λμμλ λ³Ό μ μμ§λ§ map
ν¨μλ₯Ό μ¬μ©νμ¬ κ°κ°μ κ²°κ³Ό μ€λ₯λ₯Ό 맀ννμ΅λλ€. μ΄λ‘ μΈν΄ onError
μ΄ μ무 κ²λ λ°ννμ§ μκ³ κ΄μ°° κ°λ₯ κ°μ²΄κ° ν ν° κ°±μ μμ
μ λ±λ‘λμ§ μμμ΅λλ€.
κ½€ νΌλμ€λ½κ³ κ·Έκ²μ μμλ΄λ λ° λ무 μ€λ κ±Έλ Έμ΅λλ€. μ λ₯Ό λμμ£Όμ λΈλ‘κ·Έ ν¬μ€νΈ μμ±μμκ² κ°μ¬λ립λλ€.
ERROR Error: Network error: Cannot read property 'length' of null
@WilsonLau0755 , λλ κ°μ λ¬Έμ κ°μμμ΅λλ€. λͺ¨λ null
ν€λλ₯Ό λΉ λ¬Έμμ΄ ''
λ‘ μ€μ νμ¬ ν΄κ²°νμ΅λλ€.
onErrorλ₯Ό async awaitμ ν¨κ» μ¬μ©ν μ μλ μ΄μ λ 무μμ λκΉ?
κ°μ₯ μ μ©ν λκΈ
onError
μ½λ°±μ΄aync
ν¨μ λλPromise
λ°νμ νμ©νμ§ μλ κ² κ°μ΅λλ€. μ½λ μ°Έμ‘° https://github.com/apollographql/apollo-link/blob/59abe7064004b600c848ee7c7e4a97acf5d230c2/packages/apollo-link-error/src/index.ts#L60 -L74μ΄ λ¬Έμ λ μ΄μ μ λ³΄κ³ λμμ΅λλ€: #190
apollo-link-retry
κ° μ¬κΈ°μ νλ κ²κ³Ό μ μ¬νκ²apollo-link-error
κ°Promise
μ²λ¦¬ν μ μμΌλ©΄ λ μ μλν κ²μ΄λΌκ³ μκ°ν©λλ€. #436