You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is my current implementation, it relies on the assumption every node query will include a fragment on the GraphQL object Relay is trying to fetch, for example:
So even though Prisma doesn't have global identifiers I can understand what model to fetch using the GraphQL query AST as source.
import{DefinitionNode,SelectionNode,parse}from"graphql";import{Arg,Ctx,Query,Resolver}from"type-graphql";import{prisma}from"@/prisma";import{RelayNode}from"@/types/relay-node";import{Prisma}from"@generated/prisma-client";import{GraphQLContext}from"..";import{CreatorNode,ProductPriceNode}from"./creators";/** * Node Resolver */
@Resolver()exportclassNodesResolver{
@Query(()=>RelayNode,{nullable: true})asyncnode(@Arg("id")id: string, @Ctx()ctx: GraphQLContext){if(ctx.params.query==null){returnnull;}constfragmentTypes=extractFragmentTypes(ctx.params.query);constfirstFragmentType=fragmentTypes[0];if(firstFragmentType==null){returnnull;}constmodelName=lowerCaseFirstChar(firstFragmentType.replace(/Node$/,""),)asTPrismaModels;// @ts-expect-error This is a dynamic property accessconstnode=awaitprisma[modelName].findUnique({where: { id }});returnnode;}}exportconstresolvers=[NodesResolver]asconst;/** * Utilities */functionextractFragmentTypes(query: string): string[]{constfragmentTypes: string[]=[];constast=parse(query);// Traverse the AST to find fragment definitionsfunctionextractFragments(node: DefinitionNode|SelectionNode){console.log(node);if(node.kind==="InlineFragment"&&node.typeCondition){fragmentTypes.push(node.typeCondition.name.value);}if("selectionSet"innode&&node.selectionSet){node.selectionSet.selections.forEach((selection)=>{extractFragments(selection);});}}ast.definitions.forEach((definition)=>{extractFragments(definition);});returnfragmentTypes;}functionlowerCaseFirstChar(s: string): string{returns[0].toLowerCase()+s.slice(1);}typeLowercaseFirstChar<Sextendsstring>=Sextends `${infer First}${infer Rest}` ? `${Lowercase<First>}${Rest}` : S;typeTPrismaModels=LowercaseFirstChar<keyoftypeofPrisma.ModelName>;
Unfortunately this errors with:
ERR Error: Cannot resolve type for interface Node! You need to return instance of object type class, not a plain object!
A workaround I found is to manually instantiate the correct instance with this code:
letinstance;switch(modelName){case"creator":
instance=newCreatorNode();break;case"productPrice":
instance=newProductPriceNode();break;default:
thrownewError(`Unknown model: ${modelName}, make sure to implement it in the node resolver.`,);}for(constkeyinnode){instance[keyaskeyoftypeofinstance]=node[key];}returninstance;
The problem is I have to manually list every single node object type here, I would like a general purpose solution.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
I'm trying to implement a Relay Node interface resolver.
This is my current implementation, it relies on the assumption every node query will include a fragment on the GraphQL object Relay is trying to fetch, for example:
So even though Prisma doesn't have global identifiers I can understand what model to fetch using the GraphQL query AST as source.
Unfortunately this errors with:
A workaround I found is to manually instantiate the correct instance with this code:
The problem is I have to manually list every single node object type here, I would like a general purpose solution.
Does anyone know how to simplify this?
Beta Was this translation helpful? Give feedback.
All reactions