Search code examples
reactjstypescriptamazon-web-servicesaws-appsyncaws-amplify

How do I set typescript types for an AWS amplify GraphQL response in React?


I have a react component in typescript and I want to set the results of an appsync graphql query to a property in state.

import React, { Component } from 'react';
import { API, graphqlOperation } from 'aws-amplify';
import {ListProjectsQuery} from './API'
import {listProjects } from './graphql/queries';

class App extends Component<{}, {
  projects:ListProjectsQuery
}>  {
  state = {
    projects: null
  };
  async componentDidMount() {
    const projects = await API.graphql(graphqlOperation(listProjects));
    this.setState({ projects });
  }

  ...

How do I define the default state property to make this work?

I found a similar problem in the amplify github issues but that solution was in the context of a stateless functional component. I am using a stateful component.

Depending on what I try I seem to get one of three errors.

The code above throws Type 'null' is not assignable to type 'ListProjectsQuery'..

This makes sense so I try and map the shape in state like so:

state = {
    projects: {listProjects: {items: [{name: ''}]}}
  }

which makes it throw Types of property 'projects' are incompatible.

I either get told that a Property does not exist on type 'Observable<object>' or I get told the shape of the default state value is not compatible.

Finally I tried to use an interface like in the example I found:

interface IListProjectQuery {
  projects: ListProjectsQuery;
}

and then I reference the interface

class App extends Component<
  {},
  {
    projects: IListProjectQuery;
  }
> 

and it throws the following error Type '{ projects: null; }' is not assignable to type 'Readonly<{ projects: IListProjectQuery; }>'.

What value do I give the default state property in order for it to make typescript happy?

The ListProjectsQuery import is autogenerated by amplify/appsync codegen, the type alias looks like so:

export type ListProjectsQuery = {
  listProjects:  {
    __typename: "ModelProjectConnection",
    items:  Array< {
      __typename: "Project",
      id: string,
      name: string,
      organisation:  {
        __typename: "Organisation",
        id: string,
        name: string,
      } | null,
      list:  {
        __typename: "ModelListConnection",
        nextToken: string | null,
      } | null,
    } | null > | null,
    nextToken: string | null,
  } | null,
};

Solution

    • You have to define a type that matches the structure of data you are expecting from amplify
    • The second step is to cast your response data as the type you defined and that's it.
    • Your projects property inside your state should be properly typed, projects: IProject[ ] | undefined . You either have an array of projects or it's undefined.

    export type IProject = {
        name: string
    }
    
    export type GetProjectsQuery = {
        listProjects:{
            items: IProject[]
            nextToken: string
        }
    }
    
    const fetchAllProjects = async () => {
        try {
          const result = (await API.graphql(graphqlOperation(queries.listProjects))) as {
            data: GetProjectsQuery
          }
          projects = result.data.listProjects.items
          
    
        } catch (error) {
          //handle error here 
        }