Skip to content

TypeScript React Query Hooks

You can generate react-query hooks for interacting with your API from a React website by adding the following options to your TypeSafeApiProject in your .projenrc:

new TypeSafeApiProject({
  library: {
    libraries: [Library.TYPESCRIPT_REACT_QUERY_HOOKS],
  },
  ...
});
TypeSafeApiProject.Builder.create()
        .library(LibraryConfiguration.builder()
                .libraries(Arrays.asList(Library.TYPESCRIPT_REACT_QUERY_HOOKS))
                .build())
        ...
        .build();
TypeSafeApiProject(
    library=LibraryConfiguration(
        libraries=[Library.TYPESCRIPT_REACT_QUERY_HOOKS]
    )
    ...
)

Usage in a React Website

First, make sure you add a dependency on the generated hooks library. This is done automatically if you pass your TypeSafeApiProject into the CloudscapeReactTsWebsite, eg in your .projenrc:

const api = new TypeSafeApiProject({ ... });

new CloudscapeReactTsWebsite({
  ...,
  typeSafeApi: api,
});
TypeSafeApiProject api = new TypeSafeApiProject(TypeSafeApiProjectOptions.builder()
        ...
        .build();

CloudscapeReactTsWebsite.Builder.create()
        ...
        .typeSafeApi(api)
        .build();
api = TypeSafeApiProject(...)

CloudscapeReactTsWebsite(
    ...
    type_safe_api=api
)

Note

If you are not using CloudscapeReactTsWebsite, you can add the dependency manually using api.library.typescriptReactQueryHooks!.package.packageName

Make sure to run npx projen to synthesize your .projenrc changes.

Next, create an instance of the API client in your React Website (making sure to set the base URL and fetch instance). For example:

// NB: client may be named differently if you have tagged your operations
import { DefaultApi } from "myapi-typescript-react-query-hooks";

export const useApiClient = () =>
  useMemo(
    () =>
      new DefaultApi(
        new Configuration({
          basePath:
            "https://example123.execute-api.ap-southeast-2.amazonaws.com/prod",
          fetchApi: window.fetch.bind(window),
        })
      ),
    []
  );

Note that if you are using the Cloudscape React Website with AWS NorthStar and IAM (Sigv4) Auth for your API, you can use NorthStar's useSigv4Client() hook to create an instance of fetch which will sign requests with the logged in user's credentials. For example:

export const useApiClient = () => {
  const client = useSigv4Client();
  return useMemo(
    () =>
      new DefaultApi(
        new Configuration({
          basePath:
            "https://example123.execute-api.ap-southeast-2.amazonaws.com/prod",
          fetchApi: client,
        })
      ),
    [client]
  );
};

Next, instantiate the client provider above where you would like to use the hooks in your component hierarchy (such as above your router). For example:

// NB: client provider may be named differently if you have tagged your operations
import { DefaultApiClientProvider } from "myapi-typescript-react-query-hooks";

const api = useApiClient();

return (
  <DefaultApiClientProvider apiClient={api}>
    {/* Components within the provider may make use of the hooks */}
  </DefaultApiClientProvider>
);

Finally, you can import and use your generated hooks. For example:

import { useSayHello } from "myapi-typescript-react-query-hooks";

export const MyComponent: FC<MyComponentProps> = () => {
  const sayHello = useSayHello({ name: "World" });

  return sayHello.isLoading ? (
    <p>Loading...</p>
  ) : sayHello.isError ? (
    <p>Error!</p>
  ) : (
    <h1>{sayHello.data.message}</h1>
  );
};

Paginated Operations

You can generate useInfiniteQuery hooks instead of useQuery hooks for paginated API operations by configuring the operation in your model. The configuration requires both an inputToken (specifies the input property used to request the next page), and an outputToken (specifies the output property in which the pagination token is returned) to be present.

In Smithy, annotate your paginated API operations with the @paginated trait, making sure both inputToken and outputToken are specified:

@readonly
@http(method: "GET", uri: "/pets")
@paginated(inputToken: "nextToken", outputToken: "nextToken", items: "pets") // <- @paginated trait
operation ListPets {
    input := {
        // Corresponds to inputToken
        @httpQuery("nextToken")
        nextToken: String
    }
    output := {
        @required
        pets: Pets

        // Corresponds to outputToken
        nextToken: String
    }
}

list Pets {
    member: Pet
}

In TypeSpec, use the @extension decorator to add the x-paginated vendor extension.

@get
@route("/pets")
@extension("x-paginated", { inputToken: "nextToken", outputToken: "nextToken" })
op ListPets(@query nextToken?: string): {
  pets: Pet[];
  nextToken?: string;
};`

In OpenAPI, use the x-paginaged vendor extension in your operation, making sure both inputToken and outputToken are specified:

paths:
  /pets:
    get:
      x-paginated:
        # Input property with the token to request the next page
        inputToken: nextToken
        # Output property with the token to request the next page
        outputToken: nextToken
      parameters:
        - in: query
          name: nextToken
          schema:
            type: string
          required: true
      responses:
        200:
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  nextToken:
                    type: string
                  pets:
                    $ref: "#/components/schemas/Pets"

Custom QueryClient

If you wish to customise the react-query QueryClient, pass a custom instance to the client provider, eg:

import { DefaultApiClientProvider } from "myapi-typescript-react-query-hooks";
import { QueryClient } from "@tanstack/react-query";

const queryClient = new QueryClient({ ... });

return (
  <DefaultApiClientProvider apiClient={api} client={queryClient}>
    {/* Components within the provider may make use of the hooks */}
  </DefaultApiClientProvider>
);

Last update: 2024-12-20