type-graphql
type-graphql copied to clipboard
[FR] Default naming strategy
Is your feature request related to a problem? Please describe. I'm currently frustrated at doing this repetitively:
...
@Field(() => Boolean, { name: 'have_children' })
get haveChildren (): Promise<boolean> {
return (async () => ((await this.children) || []).length > 0)()
}
@Field(() => Boolean, { name: 'is_children' })
get isChildren (): Promise<boolean> {
return (async () => (await this.parent) !== undefined)()
}
@Field(() => Boolean, { name: 'is_root' })
get isRoot (): Promise<boolean> {
return (async () => (await this.parent) === undefined)()
}
...
And this:
...
@Query(() => Submission, {
nullable: true,
name: 'submission_feed'
})
async feed (@Arg('id') id: number): Promise<Submission> {
return this.submissions.findOneOrFail({ id }).fail('No such submission')
}
@Authorized() @Mutation(() => Submission, { name: 'submission_submit' })
async submit (
@Args() { url, description }: SubmitArgs,
@Ctx('user') submitter: User
): Promise<Submission> {
// secret code...
}
...
So we're basically using camelCase in server code but we also decided to use snake_case for the name convention of our GraphQL schema so that we could get a better separation of namespaces (because GraphQL doesn't support nested queries yet, we had to resort back to oldschool C-style OOP namings)
Describe the solution you'd like
Let there be a default naming strategy transformer in the method buildSchema
that is undefined by default. It will be a/an (a)synchronous function with arguments (name: string, type: 'field' | 'fieldResolver' | 'resolver', target?: Function) => string
, then after the basic schema skeleton is built, we do a ~DFS~ simple lookup of metadata storage data on that, then we will convert the name into various cases, for example, by using blakeembrey/change-case.
If a name
property is already denoted on the decorator metadata, name transformation will be ignored.
Alternative solution It seems like we could also do this externally ourselves by traversing the schema ourselves, although it will be harder to determine contexts and types and such due to loss of metadata.
It could also be manipulated before schema creation by rewriting all fields in the metadata storage
Thanks for sharing your idea! 😃
I like it, it's implementable, so I've added it to the features list 😉
because GraphQL doesn't support nested queries yet
What do you mean? It might be not semantically correct but you can implement queries as a field resolvers:
query NamespaceExamples {
submission {
feed {
isRoot
haveChildren
}
}
}
Although it won't work with mutations correctly (no guaranteed sequential execution) 😞
I would also extend your proposal to namespaces:
@Resolver({ namespace: "submission" })
class SubmissionResolver {
@Query(() => Submission)
async feed (@Arg('id') id: number): Promise<Submission> {
// secret code...
}
@Authorized()
@Mutation(() => Submission)
async submit (
@Args() { url, description }: SubmitArgs,
@Ctx('user') submitter: User
): Promise<Submission> {
// secret code...
}
}
type Query {
submission_feed(id: Float!): Submission
}
type Mutation {
submission_submit(url: URL!, description: String!): Submission
}
await buildSchema({
resolvers: [SubmissionResolver],
namespaceJoin: "PascalCase" | "camelCase" | "snake_case" | NamespaceJoinFn,
});