Closed ZachHaber closed 3 years ago
sorry but I don't have any info about fragmentReplacements
can you give me an example about what you try to do
fragmentReplacements
are a piece of information that are added onto the schema for individual fields.
https://github.com/maticzav/graphql-middleware/blob/master/src/fragments.ts
For example in the tutorial I was going through, the resolver for User's email requires the id
property to be defined:
export const User: IResolverObject<
UserType,
Context
> = {
email: {
fragment: `fragment userId on User { id }`,
resolve(parent: UserType, args, { user }, info) {
if (user?.id && parent.id !== user?.id) {
return null;
}
return parent.email;
},
},
}
You can get the query fragments if included on the schema from schema.fragmentReplacements
, but it might not exist - The only library I've seen that has that easily included is graphql-middleware. So, I'm not entirely sure if this is worth it over just making notes in your schema that if you want x field, also pull y field.
Yes, I understand now. We already have a solution for that without using any middleware package You can pass a default field to add even if the client request not included
https://paljs.com/plugins/select#constructor look to options here
And look to this issue #180 for advanced use
The main downside is that you have to set up the default fields in every root query/mutation at that point especially since due to the nature of graphql, you can usually get from one type to a completely different one.
I.e. Post->Comments->User. Which would require the default fields to be set up in Posts() toot query as well for it to be truly accurate
I've figured out how to use the new defaultFields function form and class extension to get much closer to what I needed without having to completely rewrite the PrismaSelect class.
The only thing I was missing here was the ability to see what the parent model was, but there's a close enough approximation here that it's not needed.
I feel like it's a fairly clean way to handle it, and all of my default replacements are in one centralized spot.
import { PrismaSelect as OriginalPrismaSelect } from "@paljs/plugins";
type ConstructorArgs = ConstructorParameters<typeof OriginalPrismaSelect>;
export const DefaultSelect = Symbol("defaultSelect");
export const ParentModel = Symbol("parentModel");
type Properties = Record<string, boolean>;
type ExtraData = {
/**
* Normal lookup - If property on Model is being selected, grab other properties as those are needed for it
*/
[property: string]: Properties;
/**
* These properties are always needed on a model
*/
[DefaultSelect]?: Properties;
/**
* These properties are needed
*/
[ParentModel]?: { [model: string]: Properties };
};
type ExtraDataByModel = {
[model: string]: ExtraData;
};
const Fragments: ExtraDataByModel = {
// On User model, if email is selected, we need id as well!
User: { email: { id: true } },
// On Post model, if the parent is a User, then we need published and userId in the Post!
Post: { [ParentModel]: { User: { published: true, userId: true } } },
};
export class PrismaSelect extends OriginalPrismaSelect {
constructor(info: ConstructorArgs[0], options?: ConstructorArgs[1]) {
const originalDefaultFields = options?.defaultFields;
// The base is the original defaults as those are important to keep
const newDefaultFields = { ...originalDefaultFields };
// Keep track of the last model seen as a decent approximation of the parent
let lastModel: string = "";
// Map over all fragments to include them
for (const [model, values] of Object.entries(Fragments)) {
newDefaultFields[model] = (select) => {
const originalDefaults = originalDefaultFields?.[model];
let defaultFields: { [key: string]: boolean } = {
// Fold in the original defaults passed in so as not to break original functionality
...(typeof originalDefaults === "function"
? originalDefaults(select)
: originalDefaults),
// Fold in the defaults for the model
...values[DefaultSelect],
// Fold in what is necessary based on the likely parent model
...values[ParentModel]?.[lastModel],
};
lastModel = model;
for (const key of Object.keys(values)) {
if (select[key]) {
defaultFields = { ...defaultFields, ...values[key] };
}
}
return defaultFields;
};
}
super(info, { ...options, defaultFields: newDefaultFields });
}
}
I was using this to set up a prisma application, and one of the things I realized is that I couldn't use fragmentReplacements in the API.
PrismaSelect would be a good place to be able to resolve those to be able to make sure that I have enough information on the field resolvers to properly resolve them. As the fragmentReplacement values end up on the
info.schema
objectWhat are your thoughts on this?