DEV Community

Cover image for dev.to clone using React-Node, Apollo-Cache, apollo-link-token-refresh, One-2-Many relation in Typeorm [Part-3]
vinodchauhan7
vinodchauhan7

Posted on

dev.to clone using React-Node, Apollo-Cache, apollo-link-token-refresh, One-2-Many relation in Typeorm [Part-3]

Hello Community, Today I am writing 3 part of this article series in which I am trying to clone dev.to with minimum feature.It will be a prototype, in which user can signUp/signIn, create post & other features.

I am doing it for just learning purpose.
If you have not visited the Part-1 & Part-2, please understand those first for better clearity of this one.

Part- 1
Part -2

Code Repo : GitHub Link

Howdy! Friends, In this article I am able to complete many things such as Login/Registration functionality with token refresh feature. Also able to maintain cache using Apollo state management and at last loggedIn user can post its articles see other users articles and profiles.

Alt Text

Login

When user login a refresh_token saved in user browser cookies.For user smooth usage of application we need to maintain its authentication even if user reload the page or if its token get expired. We have take care of both the features with RefreshToken and using Apollo-link-token-Refresh module in case of token got expired.

//Getting access Token and passing it in request headers
const requestLink = new ApolloLink(
  (operation, forward) =>
    new Observable(observer => {
      let handle: any;
      Promise.resolve(operation)
        .then(operation => {
          const accessToken = getAccessToken();
          if (accessToken) {
            operation.setContext({
              headers: {
                authorization: `bearer ${accessToken}`
              }
            });
          } //accessToken is defined
        }) //then operation ends here
        .then(() => {
          handle = forward(operation).subscribe({
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            complete: observer.complete.bind(observer)
          }); //handle ends here
        })
        .catch(observer.error.bind(observer));

      return () => {
        if (handle) handle.unsubscribe();
      };
    })
);

const client = new ApolloClient({
  link: ApolloLink.from([
    new TokenRefreshLink({
      accessTokenField: "accessToken",
      isTokenValidOrUndefined: () => {
        const token = getAccessToken();

        if (!token) {
          return true;
        }

        try {
          const { exp } = jwtDecode(token);
          if (Date.now() >= exp * 1000) {
            return false;
          } else {
            return true;
          }
        } catch (err) {
          console.log(err);
          return false;
        }
      },
      fetchAccessToken: () => {
        return fetch("http://localhost:4000/refresh_token", {
          method: "POST",
          credentials: "include"
        });
      },
      handleFetch: accessToken => {
        setAccessToken(accessToken);
      },
      handleError: err => {
        console.warn("Your refresh token is invalid. Try to relogin");
        console.error(err);
      }
    }),
    onError(() => {}),
    requestLink,
    new HttpLink({
      uri: "http://localhost:4000/graphql",
      credentials: "include"
    }) //new HttpLink ends here
  ]),
  cache
});

Enter fullscreen mode Exit fullscreen mode

Apollo-Cache

When user loggedIn, we are updating our cache for MeQuery so that it can be used throughout the application to know who is currently loggedIn without hitting the actual graphql query.

 const LoginComponentUser = withFormik<MyFormProps, FormValues>({
    mapPropsToValues: props => ({
      email: props.initialEmail || "",
      password: props.initialPassword || ""
    }),
    validationSchema: Yup.object().shape({
      email: Yup.string()
        .email("Email is not valid")
        .required("Email is required"),
      password: Yup.string().required("Password is required")
    }),
    async handleSubmit({ email, password }: FormValues) {
      console.log(email, password);

      const response = await login({
        variables: {
          data: {
            email,
            password
          }
        },
        update: (store, { data }) => {
          //updating cache so that it will not hit again and again
          if (!data) {
            return null;
          }
          store.writeQuery<MeQuery>({
            query: MeDocument,
            data: {
              __typename: "Query",
              me: data.login.user
            }
          });
        }
      });

      console.log(response);
      if (response && response.data) {
        setAccessToken(response.data.login.accessToken);
      }

      console.log(getAccessToken());
      window.location.replace("http://localhost:3000/");
    }
  })(LoginForm);
Enter fullscreen mode Exit fullscreen mode

Styled-Components

I am also adding little-bit styled-components usage for beginners to showcase how to use css in JS.

 const WritePost = styled.a`
    width: 118px;
    display: block;
    margin-top: 10px;
    padding: 3px;
    text-align: center;
    font-weight: bold;
    border-radius: 3px;
    border: 2px solid #0a0a0a;
    color: #0a0a0a;
    background: #66e2d5;
    font-size: 11px;
    text-decoration: none !important;
    font-stretch: condensed;
    &:hover {
      color: #0b0b0b;
      background: #66e2e5;
    }
  `;

  const ShowSvg = styled.div`
    margin-top: 10px;
  `;
Enter fullscreen mode Exit fullscreen mode

One-to-Many Relations

I am also showing relations between schemas to show how we can implement this TypeORM feature using Graphql in our application by showing [user-post] relationship.

 @Query(() => [Post])
  @UseMiddleware(isAuth)
  async getAllPostById(@Arg("userId") userId: number): Promise<Post[]> {
    const post = await Post.find({
      where: { user: { id: userId } },
      relations: ["user"]
    });
    console.log(JSON.stringify(post, null, 2));
    return post;
  }
Enter fullscreen mode Exit fullscreen mode

I have not made FrontEnd similar to dev.To fully because it will took time for matching all the things and also all feature.

Alt Text

Alt Text

Alt Text

Alt Text

The purpose of this article series is to familiar beginners or intermediate react developers with Token refresh,apollo and typeorm features.

Will come back with new features and articles, Till then bye guys..

Top comments (0)