Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
January 12, 2023 04:06 pm GMT

How to produce type-safe GraphQL queries using TypeScript

TL;DR

In this tutorial, you'll learn how to produce type-safe GraphQL queries using TypeScript using some great tools, techniques, and solutions.

Intro

When you're building a company obsessed with making front-end teams' lives better, it's only natural to spend a lot of time thinking about front-end type things.

And that explains a lot about my team.

But we figured that instead of just daydreaming, we'd try to turn our random thought process into something more productive and useful for people like you.

We were recently reflecting on how much front end developers love GQL. It also occurred to us that they also love TypeScript.

So... how amazing would it be if we could combine them?

Livecycle thinking

As it turns out, there are a bunch of tools that can help developers produce type-safe GraphQL queries using TypeScript. In this article, we will explore the cream-of-the-crop tools, techniques, and solutions that can help create a great experience for developers.

Livecycle - the best way for front end devs to collaborate & get PRs reviewed... fast.

[Now, just some quick background on who we are, so you'll understand why we're daydreaming about this kind of thing.

Livecycle is a contextual collaboration tool for front-end development teams. We know how painful it is trying to get multiple stakeholders to review changes before they go live (unclear comments... context switches... different time zones... too many meetings... zero coordination... you know exactly what I'm talking about ) so we built a solution.

Our SDK turns any PR deploy preview environment into a collaborative playground where the team can leave clear review comments in context. By facilitating a contextual, async review we save frontend teams tons of time, money and headaches. (If your team is building a product together - check out how Livecycle can help)]

And now back to our regular scheduled program... Buckle up.

Autocomplete for Writing Queries

Let's start by exploring some techniques for showing autocomplete types when writing queries. Autocomplete is a nice feature that allows developers to see the available schema options for fields, arguments, types, and variables as they write queries. It should work with .graphql files and ggl template strings as well.

figure1

Fig 1: Autocomplete in Action

The editor that you use will determine the process of setting up autocomplete in TypeScript as well as the method of applying the correct configuration.

For example, say that you have a GraphQL endpoint in http://localhost:10008/graphql and you want to enable autocomplete for it. You need to introspect the schema and use it to fill the autocomplete options.

Introspecting the Schema

You need to enable the editor to match the schema and show the allowed fields and types as you write. This is easy if you use Webstorm with the GraphQL plugin. Just click on the tab to configure the endpoint that you want to introspect, and it will generate a graphql-config file that looks something like this:

figure2

Fig 2: The Webstorm GraphQL Plugin

// ./graphql-config.json{  "name": "MyApp",  "schemaPath": "schema.graphql",  "extensions": {    "endpoints": {      "Default GraphQL Endpoint": {        "url": "http://localhost:10008/graphql",        "headers": {          "user-agent": "JS GraphQL"        },        "introspect": true      }    }  }}

Once this step is finished, you will be able to use autocomplete when writing ggl and .graphql queries.

You can also do this using VSCode with the VSCode GraphQL plugin. You may want to download the schema file first:

npm i get-graphql-schema -gget-graphql-schema http://localhost:10008/graphql > schema.graphql

Then, if you use the previous graphql-config.json file, everything should work as expected there as well.

Generating Types Automatically from Gql/Graphql Files

Once you have the autocomplete feature enabled, you should use TypeScript to return valid types for the result data when you write ggl files.

In this case, you need to generate TypeScript types from the GraphQL schema so that you can use the TypeScript Language Server to autocomplete fields in code.

First, you use a tool called @graphql-codegen to perform the schema -> TypeScript types generation. Install the required dependencies like this:

npm i @graphql-codegen/introspection \  @graphql-codegen/TypeScript @graphql-codegen/TypeScript-operations \  @graphql-codegen/cli --save-dev

Then, create a codegen.yml file that contains the code generation configuration:

# ./codegen.ymloverwrite: trueschema: "http://localhost:10008/graphql"documents: "pages/\*\*/\*.graphql"generates:  types/generated.d.ts:    plugins:      - typescript      - typescript-operations      - introspection

Just add the location of the schema endpoint, the documents to scan for queries, and the location of the generated files to this file.

For example, we have a posts.graphql file with the following contents:

# ./posts.graphqlquery GetPostList {posts {nodes {excerptiddatabaseIdtitleslug}}}

Then, we add this task in package.json and run it:

// package.json"scripts": {/*...*/"generate": "graphql-codegen --config codegen.yml",}
npm run generate

This will create ambient types in types/generated.d.ts. Now we can use them in queries:

import postsQuery from "./posts.graphql"import { GetPostListQuery } from "../types/generated"const [response] = useQuery<GetPostListQuery>({ query: postsQuery })

Note: we're able to load .graphql files using import statements with the following webpack rule:

{test: /\.(graphql|gql)$/,exclude: /node_modules/,loader: 'graphql-tag/loader',}

Now, the response data will be properly typechecked when you access the relevant fields even if you dont type anything:

figure3

Fig 3: Using Generated Types in TypeScript

You can also watch the .graphql files and automatically generate the appropriate types without going back and running the same commands again by adding the -w flag. That way, the types will always be in sync as you update them:

//package.json"scripts": {//"generate": "graphql-codegen --config codegen.yml -w,}

Better type inference using typed-document-node

The GraphQL Code Generator project offers a variety of plugins that make it possible to provide a better development experience with Typescript and GraphQL. One of those plugins is the typed-document-node which allows developers to avoid importing the .graphql file and instead use a generated Typescript type for the query result. As you type the result of the operation you just requested you get automatic type inference, auto-complete and type checking.

To use it first you need to install the plugin itself:

npm i @graphql-typed-document-node/core  \  @graphql-codegen/typed-document-node --save-dev

Then include it in the codegen.yml file:

# ./codegen.ymloverwrite: trueschema: "http://localhost:10008/graphql"documents: "pages/\*\*/*.graphql"generates:types/generated.d.ts:plugins:    - typescript    - typescript-operations    - typed-document-node    - introspection

Now run the generate command again to create the new TypedDocumentNode which extends the DocumentNode interface. This will update the types/generated.d.ts file that includes now the following types:

export type GetPostListQueryVariables = Exact<{ [key: string]: never; }>;export type GetPostListQuery = export const GetPostListDocument = 

Now instead of providing the generic type parameter in the query, you just use the GetPostListDocument constant and remove the .graphql import, which allows Typescript to infer the types of the result data. So we can change the index.tsx to be:

index.tsx

import { GetPostListDocument } from "../types/generated.d";const result = useQuery(GetPostListDocument);

You can query the response data which will infer the types for you as you type:

figure4

Overall this plugin greatly improves the development experience when working with GraphQL queries.

Data Fetching Libraries for Web and Node/Deno

Finally, lets explore the best data fetching libraries for Web and Node/Deno. There are three main contenders:

Graphql-Request

This is a minimalist GraphQL client, and its the simplest way to call the endpoint aside from calling it directly using fetch. You can use it to perform direct queries to the GraphQL endpoint without any advanced features like cache strategies or refetching. However, that does not stop you from leveraging the autocomplete and typed queries features:

npm i graphql-request --save
// ./getPosts.tsximport { gql } from "@apollo/client"import { request } from "graphql-request"import { GetPostListQuery } from "../types/generated"export const getPosts = async () => {  const data = await request<GetPostListQuery>(    "http://localhost:10003",    gql`query GetPostList {posts {nodes {excerptiddatabaseIdtitleslug}}}`  )  return data?.posts?.nodes?.slice() ?? []}

For example, you can use the request function to perform a direct query to the GraphQL endpoint and then pass on the generated GetPostListQuery type. While you are typing the query, youll see the autocomplete capability, and you can also see that the response data is typed correctly. Alternatively, if you do not want to pass on the endpoint every time, you can use the GraphQLClient class instead:

import { GraphQLClient } from "graphql-request"import { GetPostListQuery } from "../types/generated"const client = new GraphQLClient("http://localhost:10003")export const getPosts = async () => {  const data = await client.request}

Apollo Client

This is one of the oldest and most feature-complete clients. It offers many useful production grade capabilities like cache strategies, refetching, and integrated states.

Getting started with Apollo Client is relatively simple. You just need to configure a client and pass it around the application:

npm install @apollo/client graphql
// client.tsimport { ApolloClient, InMemoryCache } from "@apollo/client"const client = new ApolloClient({  uri: "http://localhost:10003/graphql",  cache: new InMemoryCache(),  connectToDevTools: true,})

You want to specify the uri to connect to, the cache mechanism to use, and a flag to connect to the dev tools plugin. Then, provide the client with the query or mutation parameters to perform queries:

// ./getPosts.tsximport client from "./client"import { gql } from "@apollo/client"import { GetPostListQuery } from "../types/generated"export const getPosts = async () => {  const { data } = await client.query<GetPostListQuery>({    query: gql`query GetPostList {posts {nodes {excerptiddatabaseIdtitleslug}}}`,  })  return data?.posts?.nodes?.slice() ?? []}

If youre using React, we recommend that you connect the client with the associated provider, as follows:

// ./index.tsximport { ApolloProvider } from "@apollo/client"import client from "./client"const App = () => (  <ApolloProvider client={client}>    <App />  </ApolloProvider>)

Overall, Apollo is a stable and feature-complete client that will cater to all of your needs. Its a good choice.

Urql

This is a more modern client from Formidable Labs. Its highly customizable and very flexible by default. Urql leverages the concept of exchanges, which are like middleware functions that enhance its functionality. It also comes with its own dev tools extension and offers many add-ons. You begin the setup process (which is very similar to those discussed above) as follows:

npm install --save @urql/core graphql
// ./client.tsimport { createClient, defaultExchanges } from "urql"import { devtoolsExchange } from "@urql/devtools"const client = createClient({  url: "http://localhost:10003/graphql",  exchanges: [devtoolsExchange, ...defaultExchanges],})

queries.gql

# ./graphql/queries.gqlquery GetPostList {posts {nodes {excerptiddatabaseIdtitleslug}}}

You need to specify the url to connect to and the list of exchanges to use. Then, you can perform queries by calling the query method with the client and then converting it to a promise using the toPromise method:

// ./index.tsimport client from "./client"import { GetPostDocument } from "../types/generated"async function getPosts() {  const { data } = await client.query(GetPostDocument).toPromise()  return data}

Overall, Urql can be a good alternative to Apollo, as it is highly extensible and provides a good development experience.

In addition, if we're using React app, we can install the @graphql-codegen/typescript-urql plugin and generate higher order components or hooks for easily consuming data from our GraphQL server.

Bottom line & Next Steps with GraphQL and TypeScript

At the end of the day, we just want to make developers happy. So we genuinely hope that you enjoyed reading this article.

For further reading, you can explore more tools in the GraphQL ecosystem by looking through the awesome-graphql list.

Check us out!

If you found this article useful, then you're probably the kind of human who would appreciate the value Livecycle can bring to front end teams.

I'd be thrilled if you installed our SDK on one of your projects and gave it a spin with your team.


Original Link: https://dev.to/livecycle/how-to-produce-type-safe-graphql-queries-using-typescript-52pe

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To