Chapter 15

Authenticate GraphQL APIs

Define Schema for Authentication



Error handling is a crucial aspect of developing resilient applications, and within the context of a NestJS GraphQL service, throwing an error from a resolver, such as getSongs in SongResolver, allows for graceful failure management. This approach can encapsulate business logic validation and operational faults, providing clear feedback to the client about what went wrong.

Incorporating error handling at the resolver level adheres to best practices by localizing error management and maintaining clean separation of concerns. Structured correctly, it can streamline debugging and maintenance, ensuring that the GraphQL API communicates effectively with clients and enhances the overall robustness of the service.

@Query('songs')

  async getSongs(): Promise<Song[]> {

    // return this.songService.getSongs();

    // throw new Error('Unable to fetch songs!');

    throw new GraphQLError('Unable to fetch the songs', {

      extensions: {

        code: 'INTERNAL_SERVER_ERROR',

      },

    });

  }



In practice, structured error handling is crucial as it aids client applications in deciphering the nature of errors. The inclusion of an error code within the extensions of GraphQLError allows for consistent error processing and can be used to trigger specific client-side behavior. It's recommended to handle errors gracefully and provide meaningful error messages to maintain a seamless user experience. Moreover, logging such errors in a manner that they can be monitored and analyzed can lead to improved system robustness and quicker resolution of underlying issues.



Resolve Auth Queries and Mutations



Step 1: Refactoring SignupInput

signup(signupInput: SignupInput!): SignupResponse!



Renaming from SingupInput to SignupInput corrects a typographical error, ensuring that the identifier accurately reflects its purpose and adheres to naming conventions. Similarly, updating the field from acessToken to accessToken rectifies a spelling mistake, which is crucial for maintaining the integrity of the codebase and avoiding potential bugs related to misnamed properties.

The generation of TypeScript typings is an essential step for enforcing type safety within the application, which facilitates the early discovery of errors and enhances the development experience. Implementing a script to automate the generation of these typings ensures consistency and efficiency, which is a recommended practice for maintaining a robust and scalable codebase.

npm run generate:typings



Step 2: Creating the Auth Resolver

The Auth Resolver in NestJS functions as a bridge between the client's requests and the authentication services, handling operations such as login, registration, and user authentication. Within this resolver, decorators and method handlers work in conjunction to parse client requests, validate user credentials, and return appropriate responses, effectively encapsulating authentication logic in a clean, modular way.

Best practices include implementing robust security measures within the resolver, such as password hashing and token-based authentication, to protect user data. Additionally, it's advantageous to employ middleware for consistent session management across the application, ensuring a secure and seamless user experience.

nest g resolver auth



Step 2: Resolver with Signup Mutation and Login Query

In a NestJS GraphQL service, the resolver acts as a bridge between the GraphQL API and the underlying data models, facilitating the mutation and query operations. A 'Signup' mutation within the resolver typically handles the creation of new user accounts, managing the input data, user validation, and persistence, while a 'Login' query is responsible for authenticating users, issuing tokens, and ensuring secure access control.

For best practices, the resolver functions must be designed with security as a priority, using practices such as password hashing and validation checks. Additionally, leveraging features such as guards and interceptors can optimize the authentication flow and enhance the application's security posture. It is also recommended to modularize the resolver code for maintenance and readability, separating different functionalities into distinct, manageable units.

import { Args, Mutation, Resolver, Query } from "@nestjs/graphql";

import {

  SignupResponse,

  SignupInput,

  LoginInput,

  LoginResponse,

} from "src/graphql";

import { UsersService } from "src/users/users.service";

import { AuthService } from "./auth.service";



@Resolver()

export class AuthResolver {

  constructor(

    private userService: UsersService,

    private authService: AuthService

  ) {}



  @Mutation("signup")

  async singupUser(

    @Args("signupInput")

    signupInput: SignupInput

  ): Promise<SignupResponse> {

    return this.userService.create(signupInput);

  }



  @Query("login")

  async loginUser(

    @Args("loginInput")

    loginInput: LoginInput

  ): Promise<LoginResponse> {

    return this.authService.login(loginInput);

  }

}



Now run the application and test the signup mutation and login request.



Apply Authentication using AuthGuard



Step 1: Define a Profile Query

type Query {

  profile: Profile!

}



type Profile {

  email: String!

  userId: String!

}



Defining new TypeScript typings via a dedicated script command enhances the robustness of the codebase by ensuring that data structures are predictable and type checks are enforceable at compile time. It is a sound practice to include this step in continuous integration pipelines for automated checks.

Securing a resolver in NestJS, such as a user profile resolver, is crucial to safeguard sensitive data and ensure that only authenticated users can access their profiles. Implementing a custom AuthGuard is a strategic approach to enforcing authentication and authorization policies on specific application routes or handlers, maintaining the integrity of user data. Integrating guards into the application's lifecycle allows for scalable and manageable security mechanisms, which can be tailored to the evolving needs of the application.

Step 2: Create a Resolver for the Profile object



 @Query('profile')

  @UseGuards(GraphQLAuthGaurd)

  getProfile(parent, args, contextValue, info): Profile {

    console.log(parent);

    console.log(args);

    console.log(contextValue);

    console.log(info);

    return contextValue.req.user;

  }



The getProfile function showcased is a query resolver in a GraphQL API built with NestJS, guarded by a custom GraphQLAuthGaurd. This resolver retrieves the user profile from the context of an authenticated request, where contextValue.req.user is typically populated by the authentication middleware after a user successfully logs in.

