[Plugin Request] Add typescript-react-query plugin #4792
-
Is your feature request related to a problem? Please describe. Describe the solution you'd like Describe alternatives you've considered Additional context Some things to consider:
I am currently putting things together for the latter option with these: Open for suggestions. |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 9 replies
-
if someone needs a starting point, I made a simple plugin for my use case. The graphql function copied from TanStack/query#301 (comment) custom plugin // react-query-codegen.js
const { concatAST, Kind, visit } = require('graphql');
const {
ClientSideBaseVisitor,
indentMultiline,
} = require('@graphql-codegen/visitor-plugin-common');
module.exports = {
plugin: (schema, documents, config) => {
const allAst = concatAST(documents.map((v) => v.document));
const allFragments = [
...allAst.definitions
.filter((d) => d.kind === Kind.FRAGMENT_DEFINITION)
.map((fragmentDef) => ({
node: fragmentDef,
name: fragmentDef.name.value,
onType: fragmentDef.typeCondition.name.value,
isExternal: false,
})),
...(config.externalFragments || []),
];
const visitor = new TypeScriptDocumentNodesVisitor(schema, allFragments, config, documents);
const visitorResult = visit(allAst, { leave: visitor });
return {
prepend: visitor.getImports(),
content: [
visitor.fragments,
...visitorResult.definitions.filter((t) => typeof t === 'string'),
visitor.queries,
].join('\n'),
};
},
};
class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor {
constructor(schema, fragments, config, documents) {
super(schema, fragments, config, {}, documents);
}
operations = [];
buildOperation(
node,
documentVariableName,
operationType,
operationResultType,
operationVariablesTypes
) {
this.operations.push({
node,
documentVariableName,
operationType,
operationResultType,
operationVariablesTypes,
});
return null;
}
get queries() {
return `
import { QueryConfig, useQuery } from 'react-query';
function graphql<Result, Variables>(query: string, variables?: Variables) {
return async (): Promise<Result> => {
const res = await fetch(process.env.GRAPHQL_API, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
authorization: \`Bearer \${localStorage.getItem('token')\}\`,
},
credentials: 'include',
body: JSON.stringify({ query, variables }),
});
const json = await res.json();
if (json.errors) {
const { message } = json.errors[0] || 'Error..';
throw new Error(message);
}
return json.data;
}
}
${this.operations
.map(({ node, operationResultType, documentVariableName, operationVariablesTypes }) => {
const optionalVariables =
!node.variableDefinitions ||
node.variableDefinitions.length === 0 ||
node.variableDefinitions.every(
(v) => v.type.kind !== Kind.NON_NULL_TYPE || v.defaultValue
);
const variables = `variables${optionalVariables ? '?' : ''}: ${operationVariablesTypes}`;
return `
export const use${operationResultType} = (${variables}, options?:QueryConfig<${operationResultType}>) =>
useQuery<${operationResultType}, ${operationVariablesTypes}>(
['${node.name.value}', variables],
graphql<${operationResultType}, ${operationVariablesTypes}>(${documentVariableName}, variables),
options
)`;
})
.map((s) => indentMultiline(s, 2))
.join(',\n')}`;
}
} Config // config.yml
overwrite: true
strict: true
config:
avoidOptionals:
field: true
scalars:
_text: string[]
bigint: number
date: string
float8: number
inet: string
numeric: number
timestamptz: string
uuid: string
namingConvention:
typeNames: change-case#pascalCase
transformUnderscore: true
hooks:
afterAllFileWrite:
- prettier --write
schema: graphql.introspect.json
generates:
src/api.ts:
config:
onlyOperationTypes: true
enumsAsConst: true
addDocBlocks: false
preResolveTypes: true
skipTypename: true
documentMode: 'string'
documents:
- src/**/*.gql
plugins:
- typescript
- typescript-operations
- react-query-codegen How to use it // index.ts
import React from 'react';
import ReactDOM from 'react-dom';
import { useGetBanksQuery } from './api';
export function App() {
const query1 = useGetBanksQuery({ limit: 3 });
const query1Clone = useGetBanksQuery({ limit: 3 });
const query2 = useGetBanksQuery({ limit: 2 });
// expected only two requests since query1 and query1Clone have same cache key
return (
<>
<div>{JSON.stringify(query1.data?.banks)}</div>
<div>{JSON.stringify(query1Clone.data?.banks)}</div>
<div>{JSON.stringify(query2.data?.banks)}</div>
</>
);
}
ReactDOM.render(<App />, document.getElementById('root')); |
Beta Was this translation helpful? Give feedback.
-
Thanks @balazsorban44 ! |
Beta Was this translation helpful? Give feedback.
-
I was thinking a bit more about that, and here are my thoughts:
|
Beta Was this translation helpful? Give feedback.
-
WIP here: #5107 |
Beta Was this translation helpful? Give feedback.
-
Merged and published, you can now use it! |
Beta Was this translation helpful? Give feedback.
Merged and published, you can now use it!
@graphql-codegen/[email protected]
:)