DEV Community

Cover image for SSR: clerk with hasura
Shivam
Shivam

Posted on

SSR: clerk with hasura

This article will help you in achieving server side rendering(authentication) with clerk(withServerSideAuth) in next.js

Clerk
Multiple authentication strategies are available through Clerk so that legitimate users can access your application and make authenticated requests

Into SSR with Nextjs and Clerk
To enable server-side rendering, Nextjs uses a special export named getServerSideProps. WithServerSideAuth helper from clerk is populated with the auth singleton.

steps:

  1. setup an apollo client
  2. use getServerSideProps and withServerSideAuth for ssr
  3. use graphql query
  4. test for both authenticated and non-authenticated user
  5. if there is a condition where your query is meant to response only after login then please do not return anything otherwise it will lead to issues

code snippet for same:

1.apolloclient setup:
import { useMemo } from 'react';
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
import merge from 'deepmerge';
import isEqual from 'lodash/isEqual';

let apolloClient;
const uri = process.env.NEXT_PUBLIC_HASURA_URI;
function createApolloClient(headers) {
  return new ApolloClient({
    ssrMode: typeof window === 'undefined', // set to true for SSR
    link: createHttpLink({
      uri,
      credentials: 'same-origin',
      headers: {
        Apitoken: process.env.YOUR_API_TOKEN,
      },
    }),
    cache: new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            feed: {
              keyArgs: ['strategy', 'limit', 'gt_token'],
              // Concatenate the incoming list items with
              // the existing list items.
              merge(existing = [], incoming) {
                const result = Object.assign({}, incoming);
                if (existing?.hits) {
                  result.hits = [...existing.hits, ...incoming.hits];
                }
                return result;
              },
            },
            experts: {
              keyArgs: ['limit'],
              // Concatenate the incoming list items with
              // the existing list items.
              merge(existing = [], incoming) {
                const result = Object.assign({}, incoming);
                if (existing?.hits) {
                  result.hits = [...existing.hits, ...incoming.hits];
                }
                return result;
              },
            },
          },
        },
      },
    }),
  });
}

export function initializeApollo(initialState = null, ctx) {
  const headers = ctx?.headers ?? {};
  const _apolloClient = apolloClient ?? createApolloClient(headers);

  // If your page has Next.js data fetching methods that use Apollo Client,
  // the initial state gets hydrated here
  if (initialState) {
    // Get existing cache, loaded during client side data fetching
    const existingCache = _apolloClient.extract();

    // Restore the cache using the data passed from
    // getStaticProps/getServerSideProps combined with the existing cached data
    const data = merge(initialState, existingCache, {
      // combine arrays using object equality (like in sets)
      arrayMerge: (destinationArray, sourceArray) => [
        ...sourceArray,
        ...destinationArray.filter((d) =>
          sourceArray.every((s) => !isEqual(d, s))
        ),
      ],
    });
    _apolloClient.cache.restore(data);
  }

  // For SSG and SSR always create a new Apollo Client
  if (typeof window === 'undefined') return _apolloClient;

  // Create the Apollo Client once in the client
  if (!apolloClient) apolloClient = _apolloClient;
  return _apolloClient;
}

export function useApolloHasura(initialState) {
  return useMemo(
    () => initializeApollo(initialState, undefined),
    [initialState]
  );
}
Enter fullscreen mode Exit fullscreen mode
2.use getServerSideProps and withServerSideAuth for ssr:
import { initializeApollo } from '../apolloClient';
import { withServerSideAuth } from '@clerk/nextjs/ssr';

export const getServerSideProps = withServerSideAuth(
  async (ctx) => {
   const { getToken } = ctx.req.auth;
    const token = await getToken({ template: 'hasura' });
    const apolloClient = initializeApollo(null, ctx);

   const client = initialApollo(null, ctx);
    const sub =
      (await client.query({
        query: YOUR_QUERY,
        context: {
          headers: {
            authorization: 'YOUR_AUTHORIZATION_TOKEN',
          },
        },
      }));

    const status = sub?.data;

       return {
      props: {
        isStatus: status,
      },
    };
  },
  { loadUser: true }
);
Enter fullscreen mode Exit fullscreen mode
3. condition where your query is meant to response only after logged in:

here is the snippet for query where it gives response only for authenticated users example subscription query... you need to make small changes on above code. the status will return undefined if token(hasuraToken) will return nothing for non-authenticated user, so on that case you need to return null as shown below.

  const sub =
      token !== null &&
      (await client.query({
        query: PAYMENTS,
        context: {
          headers: {
            authorization: `Bearer ${token}`,
          },
        },
      }));

   const status = sub?.data;

   return {
      props: {
        isStatus: status === undefined ? null : status
      },
    };


Enter fullscreen mode Exit fullscreen mode

For more info please refer to Clerk and Nextjs with SSR example

What do you think about this? Is there any other way that I haven't mentioned? Please please please let me know by commenting down below. I can't wait to hear that. Thanks

Happy Coding!!

Top comments (0)