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:
query {
robots {
... on RobotInterface {
name
}
... on HumanoidRobot {
height
}
... on ScaraRobot {
axes
}
}
}
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' in value) {
return HumanoidRobot;
}
return ScaraRobot;
},
})
export abstract class RobotInterface {
@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',
})
export class ScaraRobot implements RobotInterface {
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 simpleimplements: [RobotInterface]
. This in fact wasted my precious 2 hours. So it should beimplements: () => [RobotInterface]
.
Now it is time to define your union type:
import { createUnionType } from '@nestjs/graphql';
import { HumanoidRobot } from './humanoid.type';
import { ScaraRobot } from './scara.type';
export const UnionOfRobots = createUnionType({
name: 'UnionOfRobots',
description: 'Union of robots fields',
types: () => [HumanoidRobot, ScaraRobot] as const,
resolveType: (value) => {
if ('height' in value) {
return HumanoidRobot;
}
return ScaraRobot;
},
});
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) {
return HumanoidRobot;
}
return ScaraRobot;
},
})
export abstract class RobotInterface {
// ...
@Field(() => RobotType, { description: 'Type of the robot' })
type: RobotType;
// ...
}
And of course you need to register the enum type first. Then you can do the same thing in the createUnionType
helper function.
Finally we need a resolver:
// ...
@Query(() => [UnionOfRobots], {
description: 'Get all robots',
})
robots(): Array<typeof UnionOfRobots> {
return this.appService.getRobots();
}
// ...
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:
kasir-barati
/
graphql-js-ts
Where you can learn all about GraphQL and its intricacies
Caution
Keep this file in sync with index.md
.
GraphQL
Tip
Just for those curious minds who always jump from one branch to another like mine:
You can find a good definition usually in glossary.
- Intro.
- Data types.
- A simple todo app written with GraphQL + ReactJS + Relay
- Queries and mutations in depth.
- Let's breakdown the query language a bit more.
-
Functions provided by
graphql
. - Document your GraphQL service API.
- GraphQL request lifecycle
- Code-first approach.
- Auth.
- How to query information about a GraphQL schema.
- Improve developer experience
- Security in GraphQL.
-
NestJS
- …
Note
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 theapps/interfaces-unions-e2e
.
Instagram: https://www.instagram.com/node.js.developers.kh/
Facebook: https://www.facebook.com/kasirbarati
X: https://x.com/kasir_barati
YouTube: https://www.youtube.com/@kasir-barati
GitHub: https://github.com/kasir-barati/
Dev.to: https://dev.to/kasir-barati
LinkedIn: https://linkedin.com/in/kasir-barati
Top comments (0)