AWS CDK: AppSync GraphQL API with Cognito and Apollo Client in React

January 19th, 2025 878 Words

The Amazon Cognito User Pool with Managed Login is a great baseline to start a new project. This guide adds an AWS AppSync GraphQL Data API to the project and shows you how to use the Amazon Cognito Access Token to authenticate against the GraphQL API using the Apollo Client in React.

The Amazon Cognito UI in the AWS Management Console has everything you need to get started; this guide uses React with TypeScript.

Cognito Client Configuration

Based on the outputs of your cdk deploy command, you can configure React and the react-oidc-context package with the needed references to the AWS resources.

import { AuthProvider } from "react-oidc-context";

const cognitoAuthConfig = {
  authority: "https://cognito-idp.eu-central-1.amazonaws.com/eu-cent…",
  client_id: "6n1hf59di3lb65pr3…",
  redirect_uri: "http://localhost:3000",
  response_type: "code",
  scope: "aws.cognito.signin.user.admin email openid phone profile",
};

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <AuthProvider {...cognitoAuthConfig}>
      <App />
    </AuthProvider>
  </React.StrictMode>
);

Together with the react-oidc-context package, this creates everything you need. Within your <App /> component, you can use the useAuth() hook (Introduction to React Hooks) to get the current user - and most importantly - the Access Token:

import { useAuth } from "react-oidc-context";

export default function App() {
  const auth = useAuth();

  if (auth.isAuthenticated) {
    return (
      <div>
        <pre> Access Token: {auth.user?.access_token} </pre>
      </div>
    );
  } else {
    return (
      <div>
        <button onClick={() => auth.signinRedirect()}>Sign in</button>
      </div>
    );
  }
}

With the baseline, we can now send authenticated requests to AWS AppSync. Relevant for this guide is the AWS AppSync GraphQL Data API.

The recently introduced AWS AppSync Events API is an alternative to the AWS AppSync GraphQL Subscription API and is covered in the second part of this guide.

Configure AWS AppSync with Amazon Cognito

Using the Amazon Cognito User Pool created in the previous guide, you can configure the AWS AppSync API to use it for authentication. As mentioned before, AWS AppSync supports two types of API: GraphQL and Events.

AppSync GraphQL Data API

To have an example GraphQL API, create a Session type with the current user’s username and ID as fields. The GraphQL Schema looks like this:

type Session @model {
  sub: String!
  username: String!
}

type Query {
  session: Session
}

schema {
  query: Query
}

Instead of creating the AppSync GraphQL API with a manually created schema file, use the awscdk-appsync-utils package to create the schema with code (more about schema-first GraphQL APIs). First, start with an empty schema and the AppSync GraphQL API:

import * as aws_appsync from "aws-cdk-lib/aws-appsync";
import * as aws_logs from "aws-cdk-lib/aws-logs";
import { CodeFirstSchema } from "awscdk-appsync-utils";

const schema = new CodeFirstSchema();

const api = new aws_appsync.GraphqlApi(stack, "Api", {
  name: `serverless-graphql-api`,
  schema,
  authorizationConfig: {
    defaultAuthorization: {
      authorizationType: aws_appsync.AuthorizationType.USER_POOL,
      userPoolConfig: {
        userPool: users,
      },
    },
    additionalAuthorizationModes: [
      {
        authorizationType: aws_appsync.AuthorizationType.IAM,
      },
    ],
  },
  logConfig: {
    fieldLogLevel: aws_appsync.FieldLogLevel.ALL,
    retention: aws_logs.RetentionDays.ONE_WEEK,
    excludeVerboseContent: false,
  },
  introspectionConfig: aws_appsync.IntrospectionConfig.ENABLED,
  xrayEnabled: true,
});

With an empty schema definition, the deployment of this will fail. Next, create a Session type with the basic properties username and sub we need; both are strings and required in the response.

const typeSession = new ObjectType("Session", {
  definition: {
    sub: GraphqlType.string({ isRequired: true }),
    username: GraphqlType.string({ isRequired: true }),
  },
});

Next, define the AppSync Resolver for the session field.

const fieldSession = new ResolvableField({
  returnType: typeSession.attribute(),
  dataSource: api.addNoneDataSource("SessionDataSource"),
  runtime: aws_appsync.FunctionRuntime.JS_1_0_0,
  code: aws_appsync.Code.fromInline(`
    export function request(ctx) {
      return {}
    }

    export function response(ctx) {
      return ctx.identity
    }
  `),
});

Using serverless AWS AppSync JavaScript Resolvers this will return the current user’s Cognito session object. As a last step, you need to add the Session type to the schema and the session field to the Query type:

schema.addQuery("session", fieldSession);
schema.addType(typeSession);

