GraphQL & TypeScript Magic
Published: 2024-03-16 21:36:03

---
title: GraphQL & TypeScript Magic
published: true
date: 2024-03-16 16:41:02 UTC
tags: 
canonical_url: https://sean.thenewells.us/graphql-typescript-magic/
---

 ![GraphQL & TypeScript Magic](https://images.unsplash.com/photo-1598621961279-557cec9ba744?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMTc3M3wwfDF8c2VhcmNofDEwfHxlbmdpbmV8ZW58MHx8fHwxNzEwNjA3MTc4fDA&ixlib=rb-4.0.3&q=80&w=2000)

If you haven't heard, there's a new player in town when working with a GraphQL Schema in TypeScript.

🪄 gql.tada ([repo](https://github.com/0no-co/gql.tada))

🌟

"...**with `gql.tada` and [GraphQLSP](https://github.com/0no-co/graphqlsp) you get on-the-fly, automatically typed GraphQL documents with full editor feedback, auto-completion, and type hints!"**  
  
**- gql.tada readme**

What magic is this? Let's see!

We can use an existing GraphQL API and Schema, so we can focus on just a GQL Client workflow. There's a wonderful list of many GQL apis, so pick your favorite, we're using [Trevor Blade's Countries API](https://countries.trevorblades.com/).

You can find the final code [here](https://github.com/snewell92/gql-tada-explore).

## Setup

Create a new astro site, then add some relevant libraries following the gql tada instructions and urql instructions, and our editor and code boilerplate will be ready to go.

```s
mkdir gql-tada-explore
cd gql-tada-explore
pnpm create astro@latest . # choose strictest TS
pnpm astro add react
pnpm add lodash.debounce gql.tada urql @urql/exchange-graphcache
pnpm add -D @0no-co/graphqlsp @types/lodash.debounce
```

_setup instructions_

Update your tsconfig with the following plugin configuration:

```json
 {
   "plugins": [
      {
        "name": "@0no-co/graphqlsp",
        "schema": "https://countries.trevorblades.com/graphql",
        "tadaOutputLocation": "./src/graphql-env.d.ts"
      }
    ]
}
```

_tsconfig.json_

Create a client.ts file to setup urql (cache is optional, but I'd recommend it)

```ts
import { Client, fetchExchange } from 'urql';
import { cacheExchange } from '@urql/exchange-graphcache';

export const client = new Client({
  url: 'https://countries.trevorblades.com',
  exchanges: [cacheExchange({}), fetchExchange],
});
```

_src/client.ts_

In `src/component` folder, create a react component App.tsx. We will be using `<App />` as a pure SPA so we can get going quickly. We will pull in the client we declared above to provide our gql client to all useQuery calls.

```tsx
import { Provider } from 'urql';
import { client } from './client';

export const App = () => {
  return <Provider value={client}>
    <h1>Hello from React</h1>
  </Provider>
}
```

_src/App.tsx_

Use our App as a client SPA in `src/pages/index.astro` as a client only react component.

```html
---
import { App } from "../components/App";
---
<html lang="en">
  <head>
    <meta charset="utf-8" />
      <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
      <meta name="viewport" content="width=device-width" />
      <meta name="generator" content={Astro.generator} />
      <title>Country Search</title>
  </head>
  <body>
    <App client:only="react" />
  </body>
</html>
```

_src/pages/index.astro_

Now, to see the magic of our setup, we're going to create a placeholder SearchBar component in src/components and use it in our App.tsx - it won't do anything yet, but that's where we'll start issuing GQL Queries and use the data.

## Magic

Before proceeding - try to do the following task on your own

✍️

Load all continents from one query, so we have each Continent's code and name available. There are not that many Continents on the planet so it we can eager load them all and do our searches in memory. Try your hand at implementing a search by name in memory and display matching continents in a list. 

Now that we are all set up, we can see the 🪄 magic in action. In our SearchBar, import gql from gql.tada, and start authoring a new query in the tagged template literal and you will now get end to end autocomplete and types. Beautiful.

![GraphQL & TypeScript Magic](https://sean.thenewells.us/content/images/2024/03/image--3-.png)
_type safe completions at query author time_

Now that we are authoring queries it's time to see how this works, and how far the intelligence goes.

## Lints

If we query for continents, including their code and name, but only access the name - we get a TypeScript warning telling us we are, essentially, over fetching!

![GraphQL & TypeScript Magic](https://sean.thenewells.us/content/images/2024/03/image-3.png)

## All The Types

When a project gets going, you often want to create components to display child nodes of a large query, gql.tada gives us some type helpers so we can cut through the GQL and get to the useful TypeScript bits quickly, look at this code example, taken from `src/queries.ts` in the codebase

```ts
import { graphql, type ResultOf } from "gql.tada";

export const getTopLevel = graphql(`
query getTopLevel {
  continents {
    code
    name
  }

  countries {
    code
    name
    continent { code }
    emoji
    capital
    currencies
  }
}`);

type TopLevel = ResultOf<typeof getTopLevel>;

export type Continents = TopLevel["continents"];
export type Countries = TopLevel["countries"];

export type Continent = Continents[0];
export type Country = Countries[0];
```

_extract types_

## Make an App

Now that we've explored some of the powers and how to make use of gql.tada - the full app in the repo has all the code, but take the time to play with it yourself and fulfill the following requirements:

- Search all continents by Code or Name.
- Implement tab and enter and click to select a Continent.
- Show a Continent Card that displays all Countries on the selected Continent.
- When you select a Country, show a Country Card that displays information on the selected Country.
- Construct wikipedia links to relevant articles on Countries and Capitals
- Construct financial links to relevant currency comparisons.

Some further ideas and explorations you may want to consider:

- Find a GQL API you can locally host, to test how gql.tada + urqlg handles **mutations** as I have largely skipped over that here.
- Find a _large_ GQL API and see if the [latest optimizations](https://github.com/0no-co/gql.tada/pulls?q=is:pr+is:merged+perf) will still retain the snappiness (consider pulling the schema into a local file)