Skip to content

Commit

Permalink
✨ feat: apollo req & res middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
yoopark committed Jul 12, 2023
1 parent c93bbda commit 42489d5
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 4 deletions.
51 changes: 47 additions & 4 deletions app/src/providers/ApolloProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,72 @@
import { getNewAccessToken } from '@/services/auth/getNewAccessToken';
import {
ApolloClient,
ApolloLink,
ApolloProvider,
HttpLink,
InMemoryCache,
concat,
from,
fromPromise,
} from '@apollo/client';
import { onError } from '@apollo/client/link/error';
import { ROUTES } from '@routes/ROUTES';
import { getAccessToken } from '@utils/storage/accessToken';
import { clearStorage } from '@utils/storage/clearStorage';
import { getRefreshToken } from '@utils/storage/refreshToken';

const httpLink = new HttpLink({
uri: import.meta.env.VITE_BACKEND_GRAPHQL_ENDPOINT,
});

const authMiddleWare = new ApolloLink((operation, forward) => {
if (operation.operationName === 'getLanding') {
return forward(operation);
}
const accessToken = getAccessToken();
if (accessToken === null) {
return forward(operation);
}
operation.setContext({
headers: {
authorization: `Bearer ${getAccessToken()}`,
...operation.getContext().headers,
authorization: `Bearer ${accessToken}`,
},
});
return forward(operation);
});

const refreshLink = onError(({ graphQLErrors, operation, forward }) => {
if (graphQLErrors) {
// forEach 내부에서 async await 사용하면 안됨 -> for ~ of
for (const { extensions } of graphQLErrors) {
if (extensions?.status === 401) {
return fromPromise(getNewAccessToken(getRefreshToken() ?? '')).flatMap(
(accessToken) => {
operation.setContext({
headers: {
...operation.getContext().headers,
authorization: `Bearer ${accessToken}`,
},
});
return forward(operation);
},
);
}
}
}
});

const resetLink = onError(({ graphQLErrors }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ extensions }) => {
if (extensions?.status === 400) {
clearStorage();
window.location.href = ROUTES.ROOT;
}
});
}
});

/**
* @description
* 쿼리 바뀌었을때 이부분 안건드리면 기존에 있던캐시정보에 바뀐 쿼리정보 병합하려고해서 에러발생
Expand All @@ -34,8 +76,9 @@ const authMiddleWare = new ApolloLink((operation, forward) => {
* https://go.apollo.dev/c/merging-non-normalized-objects
* https://go.apollo.dev/c/generating-unique-identifiers
*/
const client = new ApolloClient({
link: concat(authMiddleWare, httpLink),

export const client = new ApolloClient({
link: from([authMiddleWare, refreshLink, resetLink, httpLink]),
cache: new InMemoryCache({
typePolicies: {
Query: {
Expand Down
36 changes: 36 additions & 0 deletions app/src/services/auth/getNewAccessToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { gql } from '@/__generated__';
import { client } from '@providers/ApolloProvider';
import { setAccessToken } from '@utils/storage/accessToken';
import { setRefreshToken } from '@utils/storage/refreshToken';

const GET_NEW_ACCESS_TOKEN = gql(/* GraphQL */ `
mutation GetNewAccessToken($refreshToken: String!) {
refreshToken(refreshToken: $refreshToken) {
__typename
... on Success {
message
accessToken
refreshToken
userId
}
... on NoAssociated {
message
}
}
}
`);

export const getNewAccessToken = async (refreshToken: string) => {
const { data } = await client.mutate({
mutation: GET_NEW_ACCESS_TOKEN,
variables: { refreshToken },
});
if (!data || data.refreshToken.__typename !== 'Success') {
return null;
}
const { accessToken: newAccessToken, refreshToken: newRefreshToken } =
data.refreshToken;
setAccessToken(newAccessToken);
setRefreshToken(newRefreshToken);
return newAccessToken;
};
4 changes: 4 additions & 0 deletions app/src/utils/storage/clearStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const clearStorage = () => {
localStorage.clear();
sessionStorage.clear();
};

0 comments on commit 42489d5

Please sign in to comment.