Skip to content

Commit 456da16

Browse files
committed
Add support for custom names for root types
1 parent 84637c6 commit 456da16

File tree

8 files changed

+213
-21
lines changed

8 files changed

+213
-21
lines changed

.changeset/great-rules-try.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@pothos/plugin-federation": minor
3+
"@pothos/plugin-sub-graph": minor
4+
"@pothos/converter": minor
5+
"@pothos/core": minor
6+
---
7+
8+
Add support for custom names on Root types

packages/converter/src/index.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,13 @@ export default class PothosConverter {
167167

168168
if (type instanceof GraphQLObjectType) {
169169
switch (type.name) {
170-
case 'Query':
170+
case this.schema.getQueryType()?.name ?? 'Query':
171171
this.queryType(type);
172172
break;
173-
case 'Mutation':
173+
case this.schema.getMutationType()?.name ?? 'Mutation':
174174
this.mutationType(type);
175175
break;
176-
case 'Subscription':
176+
case this.schema.getSubscriptionType()?.name ?? 'Subscription':
177177
this.subscriptionType(type);
178178
break;
179179
default:
@@ -204,6 +204,11 @@ export default class PothosConverter {
204204
this.sourcefile.addStatements((writer) => {
205205
writer.writeLine('builder.queryType({');
206206
writer.indent(() => {
207+
if (type.name !== 'Query') {
208+
writer.write('name:');
209+
writer.quote(type.name);
210+
writer.writeLine(',');
211+
}
207212
this.writeDescription(writer, type);
208213
this.writeObjectShape(writer, type);
209214
});
@@ -215,6 +220,11 @@ export default class PothosConverter {
215220
this.sourcefile.addStatements((writer) => {
216221
writer.writeLine('builder.mutationType({');
217222
writer.indent(() => {
223+
if (type.name !== 'Mutation') {
224+
writer.write('name:');
225+
writer.quote(type.name);
226+
writer.writeLine(',');
227+
}
218228
this.writeDescription(writer, type);
219229
this.writeObjectShape(writer, type);
220230
});
@@ -226,6 +236,11 @@ export default class PothosConverter {
226236
this.sourcefile.addStatements((writer) => {
227237
writer.writeLine('builder.subscriptionType({');
228238
writer.indent(() => {
239+
if (type.name !== 'Subscription') {
240+
writer.write('name:');
241+
writer.quote(type.name);
242+
writer.writeLine(',');
243+
}
229244
this.writeDescription(writer, type);
230245
this.writeObjectShape(writer, type);
231246
});
@@ -442,7 +457,7 @@ export default class PothosConverter {
442457
`resolve: (parent, args, context, info) => { throw new Error('Not implemented') },`,
443458
);
444459

445-
if (type.name === 'Subscription') {
460+
if (type.name === (this.schema.getSubscriptionType()?.name ?? 'Subscription')) {
446461
writer.writeLine(
447462
`subscribe: (parent, args, context, info) => { throw new Error('Not implemented') },`,
448463
);

packages/core/src/builder.ts

+31-9
Original file line numberDiff line numberDiff line change
@@ -230,15 +230,19 @@ export class SchemaBuilder<Types extends SchemaTypes> {
230230
): QueryRef<Types> {
231231
const [options = {}, fields] = args;
232232

233-
const ref = new QueryRef<Types>('Query', {
233+
const ref = new QueryRef<Types>(options.name ?? 'Query', {
234234
kind: 'Query',
235235
graphqlKind: 'Object',
236-
name: 'Query',
236+
name: options.name ?? 'Query',
237237
description: options.description,
238238
pothosOptions: options as unknown as PothosSchemaTypes.QueryTypeOptions,
239239
extensions: options.extensions,
240240
});
241241

242+
if (ref.name !== 'Query') {
243+
this.configStore.associateParamWithRef('Query', ref);
244+
}
245+
242246
this.configStore.addTypeRef(ref);
243247

244248
if (fields) {
@@ -270,17 +274,21 @@ export class SchemaBuilder<Types extends SchemaTypes> {
270274
) {
271275
const [options = {}, fields] = args;
272276

273-
const ref = new MutationRef<Types>('Mutation', {
277+
const ref = new MutationRef<Types>(options.name ?? 'Mutation', {
274278
kind: 'Mutation',
275279
graphqlKind: 'Object',
276-
name: 'Mutation',
280+
name: options.name ?? 'Mutation',
277281
description: options.description,
278282
pothosOptions: options as unknown as PothosSchemaTypes.MutationTypeOptions,
279283
extensions: options.extensions,
280284
});
281285

282286
this.configStore.addTypeRef(ref);
283287

288+
if (ref.name !== 'Mutation') {
289+
this.configStore.associateParamWithRef('Mutation', ref);
290+
}
291+
284292
if (fields) {
285293
this.configStore.addFields('Mutation', () => fields(new MutationFieldBuilder(this)));
286294
}
@@ -313,17 +321,21 @@ export class SchemaBuilder<Types extends SchemaTypes> {
313321
) {
314322
const [options = {}, fields] = args;
315323

316-
const ref = new SubscriptionRef<Types>('Subscription', {
324+
const ref = new SubscriptionRef<Types>(options.name ?? 'Subscription', {
317325
kind: 'Subscription',
318326
graphqlKind: 'Object',
319-
name: 'Subscription',
327+
name: options.name ?? 'Subscription',
320328
description: options.description,
321329
pothosOptions: options as unknown as PothosSchemaTypes.SubscriptionTypeOptions,
322330
extensions: options.extensions,
323331
});
324332

325333
this.configStore.addTypeRef(ref);
326334

335+
if (ref.name !== 'Subscription') {
336+
this.configStore.associateParamWithRef('Subscription', ref);
337+
}
338+
327339
if (fields) {
328340
this.configStore.addFields('Subscription', () => fields(new SubscriptionFieldBuilder(this)));
329341
}
@@ -671,10 +683,20 @@ export class SchemaBuilder<Types extends SchemaTypes> {
671683

672684
const builtTypes = [...buildCache.types.values()];
673685

686+
const queryName = this.configStore.hasConfig('Query')
687+
? this.configStore.getTypeConfig('Query').name
688+
: 'Query';
689+
const mutationName = this.configStore.hasConfig('Mutation')
690+
? this.configStore.getTypeConfig('Mutation').name
691+
: 'Mutation';
692+
const subscriptionName = this.configStore.hasConfig('Subscription')
693+
? this.configStore.getTypeConfig('Subscription').name
694+
: 'Subscription';
695+
674696
const schema = new GraphQLSchema({
675-
query: buildCache.types.get('Query') as GraphQLObjectType | undefined,
676-
mutation: buildCache.types.get('Mutation') as GraphQLObjectType | undefined,
677-
subscription: buildCache.types.get('Subscription') as GraphQLObjectType | undefined,
697+
query: buildCache.types.get(queryName) as GraphQLObjectType | undefined,
698+
mutation: buildCache.types.get(mutationName) as GraphQLObjectType | undefined,
699+
subscription: buildCache.types.get(subscriptionName) as GraphQLObjectType | undefined,
678700
extensions: extensions ?? {},
679701
directives: directives as GraphQLDirective[],
680702
types: builtTypes,

packages/core/src/types/global/type-options.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ declare global {
4949
| (Interfaces & ValidateInterfaces<Shape, Types, Interfaces[number]>[]);
5050
}
5151
export interface RootTypeOptions<Types extends SchemaTypes, Type extends RootName>
52-
extends BaseTypeOptions<Types> {}
52+
extends BaseTypeOptions<Types> {
53+
name?: string;
54+
}
5355

5456
export interface QueryTypeOptions<Types extends SchemaTypes = SchemaTypes>
5557
extends RootTypeOptions<Types, 'Query'> {
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { execute, lexicographicSortSchema, printSchema, subscribe } from 'graphql';
2+
import gql from 'graphql-tag';
3+
import { schema } from './examples/custom-root-names';
4+
5+
describe('custom-root-names example', () => {
6+
it('generates expected schema', () => {
7+
expect(printSchema(lexicographicSortSchema(schema))).toMatchInlineSnapshot(`
8+
"schema {
9+
query: CustomQuery
10+
mutation: CustomMutation
11+
subscription: CustomSubscription
12+
}
13+
14+
type CustomMutation {
15+
hello: String
16+
}
17+
18+
type CustomQuery {
19+
hello: String
20+
}
21+
22+
type CustomSubscription {
23+
hello: Int
24+
}"
25+
`);
26+
});
27+
28+
it('executes queries', async () => {
29+
const result = await execute({
30+
schema,
31+
document: gql`
32+
query {
33+
hello
34+
__typename
35+
}
36+
`,
37+
});
38+
39+
expect(result.data).toMatchInlineSnapshot(`
40+
{
41+
"__typename": "CustomQuery",
42+
"hello": "world",
43+
}
44+
`);
45+
});
46+
47+
it('executes mutations', async () => {
48+
const result = await execute({
49+
schema,
50+
document: gql`
51+
mutation {
52+
hello
53+
__typename
54+
}
55+
`,
56+
});
57+
58+
expect(result.data).toMatchInlineSnapshot(`
59+
{
60+
"__typename": "CustomMutation",
61+
"hello": "world",
62+
}
63+
`);
64+
});
65+
66+
it('executes subscriptions', async () => {
67+
const result = await subscribe({
68+
schema,
69+
document: gql`
70+
subscription {
71+
hello
72+
__typename
73+
}
74+
`,
75+
});
76+
77+
const results = [];
78+
for await (const value of result as AsyncIterable<{ data: { hello: number } }>) {
79+
results.push(value);
80+
}
81+
82+
expect(results).toMatchInlineSnapshot(`
83+
[
84+
{
85+
"data": {
86+
"__typename": "CustomSubscription",
87+
"hello": 0,
88+
},
89+
},
90+
{
91+
"data": {
92+
"__typename": "CustomSubscription",
93+
"hello": 1,
94+
},
95+
},
96+
{
97+
"data": {
98+
"__typename": "CustomSubscription",
99+
"hello": 2,
100+
},
101+
},
102+
]
103+
`);
104+
});
105+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import SchemaBuilder from '../../src';
2+
3+
const builder = new SchemaBuilder<{}>({});
4+
5+
builder.queryType({
6+
name: 'CustomQuery',
7+
fields: (t) => ({
8+
hello: t.string({
9+
resolve: () => 'world',
10+
}),
11+
}),
12+
});
13+
14+
builder.mutationType({
15+
name: 'CustomMutation',
16+
fields: (t) => ({
17+
hello: t.string({
18+
resolve: () => 'world',
19+
}),
20+
}),
21+
});
22+
23+
builder.subscriptionType({
24+
name: 'CustomSubscription',
25+
fields: (t) => ({
26+
hello: t.int({
27+
// biome-ignore lint/suspicious/useAwait: <explanation>
28+
subscribe: async function* () {
29+
for (let i = 0; i < 3; i++) {
30+
yield i;
31+
}
32+
},
33+
resolve: (i) => i,
34+
}),
35+
}),
36+
});
37+
38+
export const schema = builder.toSchema();

packages/plugin-federation/src/schema-builder.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ schemaBuilderProto.toSubGraphSchema = function toSubGraphSchema(
110110
]),
111111
},
112112
});
113-
const queryType = schema.getType('Query') as GraphQLObjectType | undefined;
113+
const queryType = schema.getQueryType();
114114
const types = schema.getTypeMap();
115115

116116
queryType?.toConfig();
@@ -127,7 +127,7 @@ schemaBuilderProto.toSubGraphSchema = function toSubGraphSchema(
127127
});
128128

129129
const newQuery = new GraphQLObjectType({
130-
name: 'Query',
130+
name: queryType?.name ?? 'Query',
131131
description: queryType?.description,
132132
astNode: queryType?.astNode,
133133
extensions: queryType?.extensions,
@@ -148,8 +148,8 @@ schemaBuilderProto.toSubGraphSchema = function toSubGraphSchema(
148148

149149
const subGraphSchema = new GraphQLSchema({
150150
query: newQuery,
151-
mutation: schema.getType('Mutation') as GraphQLObjectType,
152-
subscription: schema.getType('Subscription') as GraphQLObjectType,
151+
mutation: schema.getMutationType(),
152+
subscription: schema.getSubscriptionType(),
153153
extensions: schema.extensions,
154154
directives: schema.getDirectives(),
155155
extensionASTNodes: schema.extensionASTNodes,

packages/plugin-sub-graph/src/index.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,11 @@ export class PothosSubGraphPlugin<Types extends SchemaTypes> extends BasePlugin<
8585
extensions: config.extensions,
8686
extensionASTNodes: config.extensionASTNodes,
8787
assumeValid: false,
88-
query: newTypes.get('Query') as GraphQLObjectType,
89-
mutation: newTypes.get('Mutation') as GraphQLObjectType,
90-
subscription: newTypes.get('Subscription') as GraphQLObjectType,
88+
query: newTypes.get(schema.getQueryType()?.name ?? 'Query') as GraphQLObjectType,
89+
mutation: newTypes.get(schema.getMutationType()?.name ?? 'Mutation') as GraphQLObjectType,
90+
subscription: newTypes.get(
91+
schema.getSubscriptionType()?.name ?? 'Subscription',
92+
) as GraphQLObjectType,
9193
// Explicitly include types that implement an interface that can be resolved in the subGraph
9294
types: [...newTypes.values()].filter(
9395
(type) =>

0 commit comments

Comments
 (0)