fxhash unopiniated typed GraphQL client & common queries ready for consumption.
Install the package
$ pnpm add @fxhash/gql2
Run a query with @apollo/client
:
import { useQuery } from "@apollo/client"
import { Qu_getProjects } from "@fxhash/gql2"
interface Props {}
export function PresentationHeader({}: Props) {
const { data } = useQuery(Qu_getProjects, {
variables: {
where: {
//... typed !
},
},
})
// typed !
const projects = data?.Project
return ...
}
When you update the graphql schema (so the Hasura API), run:
$ npm run codegen
It will update the schema.graphql
and sync the typings from it.
This package uses @graphql-codegen
for generating the GraphQL types automatically based on the schema. The package is targetting http://localhost:8888/v1/graphql
for inspecting the schema, which is the URL under which the main hasura instance is exposed locally using the default config on the monorepo. The docgen is authenticated as a user, to ensure that no sensitive schema data leaks through the package.
@graphql-codegen
doesn't generate typings for making queries, instead it:
- generates schema typings (models, enums, resolvers, mutations...)
- generates typings for GraphQL queries written manually (and exposed by the package)
As such, the directory src/gql/
hosts the different queries, which are exposed for consuming.
.
├── _old -- content of previous "@fxhash/gql" package
├── src/
│ ├── generated -- auto-generated; types, utils,... etc
│ └── gql/ -- pre-made graph ql operations
│ ├── queries
│ ├── mutations
│ └── subscriptions
├── codegen.ts -- @graphql-codegen config
└── graphql.schema.json -- auto-generated
The code generation is divided in 2 steps, designed to optimise and improve the robustness of the process:
-
codegen:schema
: generates a globalschema.graphql
file from the hasura instance running athttp://localhost:8888
-
codegen:typescript
: generates the TS typings and query typings from the graphql schema & the queries insrc/gql
Only tun pnpm run codegen:schema
when you update the Hasura API.
Note The watch mode on dev is only running
codegen:typscript
, but it's watching for changes on both theschema.graphql
file and thesrc/gql
folder.
-
add the query to
./src/gql/queries
(or to themutations
/subscriptions
) -
encapsulate the query with the
graphql()
function from./src/generated/gql.ts
import { graphql } from "@/generated" export const Qu_getProjects = graphql(` query GetProjects($where: Project_bool_exp = {}) { Project(where: $where) { id pricing description state storage } } `)
-
run
$ npm run generate
(note: this requires the GraphQL api to be running), which will generate the types for the query.
@graphql-codegen
generates query typings based on the queries, which ensures proper typing. Instead of words, example:
import { useQuery } from "@apollo/client"
import { Qu_getProjects } from "@fxhash/gql2"
// Qu_getProjects is defined as:
export const Qu_getProjects = graphql(`
query GetProjects($where: Project_bool_exp = {}) {
Project(where: $where) {
id
pricing
description
state
storage
}
}
`)
// example when using useQuery
const { data } = useQuery(Qu_getProjects)
const projects = data?.Project
// projects is of type:
{
__typename?: "Project" | undefined;
id: any;
pricing?: any;
description?: string | null | undefined;
state: any;
storage?: any;
}[] | undefined
// even though the Project entity type is:
/** columns and relationships of "Project" */
export type Project = {
__typename?: 'Project';
/** An object relationship */
author: Account;
authorId: Scalars['uuid']['output'];
blockchain?: Maybe<Scalars['BlockchainNetwork']['output']>;
/** An object relationship */
curator?: Maybe<Account>;
curatorId?: Maybe<Scalars['uuid']['output']>;
description?: Maybe<Scalars['String']['output']>;
id: Scalars['uuid']['output'];
pricing?: Maybe<Scalars['jsonb']['output']>;
/** An array relationship */
projectMedias: Array<ProjectMedia>;
releaseAt?: Maybe<Scalars['timestamp']['output']>;
state: Scalars['ProjectState']['output'];
storage?: Maybe<Scalars['Storage']['output']>;
title: Scalars['String']['output'];
};
As seen in the example, only the properties defined in the query are exposed by typescript.
To define the input/output type of scalars, a setting can be defined in the codegen.ts
config file.
const config: CodegenConfig = {
...,
generates: {
"./src/generated/": {
config: {
scalars: {
// custom scalar field
uuid: {
input: "string",
output: "string",
},
},
},
},
},
}
This will instruct the code generator to type the uuid
scalar properly:
export type Scalars = {
...
uuid: { input: string; output: string; }
};
To learn more about custom GraphQL scalars: The complete GraphQL Scalar Guide
Right now, and because this package is public, its connection with the GraphQL endpoint has been user-authenticated so that it only shows the schema available to regular users. We should also have a package which handles the admin operations, however we don't want such operations to be exposed by the main GraphQL API.