About: Love to work with cutting edge technologies and on my journey to learn and teach. Having a can-do attitude and being industrious are the reasons why I question the status quo an venture in the unknown
Location:
Bremen, Germany
Joined:
Mar 13, 2021
GraphQL + NestJS + Interfaces + Unions
Publish Date: Mar 22
0 0
So guys I was really confused by the NestJS's doc as to ho you can have an API which can be invoked like this:
So in others words I wanted to not repeat myself by having an interface and I needed to also prevent any fields from other types leaking to another one.
The solution was simple, first I had to define my RobotInterface, HumanoidRobot object type and ScaraRobot:
app/types/robot-interface.type.ts
import{Field,ID,InterfaceType}from'@nestjs/graphql';import{HumanoidRobot}from'./humanoid.type';import{ScaraRobot}from'./scara.type';@InterfaceType({description:"'Common fields, available for all robots',"resolveType:(value)=>{if ('height'invalue){returnHumanoidRobot;}returnScaraRobot;},})exportabstractclassRobotInterface{@Field(()=>ID,{description:"'ID of the robot' })"id:string;@Field(()=>String,{description:"'Name of the robot' })"name:string;@Field(()=>String,{description:"'The COLLADA image of the robot',"})colladaImage:string;@Field(()=>String,{description:"'Description of the robot' })"description:"string;"@Field(()=>String,{description:"'Model number of the robot' })"modelNumber:string;}
app/types/scara.type.ts
import{Field,Float,Int,ObjectType}from'@nestjs/graphql';import{RobotInterface}from'./robot-interface.type';@ObjectType({implements:()=>[RobotInterface],description:""'Scara robot, specialized for tasks requiring high precision & speed',})exportclassScaraRobotimplementsRobotInterface{id:string;name:string;colladaImage:string;description:"string;"modelNumber:string;@Field(()=>Int,{description:"'Number of axes',"})axes:number;@Field(()=>Float,{description:"'Payload capacity in kg',"})payload:number;}
And something similar to this for the HumanoidRobot.
Caution
the implements option should be a callback function and not a simple implements: [RobotInterface]. This in fact wasted my precious 2 hours. So it should be implements: () => [RobotInterface].
And note that the resolveType will help your NestJS app to decide which type it should use, TBH I saw in a lot of places people use enums. In other word they add an extra field to the RobotInterface and call it type and decide based on that field. I guess that would be easier to have compare to relying on the presence of a field.
So feel free to do that instead of this, it would look like this:
@InterfaceType({description:'Common fields, available for all robots',resolveType:(value)=>{if (value.type===RobotType.HUMANOID){returnHumanoidRobot;}returnScaraRobot;},})exportabstractclassRobotInterface{// ...@Field(()=>RobotType,{description:'Type of the robot'})type:RobotType;// ...}
// ...@Query(()=>[RobotInterface],{description:'Get all robots',})robots():Array<RobotInterface>{returnthis.appService.getRobots();}// ...
Unions
This is not really any different that interfaces, just use the createUnionType helper function and pass your classes, then specify on which type it should return what:
import{createUnionType}from'@nestjs/graphql';import{HumanoidRobot}from'./humanoid.type';import{ScaraRobot}from'./scara.type';exportconstUnionOfRobots=createUnionType({name:'UnionOfRobots',description:'Union of robots fields',types:()=>[HumanoidRobot,ScaraRobot]asconst,resolveType:(value)=>{if ('height'invalue){returnHumanoidRobot;}returnScaraRobot;},});
And then specify that as the return type of your query, or mutation.
Support Me
You can support me by liking this post, reading my other posts and giving my repo a star on GitHub.
As simple as that. Need the code itself? Do not worry I've got you covered:
This is a monorepo, so I have tons of other apps, but the one you're interested in at the moment is apps/interfaces-unions. BTW If you like to learn how to write e2e tests for you NestJS app look at the apps/interfaces-unions-e2e.