Skip to main content

If you are using our backend SDK that is lesser than the following versions, please visit the older documentation link here.

Which UI do you use?
Custom UI
Pre built UI

Backend Setup

Complete Quick Setup#

CAUTION

This guide only applies to scenarios which involve SuperTokens Session Access Tokens.

If you are implementing either, Unified Login or Microservice Authentication, features that make use of OAuth2 Access Tokens, please check the separate page that shows you how to verify those types of tokens.

Follow the frontend and backend pre-built UI setup guides to setup SuperTokens.

This should give you a login UI, backend APIs for the login UI to work, session refreshing and signout. The rest of this guide will focus on how you can do session verification in your GraphQL resolvers.

Method 1: Session verification in the GraphQL context (Easier)#

We want to use the Session.getSession function in the context function to verify the session, and add the userId into our context so that our resolvers can read it. If the user id not logged in, we will set the userId to undefined in the context

import { ApolloServer } from "@apollo/server";
import express from "express";
import { expressMiddleware } from "@apollo/server/express4";
import { GraphQLError } from 'graphql';
import Session from "supertokens-node/recipe/session";

let app = express();

const typeDefs = '...'
const resolvers = {/* ... */ }

const server = new ApolloServer({
typeDefs,
resolvers,
})

server.start().then(() => {
app.use(express.json(), expressMiddleware(server, {

// Note: This example uses the `req` and `res` argument to access headers,
// but the arguments received by `context` vary by integration.
// This means they vary for Express, Fastify, Lambda, etc.
context: async ({ req, res }) => {
try {
let session = await Session.getSession(req, res, {
sessionRequired: false
})
return {
userId: session !== undefined ? session.getUserId() : undefined
};
} catch (err) {
if (Session.Error.isErrorFromSuperTokens(err)) {
throw new GraphQLError('Session related error', {
extensions: {
code: 'UNAUTHENTICATED',
http: { status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401 },
},
});
}
throw err;
}
},
}))

app.listen(3001, () => {
console.log("Server started");
})
})

In the above code snippet, we first attempt to verify the session using the Session.getSession function. If the session is valid, we will add the userId to the context. If the access token has expired, we will throw an error with a status code of 401. If a session claim has failed (for example if the user's email is not verified) we will return a status code of 403.

The 401 status code will cause the session refresh flow to start, which will give a new access token to the user, or else if the session was revoked, the user will be logged out.

In case the user is not logged in, the Session.getSession function will throw return undefined, in which case, your resolvers won'd have a userId in the context.

The downside of this method is that if you want to mutate the session's access token payload in one of your resolvers, then you don't have access to the session object in there. This is where the method below comes into the picture:

Method 2: Session verification in the graqphQL resolver (More flexible)#

Unlike the method above, we will be doing session verification on a per resolver basis here. This means that you will have access to the session object in your resolver using which you can update the information in the session (like its access token payload).

We start by creating a helper function (a sort of middleware for your resolver) which you will have to call in all of your resolvers that require a session:

import Session, { SessionContainer } from "supertokens-node/recipe/session";
import { GraphQLError } from 'graphql';

async function withSession<T>(contextValue: any, resolver: (session: SessionContainer) => Promise<T>) {
try {
let session = await Session.getSession(contextValue.req, contextValue.res);
return await resolver(session);
} catch (err) {
if (Session.Error.isErrorFromSuperTokens(err)) {
throw new GraphQLError('Session related error', {
extensions: {
code: 'UNAUTHENTICATED',
http: { status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401 },
},
});
}
}
}

In the above function, we attempt to verify the session using Session.getSession. If the session is valid, we will call the resolver function with the session object. If the access token has expired, or if the session does not exist, we will throw an error with a status code of 401. If a session claim has failed (for example if the user's email is not verified) we will return a status code of 403.

For this resolver to work, we will have to add the req and res object into the GraphQL context. This can be done as follows:

import { ApolloServer } from "@apollo/server";
import express from "express";
import { expressMiddleware } from "@apollo/server/express4";
import { GraphQLError } from 'graphql';

let app = express();

const typeDefs = '...'
const resolvers = {/* ... */}

const server = new ApolloServer({
typeDefs,
resolvers,
})

server.start().then(() => {
app.use(express.json(), expressMiddleware(server, {
// Note: This example uses the `req` and `res` argument to access headers,
// but the arguments received by `context` vary by integration.
// This means they vary for Express, Fastify, Lambda, etc.
context: async ({req, res}) => {
return {
req, res
};
},
}))

app.listen(3001, () => {
console.log("Server started");
})
})

Finally, we can use our withSession in our resolvers as shown below:

import { ApolloServer } from "@apollo/server";

const server = new ApolloServer({
typeDefs,
resolvers: {
Query: {
userProfile: async (_: any, __: any, contextValue) => {
// starts of your resolver code..
return await withSession(contextValue, async (session) => {
// getUserName is a custom application function...
let name = await getUserName(session.getUserId())

return {
userId: session.getUserId(),
sessionHandle: session.getHandle(),
name
};
});
}
},
},
})