Open Weakky opened 5 years ago
Backing types represent the typescript types used to define the root
of a resolver.
So far, they have been either inferred by nexus or defined using the typegenAutoConfig
nexus global configuration.
The current backing type API allows two ways to interact with it:
typegenAutoConfig.sources
API, which basically replaces nexus inferred types by some custom ones.It's either one or the other. The only way to change a backing type if it's based on a source, is by writing a whole new definition.
While it's fine if your backing types were hand-written, it causes problem with generated backing types.
Let's consider the following source
:
// source1.ts
type User = {
id: string
name: string
}
Defined in makeSchema
:
// schema.ts
import { makeSchema } from 'nexus'
makeSchema({
typegenAutoConfig: {
sources: [{
path: require.resolve('./source1.ts'),
alias: "source1"
}]
}
})
Resulting in the following changes in the nexus typegen:
// nexus-typegen.ts
+ import * as source1 from './source1.ts'
export interface NexusGenRootTypes {
- User: { ... }
+ User: source1.User;
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
DateTime: any;
}
Now, the only way to alter source1.User
is to either manually modify source1.User
(if it was hand-written), or to handwrite a whole new User
type and override source1.User
, like so:
// custom-source.ts
import { User as UserBase } from './source1.ts'
type User = UserBase & {
newField: string
}
// schema.ts
import { makeSchema } from 'nexus'
makeSchema({
typegenAutoConfig: {
sources: [
{
path: require.resolve('./source1.ts'),
alias: "source1"
},
{
path: require.resolve('./custom-source.ts'),
alias: "custom_source"
} // Order matters, `custom_source` will override `source1` if two types have the same name
]
}
})
Resulting in the following changes in the nexus typegen:
// nexus-typegen.ts
import * as source1 from './source1.ts'
+ import * as custom_source from './custom-source'
export interface NexusGenRootTypes {
- User: source1.User;
+ User: custom_source.User;
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
DateTime: any;
}
sources
In order for this to work, we need a couple of rules:
⚠️ By default, Nexus should assume that:
⚠️ sources
must comply to these rules, or override them if necessary (More on that later).
Let's consider the following source
:
// source1.ts
type User = {
id: string
name: string
}
Defined in makeSchema
:
import { makeSchema } from 'nexus'
makeSchema({
typegenAutoConfig: {
sources: [{
path: require.resolve('./source1.ts'),
alias: "source1"
}]
}
})
Resulting, in the following changes in the nexus typegen:
+ import * as source1 from './source1.ts'
export interface NexusGenRootTypes {
- User: { ... }
+ User: source1.User;
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
DateTime: any;
}
Now let's define an User
Object Type:
import { objectType } from 'nexus'
const User = objectType({
name: 'User',
definition(t) {
t.id('id')
t.string('name')
t.field('friends', { type: User, list: [true] }) // we make a relation eager by not providing a custom resolver
}
})
Then Nexus would update nexus typegen as so:
import * as source1 from './source1.ts'
export interface NexusGenRootTypes {
- User: source1.User;
+ User: source1.User & { friends: User[] };
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
DateTime: any;
}
👆Because friends
is a relation and has no resolver, we can deduce that the relation is eager, and that the parent thus needs to pass the value down to the children.
Closing now as it turned out not to serve any use-cases
I don't agree it has no use-cases. For caching purpose for example is better option to fetch all data with all relations (at least ids of relation fields). Now the only possible way is to use public field that we don`t want to share via API:
import { idArg, queryField } from 'nexus';
const store = queryField('store', {
type: 'Store',
args: {
id: idArg(),
},
resolve: async (_parent, args, { prisma }) => {
return {
...(await prisma.store({ id: args.id })),
cityId: await prisma
.store({ id: args.id })
.city()
.id(),
};
},
});
export default store;
import { objectType } from 'yoga';
const Store = objectType({
name: 'Store',
definition(t) {
t.id('id');
t.string('name');
// This field should not be visible via schema to users
t.string('cityId');
t.field('city', {
type: 'City',
resolve: (parent, _args, { prisma }) => {
// Now we can grab cache from Redis for example
return prisma.city({ id: parent.cityId });
},
});
},
});
export default Store;
Or is there any other solution for this (to get it properly typed)?
Hey @homoky, we've discussed this further and came to the same conclusion: that there are use-cases. Yours being one indeed. The original proposal tried to solve this using the hide
property, but we think this is not an elegant API.
One way to solve your issue currently is to use typegenAutoConfig.sources
as shown on the issue above.
We'll re-open the issue once we have a better-refined proposal.
In the meantime, feel free to open a new one or comment here if you have ideas to solve your issue better than the current solution 🙏
Thank you for your quick reply. Personaly I like your proposal. The fifth example is what I am looking for exactly. We talked about it on the Slack few months ago and this is far more I`ve expected.
By far I think there are many people who want to use this, even if you don't know about them. The simplest way around is to pass those values from queries
and ask the parent for the value but with // @ts-ignore
, otherwise it will yell that the property does not exists on the parent object. Like this:
import { objectType } from 'yoga';
const User = objectType({
name: 'User',
definition(t) {
t.id('id');
t.field('ratings', {
type: 'Rating',
list: true,
resolve: (parent, _args, { redis }) => {
// @ts-ignore
const ratingIds = parent.ratingIds;
if (ratingIds) {
return redis.getKeyValues(ratingIds)
}
return null
}
});
},
});
export default User;
So this is great step in the right direction.
What are backing types?
Backing types represent the typescript types used to define the
root
of a resolver.Motivation
TBD
Proposal
TBD