graphql#OperationTypeNode TypeScript Examples
The following examples show how to use
graphql#OperationTypeNode.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts From graphql-mesh with MIT License | 7 votes |
export function getOperationMetadata(operationConfig: JSONSchemaOperationConfig) {
let httpMethod: HTTPMethod;
let operationType: OperationTypeNode;
let rootTypeName: 'Query' | 'Mutation' | 'Subscription';
if (isPubSubOperationConfig(operationConfig)) {
httpMethod = null;
operationType = 'subscription' as OperationTypeNode;
rootTypeName = 'Subscription';
} else {
httpMethod = operationConfig.method;
// Fix compability with Mesh handler
operationType = operationConfig.type.toLowerCase() as OperationTypeNode;
if (!httpMethod) {
if (operationType === 'mutation') {
httpMethod = 'POST';
} else {
httpMethod = 'GET';
}
}
if (!rootTypeName) {
if (httpMethod === 'GET') {
rootTypeName = 'Query';
}
}
rootTypeName = operationType === 'query' ? 'Query' : 'Mutation';
}
return {
httpMethod,
operationType,
rootTypeName,
fieldName: operationConfig.field,
};
}
Example #2
Source File: gqlTypes.ts From ra-data-prisma with MIT License | 6 votes |
operationDefinition = (
operation: OperationTypeNode,
selectionSet: SelectionSetNode,
name: NameNode,
variableDefinitions: VariableDefinitionNode[],
): OperationDefinitionNode => ({
kind: Kind.OPERATION_DEFINITION,
operation,
selectionSet,
name,
variableDefinitions,
})
Example #3
Source File: Variables.ts From tql with MIT License | 6 votes |
buildVariableDefinitions = <T extends SelectionSet<any>>(
schema: GraphQLSchema,
root: OperationTypeNode,
selectionSet: T
): Array<VariableDefinition<any, any>> => {
const variableDefinitions: VariableDefinition<any, any>[] = [];
const typeInfo = new TypeInfo(schema);
// @note need to wrap selectionset in an operation (root) for TypeInfo to track correctly
const operationDefinition = operation(
root,
"",
selectionSet,
variableDefinitions
);
const visitor = visitWithTypeInfo(typeInfo, {
[Kind.ARGUMENT]: (node) => {
const type = typeInfo.getArgument()?.astNode?.type!;
if (node.value.kind === "Variable") {
// define the `VariableDefinition`
variableDefinitions.push(variableDefinition(node.value, type));
}
},
});
// @todo return from here
visit(operationDefinition, visitor);
return variableDefinitions;
}
Example #4
Source File: render.ts From tql with MIT License | 5 votes |
operationTypeRevMapping = reverseRecord(OperationTypeNode)
Example #5
Source File: Variables.test.ts From tql with MIT License | 5 votes |
describe("Variables", () => {
it("builds variable definitions", () => {
const schema = buildSchema(
`
type Query {
hello(name: String!): String!
}
`,
{ noLocation: true }
);
const selection = new Selection(schema, "Query", [
field("hello", [argument("name", variable("name"))]),
]);
// @note we need a way to get the input type at runtime
const variableDefinitions = buildVariableDefinitions(
schema,
OperationTypeNode.QUERY,
selectionSet(selection)
);
expect(variableDefinitions).toEqual([
variableDefinition(variable("name"), nonNull(namedType("String"))),
]);
});
it("infers nested variable definitions", () => {
const schema = buildSchema(
`
type Query {
viewer: User!
}
type User {
id: ID!
friends(limit: Int!): [User!]
}
`,
{ noLocation: true }
);
const selection = new Selection(schema, "Query", [
field(
"viewer",
undefined,
selectionSet([
field(
"friends",
[argument("limit", variable("friendsLimit"))],
selectionSet([field("id")])
),
])
),
]);
const variableDefinitions = buildVariableDefinitions(
schema,
OperationTypeNode.QUERY,
selectionSet(selection)
);
expect(variableDefinitions).toEqual([
variableDefinition(variable("friendsLimit"), nonNull(namedType("Int"))),
]);
});
});
Example #6
Source File: getJSONSchemaOptionsFromOpenAPIOptions.ts From graphql-mesh with MIT License | 4 votes |
export async function getJSONSchemaOptionsFromOpenAPIOptions({
oasFilePath,
fallbackFormat,
cwd,
fetch,
baseUrl,
schemaHeaders,
operationHeaders,
selectQueryOrMutationField = [],
logger,
}: GetJSONSchemaOptionsFromOpenAPIOptionsParams) {
const fieldTypeMap: Record<string, 'query' | 'mutation'> = {};
for (const { fieldName, type } of selectQueryOrMutationField) {
fieldTypeMap[fieldName] = type;
}
const schemaHeadersFactory = getInterpolatedHeadersFactory(schemaHeaders);
logger?.debug(`Fetching OpenAPI Document from ${oasFilePath}`);
const oasOrSwagger: OpenAPIV3.Document | OpenAPIV2.Document =
typeof oasFilePath === 'string'
? await readFileOrUrl(oasFilePath, {
cwd,
fallbackFormat,
headers: schemaHeadersFactory({ env: process.env }),
fetch,
logger,
})
: oasFilePath;
const operations: JSONSchemaOperationConfig[] = [];
if ('servers' in oasOrSwagger) {
baseUrl = baseUrl || oasOrSwagger.servers[0].url;
}
for (const relativePath in oasOrSwagger.paths) {
const pathObj = oasOrSwagger.paths[relativePath];
for (const method in pathObj) {
const methodObj = pathObj[method] as OpenAPIV2.OperationObject | OpenAPIV3.OperationObject;
const operationConfig = {
method: method.toUpperCase() as HTTPMethod,
path: relativePath,
type: method.toUpperCase() === 'GET' ? 'query' : 'mutation',
field: methodObj.operationId && sanitizeNameForGraphQL(methodObj.operationId),
description: methodObj.description || methodObj.summary,
schemaHeaders,
operationHeaders,
responseByStatusCode: {},
} as JSONSchemaHTTPJSONOperationConfig & {
responseByStatusCode: Record<string, JSONSchemaOperationResponseConfig>;
};
operations.push(operationConfig);
for (const paramObjIndex in methodObj.parameters) {
const paramObj = methodObj.parameters[paramObjIndex] as OpenAPIV2.ParameterObject | OpenAPIV3.ParameterObject;
const argName = sanitizeNameForGraphQL(paramObj.name);
switch (paramObj.in) {
case 'query':
if (method.toUpperCase() === 'GET') {
const requestSchema = (operationConfig.requestSchema = operationConfig.requestSchema || {
type: 'object',
properties: {},
}) as JSONSchemaObject;
requestSchema.properties[paramObj.name] =
paramObj.schema || paramObj.content?.['application/json']?.schema || paramObj;
if (!requestSchema.properties[paramObj.name].title) {
requestSchema.properties[paramObj.name].name = paramObj.name;
}
if (!requestSchema.properties[paramObj.name].description) {
requestSchema.properties[paramObj.name].description = paramObj.description;
}
if (requestSchema.properties.__typename) {
delete requestSchema.properties.__typename;
}
if (paramObj.required) {
requestSchema.required = requestSchema.required || [];
requestSchema.required.push(paramObj.name);
}
} else {
if (!operationConfig.path.includes('?')) {
operationConfig.path += '?';
}
operationConfig.path += `${paramObj.name}={args.${argName}}`;
}
break;
case 'path': {
// If it is in the path, let JSON Schema handler put it
operationConfig.path = operationConfig.path.replace(`{${paramObj.name}}`, `{args.${argName}}`);
break;
}
case 'header': {
operationConfig.headers = operationConfig.headers || {};
operationConfig.headers[paramObj.name] = `{args.${argName}}`;
break;
}
case 'cookie': {
operationConfig.headers = operationConfig.headers || {};
operationConfig.headers.cookie = operationConfig.headers.cookie || '';
const cookieParams = operationConfig.headers.cookie.split('; ');
cookieParams.push(`${paramObj.name}={args.${argName}}`);
operationConfig.headers.cookie = cookieParams.join('; ');
break;
}
case 'body':
if (paramObj.schema && Object.keys(paramObj.schema).length > 0) {
operationConfig.requestSchema = `${oasFilePath}#/paths/${relativePath
.split('/')
.join('~1')}/${method}/parameters/${paramObjIndex}/schema`;
}
if (paramObj.example) {
operationConfig.requestSample = paramObj.example;
}
if (paramObj.examples) {
operationConfig.requestSample = Object.values(paramObj.examples)[0];
}
break;
}
switch (paramObj.schema?.type || (paramObj as any).type) {
case 'string':
operationConfig.argTypeMap = operationConfig.argTypeMap || {};
operationConfig.argTypeMap[argName] = 'String';
break;
case 'integer':
operationConfig.argTypeMap = operationConfig.argTypeMap || {};
operationConfig.argTypeMap[argName] = 'Int';
break;
case 'number':
operationConfig.argTypeMap = operationConfig.argTypeMap || {};
operationConfig.argTypeMap[argName] = 'Float';
break;
case 'boolean':
operationConfig.argTypeMap = operationConfig.argTypeMap || {};
operationConfig.argTypeMap[argName] = 'Boolean';
break;
}
if (paramObj.required) {
operationConfig.argTypeMap = operationConfig.argTypeMap || {};
operationConfig.argTypeMap[argName] = operationConfig.argTypeMap[argName] || 'ID';
operationConfig.argTypeMap[argName] += '!';
}
}
if ('requestBody' in methodObj) {
const requestBodyObj = methodObj.requestBody;
if ('content' in requestBodyObj) {
const contentKey = Object.keys(requestBodyObj.content)[0];
const contentSchema = requestBodyObj.content[contentKey]?.schema;
if (contentSchema && Object.keys(contentSchema).length > 0) {
operationConfig.requestSchema = `${oasFilePath}#/paths/${relativePath
.split('/')
.join('~1')}/${method}/requestBody/content/${contentKey?.toString().split('/').join('~1')}/schema`;
}
const examplesObj = requestBodyObj.content[contentKey]?.examples;
if (examplesObj) {
operationConfig.requestSample = Object.values(examplesObj)[0];
}
}
}
const responseByStatusCode = operationConfig.responseByStatusCode;
// Handling multiple response types
for (const responseKey in methodObj.responses) {
const responseObj = methodObj.responses[responseKey] as OpenAPIV3.ResponseObject | OpenAPIV2.ResponseObject;
let schemaObj: JSONSchemaObject;
if ('content' in responseObj) {
const contentKey = Object.keys(responseObj.content)[0];
schemaObj = responseObj.content[contentKey].schema as any;
if (schemaObj && Object.keys(schemaObj).length > 0) {
responseByStatusCode[responseKey] = responseByStatusCode[responseKey] || {};
responseByStatusCode[responseKey].responseSchema = `${oasFilePath}#/paths/${relativePath
.split('/')
.join('~1')}/${method}/responses/${responseKey}/content/${contentKey
?.toString()
.split('/')
.join('~1')}/schema`;
}
const examplesObj = responseObj.content[contentKey].examples;
if (examplesObj) {
responseByStatusCode[responseKey] = responseByStatusCode[responseKey] || {};
responseByStatusCode[responseKey].responseSample = Object.values(examplesObj)[0];
}
const example = responseObj.content[contentKey].example;
if (example) {
responseByStatusCode[responseKey] = responseByStatusCode[responseKey] || {};
responseByStatusCode[responseKey].responseSample = example;
}
} else if ('schema' in responseObj) {
schemaObj = responseObj.schema as any;
if (schemaObj && Object.keys(schemaObj).length > 0) {
responseByStatusCode[responseKey] = responseByStatusCode[responseKey] || {};
responseByStatusCode[responseKey].responseSchema = `${oasFilePath}#/paths/${relativePath
.split('/')
.join('~1')}/${method}/responses/${responseKey}/schema`;
}
} else if ('examples' in responseObj) {
const examples = Object.values(responseObj.examples);
responseByStatusCode[responseKey] = responseByStatusCode[responseKey] || {};
responseByStatusCode[responseKey].responseSample = examples[0];
} else if (responseKey.toString() === '204') {
responseByStatusCode[responseKey] = responseByStatusCode[responseKey] || {};
responseByStatusCode[responseKey].responseSchema = {
type: 'null',
description: responseObj.description,
};
}
if ('links' in responseObj) {
const dereferencedLinkObj = await dereferenceObject(
{
links: responseObj.links,
},
{
cwd,
root: oasOrSwagger,
fetch,
headers: schemaHeaders,
}
);
responseByStatusCode[responseKey].links = responseByStatusCode[responseKey].links || {};
for (const linkName in dereferencedLinkObj.links) {
const linkObj = responseObj.links[linkName];
if ('$ref' in linkObj) {
throw new Error('Unexpected $ref in dereferenced link object');
}
const args: Record<string, string> = {};
for (const parameterName in linkObj.parameters || {}) {
const parameterExp = linkObj.parameters[parameterName].split('-').join('_') as string;
args[sanitizeNameForGraphQL(parameterName)] = parameterExp.startsWith('$')
? `{root.${parameterExp}}`
: parameterExp.split('$').join('root.$');
}
if ('operationRef' in linkObj) {
const [externalPath, ref] = linkObj.operationRef.split('#');
if (externalPath) {
if (process.env.DEBUG) {
console.warn(
`Skipping external operation reference ${linkObj.operationRef}\n Use additionalTypeDefs and additionalResolvers instead.`
);
}
} else {
const actualOperation = resolvePath(ref, oasOrSwagger);
if (actualOperation.operationId) {
const fieldName = sanitizeNameForGraphQL(actualOperation.operationId);
responseByStatusCode[responseKey].links[linkName] = {
fieldName,
args,
description: linkObj.description,
};
} else {
console.warn('Missing operationId skipping...');
}
}
} else if ('operationId' in linkObj) {
responseByStatusCode[responseKey].links[linkName] = {
fieldName: sanitizeNameForGraphQL(linkObj.operationId),
args,
description: linkObj.description,
};
}
}
}
if (!operationConfig.field) {
methodObj.operationId = getFieldNameFromPath(relativePath, method, schemaObj?.$ref);
// Operation ID might not be avaiable so let's generate field name from path and response type schema
operationConfig.field = sanitizeNameForGraphQL(methodObj.operationId);
}
// Give a better name to the request input object
if (typeof operationConfig.requestSchema === 'object' && !operationConfig.requestSchema.title) {
operationConfig.requestSchema.title = operationConfig.field + '_request';
}
}
if (fieldTypeMap[operationConfig.field]) {
operationConfig.type = fieldTypeMap[operationConfig.field] as OperationTypeNode;
}
}
}
return {
operations,
baseUrl,
cwd,
fetch,
schemaHeaders,
operationHeaders,
};
}
Example #7
Source File: get-mesh.ts From graphql-mesh with MIT License | 4 votes |
export async function getMesh<TMeshContext = any>(options: GetMeshOptions): Promise<MeshInstance<TMeshContext>> {
const rawSources: RawSourceOutput[] = [];
const {
pubsub = new PubSub(),
cache,
logger = new DefaultLogger('?️ Mesh'),
additionalEnvelopPlugins = [],
sources,
merger,
additionalResolvers,
additionalTypeDefs,
transforms,
} = options;
const getMeshLogger = logger.child('GetMesh');
getMeshLogger.debug(`Getting subschemas from source handlers`);
let failed = false;
await Promise.allSettled(
sources.map(async apiSource => {
const apiName = apiSource.name;
const sourceLogger = logger.child(apiName);
sourceLogger.debug(`Generating the schema`);
try {
const source = await apiSource.handler.getMeshSource();
sourceLogger.debug(`The schema has been generated successfully`);
let apiSchema = source.schema;
sourceLogger.debug(`Analyzing transforms`);
let transforms: MeshTransform[];
const { wrapTransforms, noWrapTransforms } = groupTransforms(apiSource.transforms);
if (!wrapTransforms?.length && noWrapTransforms?.length) {
sourceLogger.debug(`${noWrapTransforms.length} bare transforms found and applying`);
apiSchema = applySchemaTransforms(apiSchema, source as SubschemaConfig, null, noWrapTransforms);
} else {
transforms = apiSource.transforms;
}
rawSources.push({
name: apiName,
schema: apiSchema,
executor: source.executor,
transforms,
contextVariables: source.contextVariables || [],
handler: apiSource.handler,
batch: 'batch' in source ? source.batch : true,
merge: apiSource.merge,
});
} catch (e: any) {
sourceLogger.error(`Failed to generate the schema`, e);
failed = true;
}
})
);
if (failed) {
throw new Error(
`Schemas couldn't be generated successfully. Check for the logs by running Mesh with DEBUG=1 environmental variable to get more verbose output.`
);
}
getMeshLogger.debug(`Schemas have been generated by the source handlers`);
getMeshLogger.debug(`Merging schemas using the defined merging strategy.`);
const unifiedSchema = await merger.getUnifiedSchema({
rawSources,
typeDefs: additionalTypeDefs,
resolvers: additionalResolvers,
transforms,
});
getMeshLogger.debug(`Building Mesh Context`);
const meshContext: Record<string, any> = {
pubsub,
cache,
logger,
[MESH_CONTEXT_SYMBOL]: true,
};
getMeshLogger.debug(`Attaching in-context SDK, pubsub and cache to the context`);
const sourceMap = unifiedSchema.extensions.sourceMap as Map<RawSourceOutput, GraphQLSchema>;
await Promise.all(
rawSources.map(async rawSource => {
const rawSourceLogger = logger.child(`${rawSource.name}`);
const rawSourceContext: any = {
rawSource,
[MESH_API_CONTEXT_SYMBOL]: true,
};
// TODO: Somehow rawSource reference got lost in somewhere
let rawSourceSubSchemaConfig: SubschemaConfig;
const stitchingInfo = unifiedSchema.extensions.stitchingInfo as StitchingInfo;
if (stitchingInfo) {
for (const [subschemaConfig, subschema] of stitchingInfo.subschemaMap) {
if ((subschemaConfig as any).name === rawSource.name) {
rawSourceSubSchemaConfig = subschema;
break;
}
}
} else {
rawSourceSubSchemaConfig = rawSource;
}
const transformedSchema = sourceMap.get(rawSource);
const rootTypes: Record<OperationTypeNode, GraphQLObjectType> = {
query: transformedSchema.getQueryType(),
mutation: transformedSchema.getMutationType(),
subscription: transformedSchema.getSubscriptionType(),
};
rawSourceLogger.debug(`Generating In Context SDK`);
for (const operationType in rootTypes) {
const rootType: GraphQLObjectType = rootTypes[operationType];
if (rootType) {
rawSourceContext[rootType.name] = {};
const rootTypeFieldMap = rootType.getFields();
for (const fieldName in rootTypeFieldMap) {
const rootTypeField = rootTypeFieldMap[fieldName];
const inContextSdkLogger = rawSourceLogger.child(`InContextSDK.${rootType.name}.${fieldName}`);
const namedReturnType = getNamedType(rootTypeField.type);
const shouldHaveSelectionSet = !isLeafType(namedReturnType);
rawSourceContext[rootType.name][fieldName] = ({
root,
args,
context,
info = {
fieldName,
fieldNodes: [],
returnType: namedReturnType,
parentType: rootType,
path: {
typename: rootType.name,
key: fieldName,
prev: undefined,
},
schema: transformedSchema,
fragments: {},
rootValue: root,
operation: {
kind: Kind.OPERATION_DEFINITION,
operation: operationType as OperationTypeNode,
selectionSet: {
kind: Kind.SELECTION_SET,
selections: [],
},
},
variableValues: {},
cacheControl: {
setCacheHint: () => {},
cacheHint: {},
},
},
selectionSet,
key,
argsFromKeys,
valuesFromResults,
}: {
root: any;
args: any;
context: any;
info: GraphQLResolveInfo;
selectionSet: SelectionSetParamOrFactory;
key?: string;
argsFromKeys?: (keys: string[]) => any;
valuesFromResults?: (result: any, keys?: string[]) => any;
}) => {
inContextSdkLogger.debug(`Called with`, {
args,
key,
});
const commonDelegateOptions: IDelegateToSchemaOptions = {
schema: rawSourceSubSchemaConfig,
rootValue: root,
operation: operationType as OperationTypeNode,
fieldName,
context,
transformedSchema,
info,
};
// If there isn't an extraction of a value
if (typeof selectionSet !== 'function') {
commonDelegateOptions.returnType = rootTypeField.type;
}
if (shouldHaveSelectionSet) {
let selectionCount = 0;
for (const fieldNode of info.fieldNodes) {
if (fieldNode.selectionSet != null) {
selectionCount += fieldNode.selectionSet.selections.length;
}
}
if (selectionCount === 0) {
if (!selectionSet) {
throw new Error(
`You have to provide 'selectionSet' for context.${rawSource.name}.${rootType.name}.${fieldName}`
);
}
commonDelegateOptions.info = {
...info,
fieldNodes: [
{
...info.fieldNodes[0],
selectionSet: {
kind: Kind.SELECTION_SET,
selections: [
{
kind: Kind.FIELD,
name: {
kind: Kind.NAME,
value: '__typename',
},
},
],
},
},
...info.fieldNodes.slice(1),
],
};
}
}
if (key && argsFromKeys) {
const batchDelegationOptions = {
...commonDelegateOptions,
key,
argsFromKeys,
valuesFromResults,
} as unknown as BatchDelegateOptions;
if (selectionSet) {
const selectionSetFactory = normalizeSelectionSetParamOrFactory(selectionSet);
const path = [fieldName];
const wrapQueryTransform = new WrapQuery(path, selectionSetFactory, identical);
batchDelegationOptions.transforms = [wrapQueryTransform as any];
}
return batchDelegateToSchema(batchDelegationOptions);
} else {
const regularDelegateOptions: IDelegateToSchemaOptions = {
...commonDelegateOptions,
args,
};
if (selectionSet) {
const selectionSetFactory = normalizeSelectionSetParamOrFactory(selectionSet);
const path = [fieldName];
const wrapQueryTransform = new WrapQuery(path, selectionSetFactory, valuesFromResults || identical);
regularDelegateOptions.transforms = [wrapQueryTransform as any];
}
return delegateToSchema(regularDelegateOptions);
}
};
}
}
}
meshContext[rawSource.name] = rawSourceContext;
})
);
const plugins: EnvelopPlugins = [
useSchema(unifiedSchema),
useExtendContext(() => meshContext),
useErrorHandler(errors => {
errors.forEach(error => logger.error(error.stack || error.message));
}),
{
onParse({ setParseFn }) {
setParseFn(parseWithCache);
},
},
...additionalEnvelopPlugins,
];
const EMPTY_ROOT_VALUE: any = {};
const EMPTY_CONTEXT_VALUE: any = {};
const EMPTY_VARIABLES_VALUE: any = {};
async function meshExecute<TVariables = any, TContext = any, TRootValue = any, TData = any>(
documentOrSDL: GraphQLOperation<TData, TVariables>,
variableValues: TVariables = EMPTY_VARIABLES_VALUE,
contextValue: TContext = EMPTY_CONTEXT_VALUE,
rootValue: TRootValue = EMPTY_ROOT_VALUE,
operationName?: string
) {
const getEnveloped = memoizedGetEnvelopedFactory(plugins);
const { execute, contextFactory, parse } = getEnveloped(contextValue);
return execute({
document: typeof documentOrSDL === 'string' ? parse(documentOrSDL) : documentOrSDL,
contextValue: await contextFactory(),
rootValue,
variableValues: variableValues as any,
schema: unifiedSchema,
operationName,
});
}
async function meshSubscribe<TVariables = any, TContext = any, TRootValue = any, TData = any>(
documentOrSDL: GraphQLOperation<TData, TVariables>,
variableValues: TVariables = EMPTY_VARIABLES_VALUE,
contextValue: TContext = EMPTY_CONTEXT_VALUE,
rootValue: TRootValue = EMPTY_ROOT_VALUE,
operationName?: string
) {
const getEnveloped = memoizedGetEnvelopedFactory(plugins);
const { subscribe, contextFactory, parse } = getEnveloped(contextValue);
return subscribe({
document: typeof documentOrSDL === 'string' ? parse(documentOrSDL) : documentOrSDL,
contextValue: await contextFactory(),
rootValue,
variableValues: variableValues as any,
schema: unifiedSchema,
operationName,
});
}
function sdkRequesterFactory(globalContext: any) {
return async function meshSdkRequester(document: DocumentNode, variables: any, contextValue: any) {
if (memoizedGetOperationType(document) === 'subscription') {
const result = await meshSubscribe(document, variables, {
...globalContext,
...contextValue,
});
if (isAsyncIterable(result)) {
return mapAsyncIterator(result, result => {
if (result?.errors?.length) {
return new AggregateError(result.errors);
}
return result?.data;
});
}
if (result?.errors?.length) {
return new AggregateError(result.errors);
}
return result?.data;
} else {
const result = await meshExecute(document, variables, {
...globalContext,
...contextValue,
});
if (result?.errors?.length) {
return new AggregateError(result.errors);
}
return result?.data;
}
};
}
return {
execute: meshExecute,
subscribe: meshSubscribe,
schema: unifiedSchema,
rawSources,
cache,
pubsub,
destroy: () => pubsub.publish('destroy', undefined),
logger,
meshContext: meshContext as TMeshContext,
plugins,
get getEnveloped() {
return memoizedGetEnvelopedFactory(plugins);
},
sdkRequesterFactory,
};
}
Example #8
Source File: handler.ts From graphql-sse with MIT License | 4 votes |
/**
* Makes a Protocol complient HTTP GraphQL server handler. The handler can
* be used with your favourite server library.
*
* Read more about the Protocol in the PROTOCOL.md documentation file.
*
* @category Server
*/
export function createHandler<
Request extends IncomingMessage = IncomingMessage,
Response extends ServerResponse = ServerResponse,
>(options: HandlerOptions<Request, Response>): Handler<Request, Response> {
const {
schema,
context,
validate = graphqlValidate,
execute = graphqlExecute,
subscribe = graphqlSubscribe,
authenticate = function extractOrCreateStreamToken(req) {
const headerToken =
req.headers[TOKEN_HEADER_KEY] || req.headers['x-graphql-stream-token']; // @deprecated >v1.0.0
if (headerToken)
return Array.isArray(headerToken) ? headerToken.join('') : headerToken;
const urlToken = new URL(
req.url ?? '',
'http://localhost/',
).searchParams.get(TOKEN_QUERY_KEY);
if (urlToken) return urlToken;
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0,
v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
},
onConnecting,
onConnected,
onSubscribe,
onOperation,
onNext,
onComplete,
onDisconnect,
} = options;
const streams: Record<string, Stream> = {};
function createStream(token: string | null): Stream<Request, Response> {
let request: Request | null = null,
response: Response | null = null,
pinger: ReturnType<typeof setInterval>,
disposed = false;
const pendingMsgs: string[] = [];
const ops: Record<
string,
AsyncGenerator<unknown> | AsyncIterable<unknown> | null
> = {};
function write(msg: unknown) {
return new Promise<boolean>((resolve, reject) => {
if (disposed || !response || !response.writable) return resolve(false);
response.write(msg, (err) => {
if (err) return reject(err);
resolve(true);
});
});
}
async function emit<E extends StreamEvent>(
event: E,
data: StreamData<E> | StreamDataForID<E>,
): Promise<void> {
let msg = `event: ${event}`;
if (data) msg += `\ndata: ${JSON.stringify(data)}`;
msg += '\n\n';
const wrote = await write(msg);
if (!wrote) pendingMsgs.push(msg);
}
async function dispose() {
if (disposed) return;
disposed = true;
// make room for another potential stream while this one is being disposed
if (typeof token === 'string') delete streams[token];
// complete all operations and flush messages queue before ending the stream
for (const op of Object.values(ops)) {
if (isAsyncGenerator(op)) await op.return(undefined);
}
while (pendingMsgs.length) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const msg = pendingMsgs.shift()!;
await write(msg);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
response!.end(); // response must exist at this point
response = null;
clearInterval(pinger);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
onDisconnect?.(request!); // request must exist at this point
request = null;
}
return {
get open() {
return disposed || Boolean(response);
},
ops,
async use(req, res) {
request = req;
response = res;
req.socket.setTimeout(0);
req.socket.setNoDelay(true);
req.socket.setKeepAlive(true);
res.once('close', dispose);
res.statusCode = 200;
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('X-Accel-Buffering', 'no');
if (req.httpVersionMajor < 2) res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
// write an empty message because some browsers (like Firefox and Safari)
// dont accept the header flush
await write(':\n\n');
// ping client every 12 seconds to keep the connection alive
pinger = setInterval(() => write(':\n\n'), 12_000);
while (pendingMsgs.length) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const msg = pendingMsgs.shift()!;
const wrote = await write(msg);
if (!wrote) throw new Error('Unable to flush messages');
}
await onConnected?.(req);
},
async from(operationReq, args, result, opId) {
if (isAsyncIterable(result)) {
/** multiple emitted results */
for await (let part of result) {
const maybeResult = await onNext?.(operationReq, args, part);
if (maybeResult) part = maybeResult;
await emit(
'next',
opId
? {
id: opId,
payload: part,
}
: part,
);
}
} else {
/** single emitted result */
const maybeResult = await onNext?.(operationReq, args, result);
if (maybeResult) result = maybeResult;
await emit(
'next',
opId
? {
id: opId,
payload: result,
}
: result,
);
}
await emit('complete', opId ? { id: opId } : null);
// end on complete when no operation id is present
// because distinct event streams are used for each operation
if (!opId) await dispose();
else delete ops[opId];
await onComplete?.(operationReq, args);
},
};
}
async function prepare(
req: Request,
res: Response,
params: RequestParams,
): Promise<[args: ExecutionArgs, perform: () => OperationResult] | void> {
let args: ExecutionArgs, operation: OperationTypeNode;
const maybeExecArgs = await onSubscribe?.(req, res, params);
if (maybeExecArgs) args = maybeExecArgs;
else {
// you either provide a schema dynamically through
// `onSubscribe` or you set one up during the server setup
if (!schema) throw new Error('The GraphQL schema is not provided');
const { operationName, variables } = params;
let { query } = params;
if (typeof query === 'string') {
try {
query = parse(query);
} catch {
res.writeHead(400, 'GraphQL query syntax error').end();
return;
}
}
const argsWithoutSchema = {
operationName,
document: query,
variableValues: variables,
};
args = {
...argsWithoutSchema,
schema:
typeof schema === 'function'
? await schema(req, argsWithoutSchema)
: schema,
};
}
try {
const ast = getOperationAST(args.document, args.operationName);
if (!ast) throw null;
operation = ast.operation;
} catch {
res.writeHead(400, 'Unable to detect operation AST').end();
return;
}
// mutations cannot happen over GETs as per the spec
// Read more: https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#get
if (operation === 'mutation' && req.method === 'GET') {
res
.writeHead(405, 'Cannot perform mutations over GET', {
Allow: 'POST',
})
.end();
return;
}
if (!('contextValue' in args))
args.contextValue =
typeof context === 'function' ? await context(req, args) : context;
// we validate after injecting the context because the process of
// reporting the validation errors might need the supplied context value
const validationErrs = validate(args.schema, args.document);
if (validationErrs.length) {
if (req.headers.accept === 'text/event-stream') {
// accept the request and emit the validation error in event streams,
// promoting graceful GraphQL error reporting
// Read more: https://www.w3.org/TR/eventsource/#processing-model
// Read more: https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-validation
return [
args,
function perform() {
return { errors: validationErrs };
},
];
}
res
.writeHead(400, {
'Content-Type':
req.headers.accept === 'application/json'
? 'application/json; charset=utf-8'
: 'application/graphql+json; charset=utf-8',
})
.write(JSON.stringify({ errors: validationErrs }));
res.end();
return;
}
return [
args,
async function perform() {
let result =
operation === 'subscription' ? subscribe(args) : execute(args);
const maybeResult = await onOperation?.(req, res, args, result);
if (maybeResult) result = maybeResult;
return result;
},
];
}
return async function handler(req: Request, res: Response, body: unknown) {
// authenticate first and acquire unique identification token
const token = await authenticate(req, res);
if (res.writableEnded) return;
if (typeof token !== 'string') throw new Error('Token was not supplied');
const accept = req.headers.accept ?? '*/*';
const stream = streams[token];
if (accept === 'text/event-stream') {
// if event stream is not registered, process it directly.
// this means that distinct connections are used for graphql operations
if (!stream) {
let params;
try {
params = await parseReq(req, body);
} catch (err) {
res.writeHead(400, err.message).end();
return;
}
const distinctStream = createStream(null);
// reserve space for the operation
distinctStream.ops[''] = null;
const prepared = await prepare(req, res, params);
if (res.writableEnded) return;
if (!prepared)
throw new Error(
"Operation preparation didn't respond, yet it was not prepared",
);
const [args, perform] = prepared;
const result = await perform();
if (res.writableEnded) {
if (isAsyncGenerator(result)) result.return(undefined);
return; // `onOperation` responded
}
if (isAsyncIterable(result)) distinctStream.ops[''] = result;
await onConnecting?.(req, res);
if (res.writableEnded) return;
await distinctStream.use(req, res);
await distinctStream.from(req, args, result);
return;
}
// open stream cant exist, only one per token is allowed
if (stream.open) {
res.writeHead(409, 'Stream already open').end();
return;
}
await onConnecting?.(req, res);
if (res.writableEnded) return;
await stream.use(req, res);
return;
}
if (req.method === 'PUT') {
// method PUT prepares a stream for future incoming connections.
if (!['*/*', 'text/plain'].includes(accept)) {
res.writeHead(406).end();
return;
}
// streams mustnt exist if putting new one
if (stream) {
res.writeHead(409, 'Stream already registered').end();
return;
}
streams[token] = createStream(token);
res
.writeHead(201, { 'Content-Type': 'text/plain; charset=utf-8' })
.write(token);
res.end();
return;
} else if (req.method === 'DELETE') {
// method DELETE completes an existing operation streaming in streams
// streams must exist when completing operations
if (!stream) {
res.writeHead(404, 'Stream not found').end();
return;
}
const opId = new URL(req.url ?? '', 'http://localhost/').searchParams.get(
'operationId',
);
if (!opId) {
res.writeHead(400, 'Operation ID is missing').end();
return;
}
const op = stream.ops[opId];
if (isAsyncGenerator(op)) op.return(undefined);
delete stream.ops[opId]; // deleting the operation means no further activity should take place
res.writeHead(200).end();
return;
} else if (req.method !== 'GET' && req.method !== 'POST') {
// only POSTs and GETs are accepted at this point
res.writeHead(405, undefined, { Allow: 'GET, POST, PUT, DELETE' }).end();
return;
} else if (!stream) {
// for all other requests, streams must exist to attach the result onto
res.writeHead(404, 'Stream not found').end();
return;
}
if (
!['*/*', 'application/graphql+json', 'application/json'].includes(accept)
) {
res.writeHead(406).end();
return;
}
let params;
try {
params = await parseReq(req, body);
} catch (err) {
res.writeHead(400, err.message).end();
return;
}
const opId = String(params.extensions?.operationId ?? '');
if (!opId) {
res.writeHead(400, 'Operation ID is missing').end();
return;
}
if (opId in stream.ops) {
res.writeHead(409, 'Operation with ID already exists').end();
return;
}
// reserve space for the operation through ID
stream.ops[opId] = null;
const prepared = await prepare(req, res, params);
if (res.writableEnded) return;
if (!prepared)
throw new Error(
"Operation preparation didn't respond, yet it was not prepared",
);
const [args, perform] = prepared;
// operation might have completed before prepared
if (!(opId in stream.ops)) {
res.writeHead(204).end();
return;
}
const result = await perform();
if (res.writableEnded) {
if (isAsyncGenerator(result)) result.return(undefined);
delete stream.ops[opId];
return; // `onOperation` responded
}
// operation might have completed before performed
if (!(opId in stream.ops)) {
if (isAsyncGenerator(result)) result.return(undefined);
res.writeHead(204).end();
return;
}
if (isAsyncIterable(result)) stream.ops[opId] = result;
res.writeHead(202).end();
// streaming to an empty reservation is ok (will be flushed on connect)
await stream.from(req, args, result, opId);
};
}
Example #9
Source File: generated.ts From tql with MIT License | 4 votes |
SCHEMA = buildASTSchema({
kind: Kind.DOCUMENT,
definitions: [
{
kind: Kind.SCHEMA_DEFINITION,
directives: [],
operationTypes: [
{
kind: Kind.OPERATION_TYPE_DEFINITION,
operation: OperationTypeNode.QUERY,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Query",
},
},
},
{
kind: Kind.OPERATION_TYPE_DEFINITION,
operation: OperationTypeNode.MUTATION,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Mutation",
},
},
},
],
},
{
kind: Kind.OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value:
"The query type, represents all of the entry points into our object graph",
block: true,
},
name: {
kind: Kind.NAME,
value: "Query",
},
interfaces: [],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "hero",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "episode",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Episode",
},
},
directives: [],
},
],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Character",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "reviews",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "episode",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Episode",
},
},
},
directives: [],
},
],
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Review",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "search",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "text",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "String",
},
},
directives: [],
},
],
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "SearchResult",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "character",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "id",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
},
directives: [],
},
],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Character",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "droid",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "id",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
},
directives: [],
},
],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Droid",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "human",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "id",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
},
directives: [],
},
],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Human",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "starship",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "id",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
},
directives: [],
},
],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Starship",
},
},
directives: [],
},
],
},
{
kind: Kind.OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value:
"The mutation type, represents all updates we can make to our data",
block: true,
},
name: {
kind: Kind.NAME,
value: "Mutation",
},
interfaces: [],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "createReview",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "episode",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Episode",
},
},
directives: [],
},
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "review",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ReviewInput",
},
},
},
directives: [],
},
],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Review",
},
},
directives: [],
},
],
},
{
kind: Kind.ENUM_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "The episodes in the Star Wars trilogy",
block: true,
},
name: {
kind: Kind.NAME,
value: "Episode",
},
directives: [],
values: [
{
kind: Kind.ENUM_VALUE_DEFINITION,
description: {
kind: Kind.STRING,
value: "Star Wars Episode IV: A New Hope, released in 1977.",
block: true,
},
name: {
kind: Kind.NAME,
value: "NEWHOPE",
},
directives: [],
},
{
kind: Kind.ENUM_VALUE_DEFINITION,
description: {
kind: Kind.STRING,
value:
"Star Wars Episode V: The Empire Strikes Back, released in 1980.",
block: true,
},
name: {
kind: Kind.NAME,
value: "EMPIRE",
},
directives: [],
},
{
kind: Kind.ENUM_VALUE_DEFINITION,
description: {
kind: Kind.STRING,
value:
"Star Wars Episode VI: Return of the Jedi, released in 1983.",
block: true,
},
name: {
kind: Kind.NAME,
value: "JEDI",
},
directives: [],
},
],
},
{
kind: Kind.INTERFACE_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "A character from the Star Wars universe",
block: true,
},
name: {
kind: Kind.NAME,
value: "Character",
},
interfaces: [],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The ID of the character",
block: true,
},
name: {
kind: Kind.NAME,
value: "id",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The name of the character",
block: true,
},
name: {
kind: Kind.NAME,
value: "name",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "String",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value:
"The friends of the character, or an empty list if they have none",
block: true,
},
name: {
kind: Kind.NAME,
value: "friends",
},
arguments: [],
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Character",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value:
"The friends of the character exposed as a connection with edges",
block: true,
},
name: {
kind: Kind.NAME,
value: "friendsConnection",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "first",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Int",
},
},
directives: [],
},
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "after",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
directives: [],
},
],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "FriendsConnection",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The movies this character appears in",
block: true,
},
name: {
kind: Kind.NAME,
value: "appearsIn",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Episode",
},
},
},
},
directives: [],
},
],
},
{
kind: Kind.ENUM_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "Units of height",
block: true,
},
name: {
kind: Kind.NAME,
value: "LengthUnit",
},
directives: [],
values: [
{
kind: Kind.ENUM_VALUE_DEFINITION,
description: {
kind: Kind.STRING,
value: "The standard unit around the world",
block: true,
},
name: {
kind: Kind.NAME,
value: "METER",
},
directives: [],
},
{
kind: Kind.ENUM_VALUE_DEFINITION,
description: {
kind: Kind.STRING,
value: "Primarily used in the United States",
block: true,
},
name: {
kind: Kind.NAME,
value: "FOOT",
},
directives: [],
},
{
kind: Kind.ENUM_VALUE_DEFINITION,
description: {
kind: Kind.STRING,
value: "Ancient unit used during the Middle Ages",
block: true,
},
name: {
kind: Kind.NAME,
value: "CUBIT",
},
directives: [
{
kind: Kind.DIRECTIVE,
name: {
kind: Kind.NAME,
value: "deprecated",
},
arguments: [
{
kind: Kind.ARGUMENT,
name: {
kind: Kind.NAME,
value: "reason",
},
value: {
kind: Kind.STRING,
value: "Test deprecated enum case",
block: false,
},
},
],
},
],
},
],
},
{
kind: Kind.OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "A humanoid creature from the Star Wars universe",
block: true,
},
name: {
kind: Kind.NAME,
value: "Human",
},
interfaces: [
{
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Character",
},
},
],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The ID of the human",
block: true,
},
name: {
kind: Kind.NAME,
value: "id",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "What this human calls themselves",
block: true,
},
name: {
kind: Kind.NAME,
value: "name",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "String",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The home planet of the human, or null if unknown",
block: true,
},
name: {
kind: Kind.NAME,
value: "homePlanet",
},
arguments: [],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "String",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "Height in the preferred unit, default is meters",
block: true,
},
name: {
kind: Kind.NAME,
value: "height",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "unit",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "LengthUnit",
},
},
defaultValue: {
kind: Kind.ENUM,
value: "METER",
},
directives: [],
},
],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Float",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "Mass in kilograms, or null if unknown",
block: true,
},
name: {
kind: Kind.NAME,
value: "mass",
},
arguments: [],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Float",
},
},
directives: [
{
kind: Kind.DIRECTIVE,
name: {
kind: Kind.NAME,
value: "deprecated",
},
arguments: [
{
kind: Kind.ARGUMENT,
name: {
kind: Kind.NAME,
value: "reason",
},
value: {
kind: Kind.STRING,
value: "Weight is a sensitive subject!",
block: false,
},
},
],
},
],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "This human's friends, or an empty list if they have none",
block: true,
},
name: {
kind: Kind.NAME,
value: "friends",
},
arguments: [],
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Character",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value:
"The friends of the human exposed as a connection with edges",
block: true,
},
name: {
kind: Kind.NAME,
value: "friendsConnection",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "first",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Int",
},
},
directives: [],
},
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "after",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
directives: [],
},
],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "FriendsConnection",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The movies this human appears in",
block: true,
},
name: {
kind: Kind.NAME,
value: "appearsIn",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Episode",
},
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value:
"A list of starships this person has piloted, or an empty list if none",
block: true,
},
name: {
kind: Kind.NAME,
value: "starships",
},
arguments: [],
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Starship",
},
},
},
directives: [],
},
],
},
{
kind: Kind.OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "An autonomous mechanical character in the Star Wars universe",
block: true,
},
name: {
kind: Kind.NAME,
value: "Droid",
},
interfaces: [
{
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Character",
},
},
],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The ID of the droid",
block: true,
},
name: {
kind: Kind.NAME,
value: "id",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "What others call this droid",
block: true,
},
name: {
kind: Kind.NAME,
value: "name",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "String",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "This droid's friends, or an empty list if they have none",
block: true,
},
name: {
kind: Kind.NAME,
value: "friends",
},
arguments: [],
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Character",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value:
"The friends of the droid exposed as a connection with edges",
block: true,
},
name: {
kind: Kind.NAME,
value: "friendsConnection",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "first",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Int",
},
},
directives: [],
},
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "after",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
directives: [],
},
],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "FriendsConnection",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The movies this droid appears in",
block: true,
},
name: {
kind: Kind.NAME,
value: "appearsIn",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Episode",
},
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "This droid's primary function",
block: true,
},
name: {
kind: Kind.NAME,
value: "primaryFunction",
},
arguments: [],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "String",
},
},
directives: [],
},
],
},
{
kind: Kind.OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "A connection object for a character's friends",
block: true,
},
name: {
kind: Kind.NAME,
value: "FriendsConnection",
},
interfaces: [],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The total number of friends",
block: true,
},
name: {
kind: Kind.NAME,
value: "totalCount",
},
arguments: [],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Int",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The edges for each of the character's friends.",
block: true,
},
name: {
kind: Kind.NAME,
value: "edges",
},
arguments: [],
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "FriendsEdge",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value:
"A list of the friends, as a convenience when edges are not needed.",
block: true,
},
name: {
kind: Kind.NAME,
value: "friends",
},
arguments: [],
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Character",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "Information for paginating this connection",
block: true,
},
name: {
kind: Kind.NAME,
value: "pageInfo",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "PageInfo",
},
},
},
directives: [],
},
],
},
{
kind: Kind.OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "An edge object for a character's friends",
block: true,
},
name: {
kind: Kind.NAME,
value: "FriendsEdge",
},
interfaces: [],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "A cursor used for pagination",
block: true,
},
name: {
kind: Kind.NAME,
value: "cursor",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The character represented by this friendship edge",
block: true,
},
name: {
kind: Kind.NAME,
value: "node",
},
arguments: [],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Character",
},
},
directives: [],
},
],
},
{
kind: Kind.OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "Information for paginating this connection",
block: true,
},
name: {
kind: Kind.NAME,
value: "PageInfo",
},
interfaces: [],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "startCursor",
},
arguments: [],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "endCursor",
},
arguments: [],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "hasNextPage",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Boolean",
},
},
},
directives: [],
},
],
},
{
kind: Kind.OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "Represents a review for a movie",
block: true,
},
name: {
kind: Kind.NAME,
value: "Review",
},
interfaces: [],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The number of stars this review gave, 1-5",
block: true,
},
name: {
kind: Kind.NAME,
value: "stars",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Int",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "Comment about the movie",
block: true,
},
name: {
kind: Kind.NAME,
value: "commentary",
},
arguments: [],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "String",
},
},
directives: [],
},
],
},
{
kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "The input object sent when someone is creating a new review",
block: true,
},
name: {
kind: Kind.NAME,
value: "ReviewInput",
},
directives: [],
fields: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
description: {
kind: Kind.STRING,
value: "0-5 stars",
block: true,
},
name: {
kind: Kind.NAME,
value: "stars",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Int",
},
},
},
directives: [],
},
{
kind: Kind.INPUT_VALUE_DEFINITION,
description: {
kind: Kind.STRING,
value: "Comment about the movie, optional",
block: true,
},
name: {
kind: Kind.NAME,
value: "commentary",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "String",
},
},
directives: [],
},
{
kind: Kind.INPUT_VALUE_DEFINITION,
description: {
kind: Kind.STRING,
value: "Favorite color, optional",
block: true,
},
name: {
kind: Kind.NAME,
value: "favorite_color",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ColorInput",
},
},
directives: [],
},
],
},
{
kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
description: {
kind: Kind.STRING,
value: "The input object sent when passing in a color",
block: true,
},
name: {
kind: Kind.NAME,
value: "ColorInput",
},
directives: [],
fields: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "red",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Int",
},
},
},
directives: [],
},
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "green",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Int",
},
},
},
directives: [],
},
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "blue",
},
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Int",
},
},
},
directives: [],
},
],
},
{
kind: Kind.OBJECT_TYPE_DEFINITION,
name: {
kind: Kind.NAME,
value: "Starship",
},
interfaces: [],
directives: [],
fields: [
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The ID of the starship",
block: true,
},
name: {
kind: Kind.NAME,
value: "id",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "ID",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "The name of the starship",
block: true,
},
name: {
kind: Kind.NAME,
value: "name",
},
arguments: [],
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "String",
},
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
description: {
kind: Kind.STRING,
value: "Length of the starship, along the longest axis",
block: true,
},
name: {
kind: Kind.NAME,
value: "length",
},
arguments: [
{
kind: Kind.INPUT_VALUE_DEFINITION,
name: {
kind: Kind.NAME,
value: "unit",
},
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "LengthUnit",
},
},
defaultValue: {
kind: Kind.ENUM,
value: "METER",
},
directives: [],
},
],
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Float",
},
},
directives: [],
},
{
kind: Kind.FIELD_DEFINITION,
name: {
kind: Kind.NAME,
value: "coordinates",
},
arguments: [],
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.LIST_TYPE,
type: {
kind: Kind.NON_NULL_TYPE,
type: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Float",
},
},
},
},
},
},
directives: [],
},
],
},
{
kind: Kind.UNION_TYPE_DEFINITION,
name: {
kind: Kind.NAME,
value: "SearchResult",
},
directives: [],
types: [
{
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Human",
},
},
{
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Droid",
},
},
{
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: "Starship",
},
},
],
},
],
})