nexus
Version:
Scalable, strongly typed GraphQL schema development
153 lines (141 loc) • 5.34 kB
text/typescript
import { assertValidName, GraphQLInterfaceTypeConfig } from 'graphql'
import type { FieldResolver, GetGen, InterfaceFieldsFor, ModificationType } from '../typegenTypeHelpers'
import type { ArgsRecord } from './args'
import { OutputDefinitionBlock, OutputDefinitionBuilder } from './definitionBlocks'
import { AbstractTypes, Maybe, NexusTypes, NonNullConfig, SourceTypingDef, withNexusSymbol } from './_types'
export type Implemented = GetGen<'interfaceNames'> | NexusInterfaceTypeDef<any>
export interface FieldModification<TypeName extends string, FieldName extends string> {
type?: ModificationType<TypeName, FieldName>
/** The description to annotate the GraphQL SDL */
description?: Maybe<string>
/** The resolve method we should be resolving the field with */
resolve?: FieldResolver<TypeName, FieldName>
/** You are allowed to add non-required args when modifying a field */
args?: Maybe<ArgsRecord>
/**
* Custom extensions, as supported in graphql-js
*
* @see https://github.com/graphql/graphql-js/issues/1527
*/
extensions?: GraphQLInterfaceTypeConfig<any, any>['extensions']
}
export interface FieldModificationDef<TypeName extends string, FieldName extends string>
extends FieldModification<TypeName, FieldName> {
field: FieldName
}
export type NexusInterfaceTypeConfig<TypeName extends string> = {
name: TypeName
// Really wanted to keep this here, but alas, it looks like there's some
// issues around inferring the generic.
// https://github.com/Microsoft/TypeScript/pull/29478
// https://github.com/Microsoft/TypeScript/issues/10195
//
// resolveType: AbstractTypeResolver<TypeName>;
definition(t: InterfaceDefinitionBlock<TypeName>): void
/**
* Configures the nullability for the type, check the documentation's "Getting Started" section to learn
* more about GraphQL Nexus's assumptions and configuration on nullability.
*/
nonNullDefaults?: NonNullConfig
/** The description to annotate the GraphQL SDL */
description?: Maybe<string>
/** Source type information for this type */
sourceType?: SourceTypingDef
/**
* Custom extensions, as supported in graphql-js
*
* @see https://github.com/graphql/graphql-js/issues/1527
*/
extensions?: GraphQLInterfaceTypeConfig<any, any>['extensions']
/** Adds this type as a method on the Object/Interface definition blocks */
asNexusMethod?: string
} & AbstractTypes.MaybeTypeDefConfigFieldResolveType<TypeName>
export interface InterfaceDefinitionBuilder<TypeName extends string> extends OutputDefinitionBuilder {
addInterfaces(toAdd: Implemented[]): void
addModification(toAdd: FieldModificationDef<TypeName, any>): void
}
export class InterfaceDefinitionBlock<TypeName extends string> extends OutputDefinitionBlock<TypeName> {
constructor(protected typeBuilder: InterfaceDefinitionBuilder<TypeName>) {
super(typeBuilder)
}
/** @param interfaceName */
implements(...interfaceName: Array<Implemented>) {
this.typeBuilder.addInterfaces(interfaceName)
}
/** Modifies a field added via an interface */
modify<FieldName extends Extract<InterfaceFieldsFor<TypeName>, string>>(
field: FieldName,
modifications: FieldModification<TypeName, FieldName>
) {
this.typeBuilder.addModification({ ...modifications, field })
}
}
export class NexusInterfaceTypeDef<TypeName extends string> {
constructor(readonly name: TypeName, protected config: NexusInterfaceTypeConfig<TypeName>) {
assertValidName(name)
}
get value() {
return this.config
}
}
withNexusSymbol(NexusInterfaceTypeDef, NexusTypes.Interface)
/**
* [API Docs](https://nxs.li/docs/api/interface-type) | [Abstract Types
* Guide](https://nxs.li/guides/abstract-types) | [2018 GraphQL
* Spec](https://spec.graphql.org/June2018/#sec-Interfaces)
*
* Defines an Interface type.
*
* Interface types are one of the two abstract type in GraphQL. They let you express polymorphic fields
* wherein the field may return a number of different object types but they all share some subset of fields.
* Interface types in Nexus also serve as a way to share a set of fields amongst different object types.
*
* @example
* export const Media = interfaceType({
* name: 'Media',
* resolveType(source) {
* return 'director' in source ? 'Movie' : 'Song'
* },
* definition(t) {
* t.string('url')
* },
* })
*
* export const Movie = objectType({
* name: 'Movie',
* definition(t) {
* t.implements('Media')
* t.string('director')
* },
* })
*
* export const Song = objectType({
* name: 'Song',
* definition(t) {
* t.implements('Media')
* t.string('album')
* },
* })
*
* // GraphQL SDL
* // -----------
* //
* // interface Media {
* // url: String
* // }
* //
* // type Movie implements Media {
* // director: String
* // url: String
* // }
* //
* // type Song implements Media {
* // album: String
* // url: String
* // }
*
* @param config Specify your interface's name, its fields, and more. See each config property's jsDoc for more detail.
*/
export function interfaceType<TypeName extends string>(config: NexusInterfaceTypeConfig<TypeName>) {
return new NexusInterfaceTypeDef<TypeName>(config.name, config)
}