NestJS GraphQL using the same class as input and output type - graphql

After reading this post, I wanted to know what's the right approach assuming a "code first" paradigm. The following seems to work - using Location as both input and output (implicitly generating 2 separate sections in the schema file, derived from the same code).
#ObjectType()
#InputType('LocationInput')
export class Location {
#Field()
lat: number;
#Field()
lon: number;
}
I know there's a lot of no-nos about input and output types sharing code - the fact that inputs have various validations that outputs dont, and so on. But I think Location is a good exception because in the big picture, let's say Location is like String or Int - treated throughout the entire schema as a primitive:
#InputType
class MyInput {
#Field(type => Location)
location: Location
}
#ObjectType
class MyPlace {
#Field(type => Location, { nullable: true })
location?: Location
}
Then I don't see why I should not re-use something as simple as a Location.
Does anyone else have a take on this?

Related

How resolve the right type in GraphQL when using interface and inline fragments

I'm facing a problem where I need to reference a resolved field on the parent from inside the __resolveType. Unfortunately the field I need to reference did not come as part of the original api response for the parent, but from another field resolver, which I would not have though mattered, but indeed it does, so it is undefined.
But I need these fields (in this example the; obj.barCount and obj.bazCount) to be able to make the following query, so I've hit a dead end. I need them to be available in the resolveType function so that I can use them to determine what type to resolve in case this field is defined.
Here's an example:
The graphql query I wish to be able to make:
{
somethings {
hello
... on HasBarCount {
barCount
}
... on HasBazCount {
bazCount
}
}
}
Schema:
type ExampleWithBarCount implements Something & HasBarCount & Node {
hello: String!
barCount: Int
}
type ExampleWithBazCount implements Something & HasBazCount & Node {
hello: String!
bazCount: Int
}
interface Something {
hello: String!
}
interface HasBarCount {
barCount: Int
}
interface HasBazCount {
bazCount: Int
}
Resolvers:
ExampleWithBarCount: {
barCount: (obj) => {
return myApi.getBars(obj.id).length || 0
}
}
ExampleWithBazCount {
bazCount: (obj) => {
return myApi.getBazs(obj.id).length || 0
}
}
Problem:
Something: {
__resolveType(obj) {
console.log(obj.barCount) // Problem: this is always undefined
console.log(obj.bazCount) // Problem: this is always undefined
if (obj.barCount) {
return 'ExampleWithBarCount';
}
if (obj.bazCount) {
return 'ExampleWithBazCount';
}
return null;
}
}
Any ideas of alternative solutions or what am I missing?
Here's a little more about the use case.
In the database we have a table "entity". This table is very simple and only really important columns are id, parent_id, name. type, and then you can of course attach some additional metadata to it.
Like with "entity", types are created dynamically from within the backend management system, and aftewards you can assign a type to your concrete entity.
The primary purpose of "entity" is to establish a hierarchy / tree of nested entities by parent_id and with different "types" (in the type column of entity). There will be some different meta data, but let's not focus on that.
Note: entity can be named anything, and the type can be anything.
In the API we then have an endpoint where we can get all entities with a specific type (sidenote: and in addition to the single type on an entitiy we also have an endpoint to get all entities by their taxonomy/term).
In the first implementation I modeled the schema by adding all the "known" types I had in my specification from the UX'er during development. The tree of entities could be like eg.
Company (or Organization, ..., Corporation... etc)
Branch (or Region, ..., etc)
Factory (or Building, facility, ..., etc)
Zone (or Room, ..., etc)
But this hierarchy is just one way it could be done. The naming of each might be totally different, and you might move some of them a level up or down or not have them at all, depending on the use case.
Only thing that is set in stone is that they share the same database table, will have the type column/field defined and they may or may not have children. The bottom layer in the hierarchy will not have children, but machines instead. The rest of just diffent metadata, which I think we should ignore for to not complicate this further.
As you can see the hierarchy needs to be very flexible and dynamic, so I realized it wasn't a great solution I had begun on.
At the lowest level "Zone" in this case, there will need to be a "machines" field, which should return a list of machines (they are in a "machines" table in the db, and not part of the hierarchy, but simply related with an "entity_id" on the "machines" table.
I had schema types and resolvers for all in the above hierarchy: Organization, Branch, Factory, Zone etc, but I was for the most part just repeating myself, so I thought I could turn to interfaces to try to generalize this more.
So instead of doing
{
companies{
name
branchCount
buildingCount
zoneCount
branches {
name
buildingCount
zoneCount
buildings {
name
zoneCount
zones {
name
machines {
name
}
}
}
}
}
}
And having to add schema/resolvers for all the different namings of the entities, I thought this would work:
{
entities(type: "companies") {
name
... on HasEntityCount {
branchCount: entityCount(type: "branch")
buildingCount: entityCount(type: "building")
zoneCount: entityCount(type: "zone")
}
... on HasSubEntities {
entities(type: "branch") {
name
... on HasEntityCount {
buildingCount: entityCount(type: "building")
zoneCount: entityCount(type: "zone")
}
... on HasMachineCount {
machineCount
}
... on HasSubEntities {
entities(type: "building") {
name
... on HasEntityCount {
zoneCount: entityCount(type: "zone")
}
... on HasMachineCount {
machineCount
}
... on HasSubEntities {
entities(type: "zone") {
name
... on HasMachines {
machines
}
}
}
}
}
}
}
}
}
With the interfaces being:
interface HasMachineCount {
machineCount: Int
}
interface HasEntityCount {
entitiyCount(type: String): Int
}
interface HasSubEntities {
entities(
type: String
): [Entity!]
}
interface HasMachines {
machines: [Machine!]
}
interface Entity {
id: ID!
name: String!
type: String!
}
The below works, but I really want to avoid a single type with lots of optional / null fields:
type Entity {
id: ID!
name: String!
type: String!
# Below is what I want to avoid, by using interfaces
# Imagine how this would grow
entityCount
machineCount
entities
machines
}
In my own logic I don't care what the entities are called, only what fields expected. I'd like to avoid a single Entity type with alot of nullable fields on it, so I thought interfaces or unions would be helpful for keeping things separated so I ended up with HasSubEntities, HasEntityCount, HasMachineCount and HasMachines since the bottom entity will not have entities below, and only the bottom entity will have machines. But in the real code there would be much more than the 2, and it could end up with a lot of optional fields, if not utilizing interfaces or unions in some way I think.
There's two separate problems here.
One, GraphQL resolves fields in a top down fashion. Parent fields are always resolved before any children fields. So it's never possible to access the value that a field resolved to from the parent field's resolver (or a "sibling" field's resolver). In the case of fields with an abstract type, this applies to type resolvers as well. A field type will be resolved before any children resolvers are called. The only way to get around this issue is to move the relevant logic from the child resolver to inside the parent resolver.
Two, assuming the somethings field has the type Something (or [Something], etc.), the query you're trying to run will never work because HasBarCount and HasBazCount are not subtypes of Something. When you tell GraphQL that a field has an abstract type (an interface or a union), you're saying that what's returned by the field could be one of several object types that will be narrowed down to exactly one object type at runtime. The possible types are either the types that make up the union, or types that implement the interface.
A union may only be made up of object types, not interfaces or other unions. Similarly, only an object type may implement an interface -- other interfaces or unions may not implement interfaces. Therefore, when using inline fragments with a field that returns an abstract type, the on condition for those inline fragments will always be an object type and must be one of the possible types for the abstract type in question.
Because this is pseudocode, it's not really clear what business rules or use case you're trying to model with this sort of schema. But I can say that there's generally no need to create an interface and have a type implement it unless you're planning on adding a field in your schema that will have that interface as its type.
Edit: At a high level, it sounds like you probably just want to do something like this:
type Query {
entities(type: String!): [Entity!]!
}
interface Entity {
type: String!
# other shared entity fields
}
type EntityWithChildren implements Entity {
type: String!
children: [Entity!]!
}
type EntityWithModels implements Entity {
type: String!
models: [Model!]!
}
The type resolver needs to check for whether we have models, so you'll want to make sure you fetch the related models when you fetch the entity (as opposed to fetching them inside the models resolver). Alternatively, you may be able to add some kind of column to your db that identifies an entity as the "lowest" in the hierarchy, in which case you can just use this property instead.
function resolveType (obj) {
return obj.models ? 'EntityWithModels' : 'EntityWithChildren'
}
Now your query looks like this:
entities {
type
... on EntityWithModels {
models { ... }
}
... on EntityWithChildren {
children {
... on EntityWithModels {
models { ... }
}
... on EntityWithChildren {
# etc.
}
}
}
}
The counts are a bit trickier because of the variability in the entity names and the variability in the depth of the hierarchy. I would suggest just letting the client figure out the counts once it gets the whole graph from the server. If you really want to add count fields, you'd have to have fields like childrenCount, grandchildrenCount, etc. Then the only way to populate those fields correctly would be to fetch the whole graph at the root.

graphql + mongoose + typescript, how to reduce model definition duplicated

I use mongoose, typescript and graphql to build my app.
I am a full-stack developer.
The problem is I define the fields and types of model FIVE times.
server side:
models/book.ts:
// first time
interface IBook extends mongoose.Document {
title: string;
author: string;
}
// second time
const bookSchema = new mongoose.Schema({
title: String,
author: String
})
const Book: mongoose.Model<IBook> = mongoose.model<IBook>('Book', bookSchema)
graphql/typeDefs.ts
const typeDefs = `
// third time
type Book {
title: String!
author: String!
}
// fourth time
input BookInput {
title: String!
author: String!
}
`
client side:
interfaces/book.ts
// fifth time
interface IBook {
title: string;
author: string;
}
As you can see. the title and author fields and types are defined FIVE times.
There are three main disadvantages:
duplicated
lack of maintainability
inefficient
Is there a way to solve this? I think this is almost a DRY problem.
Here are my thinkings:
universal app - extract some common modules used in client and server side.
make a tool handle this.
make a project generator or command line tool like ng-cli for generating model and types statically which means before the run-time
make model definition decorator or syntax sugar generate model and types dynamically at run-time
We recently ran into this issue, requiring us to maintaining a duplicated Typescript interface alongside our Mongoose schemas (for us the issue was solely server-side as we are not using graphql or client-side Typescript)
We built mongoose-tsgen to address this. It may not handle all cases concerned here, but could be easily bootstrapped to handle your use case.
It reads your Mongoose schemas and generates an index.d.ts file which includes an interface for each document and subdocument. These interfaces can be imported from the mongoose module directly like so:
import { IUser } from "mongoose"
async function getUser(uid: string): IUser {
// `user` is of type IUser
const user = await User.findById(uid)
return user;
}
In case anyone is still wondering,
You can use TypeGraphQL together with Typegoose to create all the schemas within one single class with decorators like this:
#ObjectType()
export class Book{
#Field() #prop({ required: true })
title!: string;
#Field() #prop({ required: true })
name!: string;
}

Using GraphQL Fragment on multiple types

If I have a set of field that is common to multiple types in my GraphQL schema, is there a way to do something like this?
type Address {
line1: String
city: String
state: String
zip: String
}
fragment NameAndAddress on Person, Business {
name: String
address: Address
}
type Business {
...NameAndAddress
hours: String
}
type Customer {
...NameAndAddress
customerSince: Date
}
Fragments are only used on the client-side when making requests -- they can't be used inside your schema. GraphQL does not support type inheritance or any other mechanism that would reduce the redundancy of having to write out the same fields for different types.
If you're using apollo-server, the type definitions that make up your schema are just a string, so you can implement the functionality you're looking for through template literals:
const nameAndAddress = `
name: String
address: Address
`
const typeDefs = `
type Business {
${nameAndAddress}
hours: String
}
type Customer {
${nameAndAddress}
customerSince: Date
}
`
Alternatively, there are libraries out there, like graphql-s2s, that allow you to use type inheritance.
Don't know if it was not available at this question time, but I guess interfaces should fit your needs

Why GraphQL `implements` need to duplicate the fields, is that mandatory? If yes, what is the underlying reasons?

Why GraphQL implements keyword need to duplicate the fields, is that mandatory? Like the examples in the document:
enum Episode { NEWHOPE, EMPIRE, JEDI }
interface Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
}
type Human implements Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
homePlanet: String
}
type Droid implements Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
primaryFunction: String
}
If yes, what is the underlying reasons?
Coz If I have to duplicate, if I change then i need to change everywhere...
Yes, this is mandatory according to the current spec:
The object type must include a field of the same name for every field defined in an interface.
It allows you to specify more precisely types of fields in the derived type. Fields can be changed to be not-null or to some specific subtype of an interface or a union. Taking your example, let's assume that humans are friends only with other humans and the same for droids. Then the following schema is valid.
interface Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
}
type Human implements Character {
id: String
name: String
friends: [Human] # <- notice Human here
appearsIn: [Episode]
homePlanet: String
}
type Droid implements Character {
id: String
name: String
friends: [Droid!]! # <- specified Droid here + added not null
appearsIn: [Episode]
primaryFunction: String
}
The object field may include additional arguments not defined in the interface field (but any additional argument must not be required).
Check out the spec for more details: Object type validation
Yes it's mandatory. If it helps, think of it as analogous to Java classes and interfaces. The interfaces have the type signatures, but cannot have an implementation. It is in the classes where you write out all the implementation, and the type signatures get repeated. This gives you the ability to choose subtypes or covariant types in the types signatures, so they might not be exactly the same.
Now suppose you are creating a GraphQL schema with the JavaScript objects from graphql-js. The interfaces are simply field names and types. The Object Type definitions themselves have the "implementation," or the resolve, resolveType, and other properties that actually make it an executable schema.
Your example, however, uses the schema language instead, which has no "implementation" at all. So they pretty much are an exact repetition of one another. You don't necessarily need to spell it all out every time, you could use string interpolation to share parts of the interface.
const characterFields = `
id: String
name: String
friends: [Character]
appearsIn: [Episode]
`
const typeDefs = `
interface Character {
${characterFields}
}
type Human Implements Character {
${characterFields}
homePlanet: String
}
`
EDIT: Analogous to the Java comparison, the fields may not be exactly the same type. As RomanHotsiy pointed out, you can make the types non-nullable or use subtypes of polymorphic types.

Why does TypeScript use "type annotations" instead of keywords like other strongly-typed languages?

TypeScript's type system uses type annotations, which look like this:
private currentState: IAppState;
I read this as "currentState is an IAppState"
Why does this syntax make more design sense than this, which is what I'm used to from other strongly-typed languages?
private IAppState currentState;
I understand that TypeScript is supposed to be an extension of JavaScript syntax, but in that case, what made the designers of TypeScript choose the current syntax over this:
private IAppState: currentState;
A friend and I were discussing it and he raised a good example:
type student_detail = {
id: number,
name: string,
percentage: number,
clg_data: {
college_id: number,
college_name: string
}
}
This almost looks like standard JS. Now imagine what you'd have with C style syntax:
struct student_detail {
number id;
string name;
number percentage;
// structure within structure
struct {
number college_id;
string college_name;
} clg_data;
}
When it comes to structures in structures the only way to really do it is by splitting them out into separate structures:
struct student_college_detail {
number college_id;
string college_name;
};
struct student_detail {
number id;
string name;
number percentage;
struct student_college_detail clg_data;
}
This is fine but doesn't really fit into JavaScript. This is likely the reason why they stuck with the var: type syntax. It would maybe be nice if you could do either though. :)

Resources