Best practices for such resolvers include implementing robust logging strategies to facilitate debugging and introspection, while also ensuring sensitive information is not logged in production environments. Additionally, thorough checks and validation on args and contextValue should be in place to maintain the integrity of the data and the security of the API.

Step 3: Create a GraphQLAuth authentication guard

In NestJS, guards are a type of component that determine whether a given request will be handled by the route handler or not, typically used for authentication and authorization. Creating a new guard in the auth folder encapsulates authentication logic, ensuring that only valid requests with proper credentials can access certain routes, contributing to a secure application architecture.

As a robust practice, this guard should be thoroughly tested to guarantee it behaves as expected under various scenarios, thereby reinforcing the security posture. It is also advisable to keep the guard's logic decoupled and modular, allowing for reuse across different parts of the application as security requirements evolve.

import { AuthenticationError } from "@nestjs/apollo";

import { ExecutionContext, Injectable } from "@nestjs/common";

import { ExecutionContextHost } from "@nestjs/core/helpers/execution-context-host";

import { GqlExecutionContext } from "@nestjs/graphql";

import { AuthGuard } from "@nestjs/passport";

import { Observable } from "rxjs";



@Injectable()

export class GraphQLAuthGaurd extends AuthGuard("jwt") {

  canActivate(

    context: ExecutionContext

  ): boolean | Promise<boolean> | Observable<boolean> {

    const ctx = GqlExecutionContext.create(context);

    const { req } = ctx.getContext();

    return super.canActivate(new ExecutionContextHost([req]));

  }



  handleRequest<TUser = any>(err: any, user: any): TUser {

    if (err || !user) {

      throw new AuthenticationError("GqlAuthguard");

    }

    return user;

  }

}



This custom guard overrides the canActivate and handleRequest methods, facilitating the integration of the authentication guard with the GraphQL execution context. By ensuring the authentication flows seamlessly with GraphQL's unique context handling, the application upholds security across GraphQL resolvers. As a best practice, it is critical to handle errors gracefully and provide informative messages, as demonstrated in the handleRequest method. Additionally, utilizing Observables for asynchronous operations offers a robust approach to handle streams of authentication events, ensuring a reactive system that can scale with the application's needs.

Step 4: Test the Application

In the context of authentication within a NestJS application, sending a login request typically involves submitting user credentials to receive an access token. This token is then used in subsequent requests to access user-specific resources, such as a profile, serving as proof of authentication and authorization.

As a best practice, the access token should be handled securely, often sent in the HTTP Authorization header, and validated on the server for each request to protect against unauthorized access. Utilizing middleware or guards in NestJS to automate the token validation process can streamline security and ensure consistency across the application.



Subscriptions in GraphQL



Step 1: Installing graphql subscriptions

The graphql-subscriptions package is a key element for integrating subscription-based real-time functionality into a Nest.js application, leveraging GraphQL. By implementing subscriptions, the application can push updates to clients in real-time, which is essential for features that require immediate data reflection, such as live chats or real-time feeds.

Incorporating this package into a project follows the best practice of enhancing user experience with dynamic content updates, without the need for manual refreshes. It's advisable to ensure that the use of subscriptions is optimized and does not strain the server with unnecessary loads, and proper authorization mechanisms are implemented to secure real-time data channels.

npm install graphql-subscriptions



GraphQLModule.forRoot<ApolloDriverConfig>({

      installSubscriptionHandlers: true,

    }),



Enabling installSubscriptionHandlers to true within the AppModule activates WebSocket support in a NestJS application, which is essential for handling real-time bi-directional communication between the server and clients. This configuration step is critical when the application needs to utilize WebSockets for features such as live chat, real-time feeds, or push notifications.

It is considered good practice to encapsulate the WebSocket configuration within dedicated modules or providers, thus keeping the AppModule clean and maintainable. Additionally, thorough testing of WebSocket handlers is recommended to ensure that they handle client communication effectively and securely.

Step 2: Create Subscription

type Subscription {

  songCreated: Song!

}



A new subscription, songCreated, is established, which dispatches notifications with comprehensive details to subscribers whenever a new song is created. In practical applications, akin to an artist releasing a song on Spotify, the system triggers notifications to inform users about the new release, enhancing user engagement and experience.

Step 3: Resolve the Subscription

A PubSub instance facilitates event publishing to specific labels and event subscription for those labels within server code. Creation of a PubSub instance is initiated at the top of the SongResolver.

Instantiation of a PubSub instance at the SongResolver commencement allows for event-driven interactions throughout the resolver's scope. This pattern is central to implementing subscription operations effectively in GraphQL, allowing for real-time updates and communication within the application.

const pubSub = new PubSub();



@Subscription('songCreated')

  songCreated() {

    return pubSub.asyncIterator('songCreated'); //1

  }



The songCreated field has been established within the subscription. Upon running the application and initiating the subscription, it actively listens and awaits the event to be published. The AsyncIterator specifically listens for events associated with a designated label, facilitating event-driven workflows in the application.



Step 4: Publish the event

const newSong = this.songService.createSong(args);

pubSub.publish("songCreated", { songCreated: newSong });

return newSong;



The createSong resolver is configured to publish an event, with songCreated being the designated payload. Observers of this event can expect to receive the songCreated payload as demonstrated in the response provided below.


$7 bundle of my best NestJS + backend developer Courses.
More details coming soon.





Get the latest insights from the marketing world.

A blog that focuses on providing practical tips and strategies for businesses to improve their marketing and sales efforts.

Solutions

Helping you help your customers.

Sell smarter, better, and faster.

The insights you need to make smarter business decisions.