Part 2: Querying dApp GraphQL databases in React and TypeScript hosted on The Graph

As we saw in the previous post, it is not ideal to do it in Dart or Flutter. So let's do it in React and TypeScript instead.

Create a React project in TypeScript

First let's create a React project in TypeScript (see doc):

npx create-react-app borrow-stats-react --template typescript

Running npm start runs the project fine. Good start.

Integrate the Apollo client library

Next based on the Apollo doc, we install the client library:

npm install @apollo/client graphql

Run as regular TypeScript

Then we copy/paste the example code that fetches currency data off of their test server and run npm start again.

See the pasted code highlighted in red
Runs perfectly on the first try

Compared with my experience in Dart, it's day and night.

Run as React

Let's keep going and render the data in our React component instead. This time I have to adapt the sample code in https://www.apollographql.com/docs/react/get-started/#3-connect-your-client-to-react slightly, since I am rendering the App in the separate <App> component, not directly in index.tsx.

Also since I am using TypeScript, I need to type the sample code.

Compilation error because TypeScript requires the types for each parameter

They have a page dedicated to the topic: https://www.apollographql.com/docs/react/development-testing/static-typing/. I can either write my own interface or use their code generation tool.

Handwritten interface

Let's start with a manual interface. I already know that currency is a string. I assume rate is a number:

And the page renders right away:

NICE.

Generated interface

To generate the code, the doc suggests apollo's own command-line tool. The command should look like:

sudo npm install -g apollo
apollo client:codegen --target typescript --endpoint https://48p1r2roz4.sse.codesandbox.io src/generated 

Here come the first errors:

% apollo client:codegen --target typescript --endpoint https://48p1r2roz4.sse.codesandbox.io src/generated 
    Error: Cannot find module 'graphql/validation/rules/KnownArgumentNamesRule'
    Require stack:
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@apollo/federation/dist/composition/validate/preNormalization/tagDirecti
    ve.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@apollo/federation/dist/composition/validate/preNormalization/index.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@apollo/federation/dist/composition/validate/index.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@apollo/federation/dist/composition/composeAndValidate.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@apollo/federation/dist/composition/index.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@apollo/federation/dist/index.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/apollo-language-server/lib/providers/schema/file.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/apollo-language-server/lib/providers/schema/index.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/apollo-language-server/lib/project/base.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/apollo-language-server/lib/index.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/lib/commands/client/codegen.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@oclif/config/lib/plugin.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@oclif/config/lib/config.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@oclif/config/lib/index.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@oclif/command/lib/command.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/@oclif/command/lib/index.js
    - ~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/bin/run
    Code: MODULE_NOT_FOUND

Looks like it's a known issue with several suggested workarounds. Here they are in no particular order:

  • use an older version of Node.js
  • install graphql globally
  • run apollo with npx and let it resolve its dependencies by itself.
  • run an older version of apollo, namely 2.33.6 instead of 2.33.9.

I tried to use an older version of apollo, but it didn't work. I also tried to use npx apollo..., but this also doesn't resolve to dependencies that work. Then I installed graphql globally, and this time it worked:

% apollo client:codegen --target typescript --endpoint https://48p1r2roz4.sse.codesandbox.io src/generated     Error: For TypeScript and Flow generators, "output" must be empty or a single directory name, unless the "outputFlat" flag is set.

Although it's an error, it's an error that popped up after the tool actually executed. I don't understand the error though. I also get the same error when I try other combinations of the command:

apollo client:codegen --target=typescript --endpoint=https://48p1r2roz4.sse.codesandbox.io src/generated
apollo client:codegen src/generated --target=typescript --endpoint=https://48p1r2roz4.sse.codesandbox.io

However this one works:

mkdir types
apollo client:codegen types --target=typescript --endpoint=https://48p1r2roz4.sse.codesandbox.io

The output however is underwhelming.

I guess I should pass the schema file instead of letting it introspect the endpoint.

% apollo client:download-schema schema.graphql --endpoint=https://48p1r2roz4.sse.codesandbox.io 
  ✔ Loading Apollo Project
  ✔ Saving schema to schema.graphql

The resulting schema looks promising:

Now the code generating tool still generates only one empty file, even though it says it wrote 3:

% apollo client:codegen types --target=typescript --localSchemaFile=schema.graphql                
  ✔ Loading Apollo Project
  ✔ Generating query files with 'typescript' target - wrote 3 files

With the --outputFlat parameter, it seems to generate all 3 files in the folder.

One thing I also hadn't noticed is that in the src/ folder, there are similar files.

So maybe the previous commands did work after all.

After removing all the generated files and running the first command, it did indeed work.

% apollo client:codegen types --target=typescript --endpoint=https://48p1r2roz4.sse.codesandbox.io
  ✔ Loading Apollo Project
  ✔ Generating query files with 'typescript' target - wrote 3 files

No need to even run apollo client:download-schema after all. Awesome!

Let's replace my hard-coded type information by the generated interface.

VSC lets me add the import automatically. And it still compiles and runs fine.

The Graph

Now will it work on The Graph? Let's try it.

% apollo client:codegen types --target=typescript --endpoint=https://api.thegraph.com/subgraphs/name/aave/protocol-v2
  ⠼ Loading Apollo Project
    Generating query files
.../aave_experiments/borrow-stats-react/src/App.tsx: Cannot query field "rates" on type "Query".
.../aave_experiments/borrow-stats-react/src/index.tsx: Cannot query field "rates" on type "Query".
Validation of GraphQL query document failed
    at Object.validateQueryDocument (~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/node_modules/apollo-language-server/lib/errors/validation.js:40:38)
    at Object.generate [as default] (~/.nvm/versions/node/v15.8.0/lib/node_modules/apollo/lib/generate.js:23:18)
  ✔ Loading Apollo Project
  ✖ Generating query files with 'typescript' target
    → Cannot query field "rates" on type "Query"
    GraphQLError: Cannot query field "rates" on type "Query"

Interesting, it looks like the tool looked at my React code. I thought all it did was query the endpoint. So I rewrote the queries, reran the code generation tool, and compiled the React project again. Here's what I saw in the Debugger console.

So the console.log works fine, but not the query in my <App>. It turns out, I had forgotten to rename data.rates to data.borrows. Now it renders fine.

Unfortunately, the type for amount is not properly typed:

export interface GetBorrows_borrows {
  __typename: "Borrow";
  /**   * tx hash   */
  id: string;
  amount: any;
}

Let's check what client:download-schema finds.

% apollo client:download-schema schema.graphql --endpoint=https://api.thegraph.com/subgraphs/name/aave/protocol-v2
  ✔ Loading Apollo Project
  ✔ Saving schema to schema.graphql

Perhaps it doesn't know what to make of the custom BigInt scalar. Let's add timestamp to compare Apollo's behavior:

Add timestamp to the request made by App.tsx

And the type is correctly inferred as a number:

Looks like apollo client:codegen supports custom scalars:

--passthroughCustomScalars: Use your own types for custom scalars
% apollo client:codegen types --target=typescript --endpoint=https://api.thegraph.com/subgraphs/name/aave/protocol-v2 --passthroughCustomScalars

Now it does keep the BigInt type:

I expected a compile error to pop up and that I'd have to make a type alias like type BigInt = number (see TypeScript's doc). But it looks like BigInt has already been defined somewhere.

I assume that at runtime, it really is just a number and not a BigInt though, whatever that might be.

So in one hour, I've gone further than earlier when I spent the whole day trying to do just that in Dart. It's clear that I'll continue that dashboard in React and TypeScript. And I didn't even have to mess with Aave's subgraph schema nor The Graph's tooling.