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

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

vinodchauhan7 profile image vinodchauhan7 ・3 min read

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
});

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);

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;
  `;

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;
  }

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..

Posted on Oct 23 '19 by:

vinodchauhan7 profile

vinodchauhan7

@vinodchauhan7

A learning enthusiast with expertise in react and node.

Discussion

markdown guide