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

2

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

Image of Stellar post

How a Hackathon Win Led to My Startup Getting Funded

In this episode, you'll see:

  • The hackathon wins that sparked the journey.
  • The moment José and Joseph decided to go all-in.
  • Building a working prototype on Stellar.
  • Using the PassKeys feature of Soroban.
  • Getting funded via the Stellar Community Fund.

Watch the video 🎥

Top comments (0)

Image of PulumiUP 2025

From Cloud to Platforms: What Top Engineers Are Doing Differently

Hear insights from industry leaders about the current state and future of cloud and IaC, platform engineering, and security.

Save Your Spot

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay