Shivam
Posted on July 21, 2022
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:
- setup an apollo client
- use getServerSideProps and withServerSideAuth for ssr
- use graphql query
- test for both authenticated and non-authenticated user
- 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]
);
}
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 }
);
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
},
};
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!!
Posted on July 21, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.