With this, you can now deploy the GraphQL API and use it with React. I prefer to use Apollo Client for React when working with GraphQL in the browser.

Apollo Client for AppSync GraphQL

Thanks to the react-oidc-context package, you can easily retrieve the Access Token from the useAuth() hook and use it to create a new Apollo Client object:

export default function App() {
  const auth = useAuth();

  const link = ApolloLink.from([
    setContext(async (_, { headers }) => {
      return {
        headers: {
          ...headers,
          authorization: `Bearer ${auth.user!.access_token}`,
        },
      };
    }),
    createHttpLink({
      uri: "https://<api-id>.appsync-api.eu-central-1.amazonaws.com/graphql",
    }),
  ]);

  const client = new ApolloClient({ link, cache: new InMemoryCache() });

  if (auth.isAuthenticated) {
    return (
      <ApolloProvider client={client}>
        <div>Authenticated</div>
      </ApolloProvider>
    );
  } else {
    return <div>Not authenticated</div>;
  }
}

That’s all you need to have the Apollo GraphQL Client working with AWS AppSync GraphQL Data API using Amazon Cognito for authentication; using TypeScript. Next, you can create a React component to query for the user’s session.

interface SessionData {
  session: {
    sub: string;
    username: string;
  };
}

export const Session: React.FC = () => {
  const auth = useAuth();
  const { loading, error, data } = useQuery<SessionData>(gql`
    query session {
      session {
        sub
        username
      }
    }
  `);

  if (loading) return <Text type="secondary">Loading...</Text>;
  if (error) return <Text type="danger">Error: {error.message}</Text>;

  return <div>{data?.session.sub}</div>;
};

Thanks to the useAuth() and useQuery() hooks, this is all it takes to get data from the AWS AppSync GraphQL Data API via React in TypeScript.

What’s next?

Recently, AWS AppSync introduced the AWS AppSync Events API as an alternative to the AWS AppSync GraphQL Subscription API. The GraphQL Data API in this guide is for fetching data; the Events API allows you to publish data to clients using WebSockets.


  • Deploy Serverless Containers to Scaleway with OpenTofu

    December 5 th, 2025 721 Words

    After building multiarch Docker containers for Rust applications, you want to deploy them somewhere. Scaleway’s serverless container platform offers a straightforward way to run containers without managing infrastructure, and OpenTofu provides the infrastructure-as-code tooling. This guide walks through deploying Docker containers to Scaleway using the OpenTofu provider, from setting up the registry to running your container.

  • Multiarch Docker Containers with Rust

    December 5 th, 2025 395 Words

    This guide shows how to build multiarch Docker containers for Rust applications using cargo zigbuild for cross-compilation and Docker build commands for architecture-specific or multiarch container images. Based on the example for Building a Rust API with Rocket and JWT Authentication, this guide provides the next steps.

  • Building a Rust API with Rocket and JWT Authentication

    December 5 th, 2025 1868 Words

    When building backend APIs, JWT authentication is a common requirement. In Rust, you’ve got several web frameworks to choose from, and Rocket is one that makes request handling feel natural with its request guard system. Combining Rocket with JWTiny for JWT validation and JWKServe as a local identity provider gives you a complete setup for development and testing without external dependencies.

  • JWTiny: Minimal JWT Validation for Rust

    December 4 th, 2025 1340 Words

    I was learning Rust with an example project that needed JWT validation. The popular jsonwebtoken crate depends on serde, but I wanted miniserde instead. That constraint led me to build my own validator — handling signature verification, claims validation, and remote key fetching, designed for reuse across requests. JWTiny is the result.

  • JWKServe: A Fake JWT Authentication Service for Local Development

    December 4 th, 2025 990 Words

    When writing backend services that validate JWT access tokens, you run into a frustrating problem: you need a real identity provider just to test your authentication logic. With Cognito, Auth0, or other OpenID Connect providers, spinning up an authentication service for local development or CI pipelines adds unnecessary complexity. You need valid signatures and correct claims, not the provider itself. That’s where JWKServe comes in.

  • Static Website Hosting in Europe with Free Services

    June 10 th, 2025 303 Words

    The AWS European Sovereign Cloud is maybe the most interesting developments of the current cloud computing era; having AWS create a dedicated branch for european workloads is the next big move. But, how do you run a static website without using US vendors at all?

  • AWS CDK: Serverless WebService Blueprints

    January 19 th, 2025 113 Words

    The past days have been full of content about serverless workloads with AWS AppSync, Amazon Cognito, and AWS Fargate. This guide wraps up all scenarios and is your starting point if you want to build modern serverless applications with AWS using the Cloud Development Kit (CDK).