This video is available to students only

Build GraphQL Authentication Resolvers for Google Auth

We'll continue to update the GraphQL resolver functions we've prepared to allow users to log-in & log-out of our application.

Building the Authentication Resolvers#

We've set up functions needed to interact with Google's Node.js Client to either generate an authentication URL or get user information for a user logging in. We'll now begin to establish our GraphQL schema and the resolvers for the fields our client application can interact with to handle this functionality.

We'll first modify our GraphQL schema and introduce some new type definitions.

We'll want the React client to pass a code argument to the logIn mutation to help conduct the login process in our server. Good convention often finds people specifying the arguments of a mutation within an input object. In the src/graphql/typeDefs.ts file, we'll create a new input object type to represent the input that can be passed into the logIn mutation. We'll label this input object type LogInInput and it is to contain a non-null code property of type GraphQL String.

We'll also state that the logIn mutation field is to accept an input argument of type LogInInput. Furthermore, we'll have the logIn mutation expect input as an optional argument. This is because, in the next couple of lessons, we'll investigate how users will also be able to log-in with the presence of a request cookie.

When the logIn and logOut mutations resolve successfully, we'll expect them both to return a GraphQL object type we'll create shortly labeled Viewer.

Viewer GraphQL Object Type#

Viewer is an object that is to represent the actual person looking/using our app (i.e. the person viewing our app). We'll create an object type to represent what a viewer object is to contain. It will have the following fields and corresponding field types:

  • id: ID - a unique identifier since every user in our database is to have a unique id.

  • token: String - a unique token value to help in countering Cross-Site Request Forgery, with which we'll learn more about in Module 5.

  • avatar: String - the viewer's avatar image URL.

  • hasWallet: Boolean - a boolean value to indicate if the viewer has connected to the payment processor in our app (Stripe).

  • didRequest: Boolean! - a boolean value to indicate if a request has been made from the client to obtain viewer information.

All the fields of the Viewer object type except for the didRequest field are optional. This is because the person viewing the app could be logged out, or doesn't have an account on our platform. In this case, we won't have any specific viewer information (id, avatar, etc.) but we'll still want our client to know that we did attempt to obtain viewer information. This is the reason as to why the didRequest field is a non-optional Boolean.

We'll get a better understanding of the purpose of these fields once we start to write more of our implementation. At this moment, the src/graphql/typeDefs.ts file of our server project will look like the following:

Viewer TypeScript Interface#

We'll now create the corresponding TypeScript definition for a Viewer object in our TypeScript code. We'll do this in the lib/types.ts file since the Viewer TypeScript type we'll create will be accessed in multiple parts of our server application.

The Viewer interface type we'll create in the lib/types.ts file will look very similar to the Viewer object type in our GraphQL schema with some minor differences such as:

  • In our Viewer TypeScript interface, we'll label the identifying field as _id instead of id because we are to reference the same _id field from our MongoDB database in our TypeScript code. We'll only have the identifying field of the Viewer return as id in a soon to be created resolver function in the Viewer GraphQL object.

  • In our Viewer TypeScript interface, we'll have a walletId field instead of hasWallet because walletId will be an actual id we'll get from our payment processor (Stripe) and we'll store in our database. We won't need to pass this sensitive information to the client which is why we resolve it to the client as a hasWallet boolean field which is to be true if the viewer has a walletId or undefined if viewer doesn't.

With that said, the Viewer TypeScript interface we'll create in the lib/types.ts file will look as follows:

Viewer, id, and hasWallet resolvers#

The _id and walletId conversion to id and hasWallet in our GraphQL schema will be done by creating resolver functions for the Viewer GraphQL object. If we recall, trivial resolvers often don't need to be declared when we simply attempt to return a value from an object argument using the same key specified in the object type (e.g. id -> viewer.id). In this case, however, we'll need to declare the resolver functions for the id and hasWallet fields for our GraphQL Viewer object type since we want to resolve these different fields.

We'll add the id and hasWallet resolver functions to a Viewer object we'll create in the viewerResolvers map within src/graphql/resolvers/Viewer/index.ts.

Our id resolver function will take a viewer input argument and return the value of viewer._id. The viewer input argument will have the shape of the Viewer interface we've set up in the lib/types.ts so we'll import the Viewer interface and set it as the type of the viewer argument.

Where is this viewer input argument coming from? The first positional argument of resolver functions will always be the root object returned from the parent fields. In this example, the parent logIn and logOut mutations will return the viewer object when resolved, and the resolver functions we define in the Viewer receives this viewer object and maps the data as we expect in our GraphQL schema.

The Viewer hasWallet resolver function will receive the viewer input argument and return true if the viewer walletId exists. Otherwise, we'll have it return undefined.

We could also have the hasWallet() resolver function just return boolean values of true or false.

Query.authUrl#

With the Viewer object defined in our TypeScript code and GraphQL schema, we'll now modify the authUrl() query resolver function to return the authUrl from the Google object we've created in our lib/api/ folder.

In the src/graphql/resolvers/Viewer/index.ts file, we'll import the Google object. In the authUrl() resolver function of the Query object, we'll use a try...catch pattern to have the field simply return the authUrl field of our Google API object or throw an error.

Mutation.logIn#

We'll now modify the resolver function for the root level logIn mutation. The logIn mutation is to expect an input that contains a code property. We'll define a TypeScript interface type to describe the shape of the arguments the logIn mutation is to expect. We'll define this interface in a types.ts we'll keep in the src/graphql/resolvers/Viewer/ folder.

We'll name the interface to describe the shape of arguments the logIn mutation is to accept LogInArgs and it will have an input property that is to be an object that has a code of type string. Since this input property is optional, we'll say the input object might also be null.

We'll head back to the viewerResolvers map and look to build the implementation for the logIn() resolver function.

Start a new discussion. All notification go to the author.