Skip to content

A webpack loader to import type-protected codegen results directly from GraphQL documents

Notifications You must be signed in to change notification settings

johnrackles/graphql-let

 
 

Repository files navigation

graphql-let npm version

A webpack loader to import type-protected codegen results directly from GraphQL documents.

Try the Next.js example that integrates graphql-let.

Why it exists

One of the strengths of GraphQL is enforcing data types on runtime. Further, TypeScript and GraphQL Code Generator (graphql-codegen) make it safer by typing data statically, so you can write truly type-protected code with rich IDE assists.

To enhance the development pattern, it's necessary to focus on a more specific use-case than what graphql-codegen allows; binding TypeScript (and assuming the use of Apollo-Client and React). In the way, graphql-let behaves as a subset of graphql-codegen.

graphql-let lets you import graphql-codegen results directly per GraphQL documents with TypeScript type definitions by webpack Loader power.

import { useNewsQuery } from './news.graphql'

const News: React.FC = () => {
	// Typed already️⚡️
	const { data: { news } } = useNewsQuery()
	if (news) return <div>{news.map(...)}</div>
}

How it works

Two things:

  • It runs graphql-codegen inside according to the .graphql-let.yml and pass the generated TypeScript source to the next loader.
  • It generates a file .d.ts.

You may also want only .d.ts before a webpack build to check types. Run graphql-let command to get .d.ts without running webpack.

Get started

This is an example of TypeScript + React + Apollo Client. Please replace the corresponding lines depending on your needs.

1. Install dependencies

Note graphql-let is devDependencies.

npm install -D graphql-let @graphql-codegen/cli @graphql-codegen/plugin-helpers @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo
npm install @apollo/react-common @apollo/react-components @apollo/react-hooks

2. Configure

.graphql-let.yml

Run this command to have a configuration template.

npx graphql-let init
# This will generate .graphql-let.yml

Next add graphql-codegen plugins in it. Please note that you have to generate TypeScript source by the plugins.

Edit it like this:

 schema: lib/type-defs.graphqls
 documents: '**/*.graphql'
 plugins:
   - typescript
+  - typescript-operations
+  - typescript-react-apollo

Available options:

property required type meaning examples
schema ✔︎ string The GraphQL schema info that graphql-let requests introspection to.
  • http://localhost:3000/graphql
  • schema.json
  • schema.graphqls (Note glob is not supported)
All available formats
documents ✔︎ string | string[] The GraphQL documents info of quereis and mutations etc. All the documents have to be separate files. ./queries-and-mutations/**/*.graphql
plugins ✔︎ string[] The plugin names of graphql-codegen.
  • typescript-operations
  • typescript-react-apollo
All available plugins
respectGitIgnore ✔︎ boolean Whether to use .gitignore to ignore like node_modules. It's passed to globby internally. true
config Record<string, boolean | string> The configuration for the plugins. more info These are configured by default.
  • withHOC: false
  • withHooks: true

.gitignore

graphql-let will generate .d.ts files in the same folder of .graphql. Add these lines in your .gitignore.

+*.graphql.d.ts
+*.graphqls.d.ts

webpack.config.ts

The webpack loader also needs to be configured. Note that the content that graphql-let/loader generates is JSX-TypeScript. You have to compile it to JavaScript with an additional loader such as babel-loader.

 const config: Configuration = {
   module: {
     rules: [
+      {
+        test: /\.graphql$/,
+        use: [
+          { loader: 'babel-loader', options: { presets: ['@babel/preset-typescript', '@babel/preset-react'] } },
+          { loader: 'graphql-let/loader' },
+        ]
+      }
     ]
   }
 }

3. Generate type declarations

Run this to generate .d.ts.

npx graphql-let

# This will generate files such as:
#   - src/query.graphql.d.ts
#   - src/schema.graphqls.d.ts

By --config option you can specify the custom path to the .graphql-let.yml. The directory .graphql-let.yml is located at
is the basepath of the relative paths in .grpahql-let.yml. Also, the basepath should be identical to webpack's config.context so the loader can find the config file.

pwd # "/app"
npx graphql-let --config custom/path/.graphql-let.yml

# This will point paths such as:
# /app/custom/path/src/query.graphql.d.ts
# /app/custom/path/src/schema.graphqls.d.ts

You may want to run it everytime calling tsc. Please check your package.json and modify like this.

   "scripts": {
-     "build": "tsc"
+     "build": "graphql-let && tsc"
   },

4. Code more

Enjoy HMR (Hot Module Replacement) of webpack with the generated
react-apollo hooks and IDE code assists.

import { useNewsQuery } from './news.graphql'

const News: React.FC = () => {
    // Already typed⚡️
    const { data: { news } } = useNewsQuery()
    if (news) return <div>{ news.map(...) }</div>
}

Experimental feature: Resolver Types

If:

, graphql-let will generate .graphqls.d.ts to help you type your GraphQL resolvers. Run:

yarn add -D @graphql-codegen/typescript-resolvers

yarn graphql-let

then you will get Resolver type from the schema file.

import { Resolvers } from "./type-defs.graphqls";

const resolvers: Resolvers = {
  Query: {
    // All typed⚡️
    viewer(parent, args, context, info) {
      return { ... }
    },
  }
};

export default resolvers;

graphql-let/schema/loader is also available. It just pass GraphQL Content to the next loader but it updates resolver types in HMR. Set it up as below:

 const config: Configuration = {
   module: {
     rules: [
+      {
+        test: /\.graphqls$/,
+        use: [
+          { loader: 'graphql-tag/loader' },
+          { loader: 'graphql-let/schema/loader' },
+        ]
+      }
     ]
   }
 }

FAQ

So, it's just a graphql-codegen wrapper generating d.ts...?

Yes.

Is this a tool only for React?

No. There are more plugins that also generates .ts from GraphQL documents.

Can I write GraphQL documents in my .tsx as const query = gql`query News{ ... }`;?

Afraid not. You need to have separate files to execute the webpack loader. Besides, typing the value of gql`...` would be impossible.

What's the extension .graphqls? Should I use it for schema and .graphql for documents?

Not exactly, but I'd recommend them. I think using different extensions for schema/documents leads to a more understandable configuration for webpack loaders with fewer pitfalls. Another reason for .graphqls is that it's one of the supported extensions in the internal library.

How to integrate Apollo refetchQueries?

Query document exports DocumentNode named ${QueryName}Document that you can make use of it.

How to import .graphql from another, to import GraphQL Fragment for example?

You can't yet. Please watch the progress.

License

MIT

About

A webpack loader to import type-protected codegen results directly from GraphQL documents

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 96.1%
  • JavaScript 3.9%