@pothos/plugin-dataloader
Version:
A Pothos plugin for attaching dataloader to object types
241 lines (209 loc) • 6.65 kB
text/typescript
import SchemaBuilder, {
type InterfaceParam,
type ObjectParam,
type OutputRef,
PothosSchemaError,
type SchemaTypes,
type ShapeFromTypeParam,
} from '@pothos/core';
import { ImplementableLoadableNodeRef, LoadableNodeRef } from './refs';
import { ImplementableLoadableInterfaceRef } from './refs/interface';
import { ImplementableLoadableObjectRef } from './refs/object';
import { LoadableUnionRef } from './refs/union';
import type {
DataloaderKey,
LoadableInterfaceOptions,
LoadableUnionOptions,
ShapeFromLoadResult,
} from './types';
import type { DataloaderObjectTypeOptions, LoadableNodeOptions } from './types';
import { dataloaderGetter } from './util';
const schemaBuilderProto = SchemaBuilder.prototype as PothosSchemaTypes.SchemaBuilder<SchemaTypes>;
schemaBuilderProto.loadableObjectRef = function loadableObjectRef(name, options) {
return new ImplementableLoadableObjectRef(this, name, options);
};
schemaBuilderProto.loadableInterfaceRef = function loadableInterfaceRef(name, options) {
return new ImplementableLoadableInterfaceRef(this, name, options);
};
schemaBuilderProto.loadableNodeRef = function loadableNodeRef(name, options) {
return new ImplementableLoadableNodeRef(this, name, options);
};
schemaBuilderProto.loadableObject = function loadableObject<
LoadResult,
Key extends DataloaderKey,
const Interfaces extends InterfaceParam<SchemaTypes>[],
NameOrRef extends ObjectParam<SchemaTypes> | string,
CacheKey = Key,
Shape = ShapeFromLoadResult<LoadResult>,
>(
nameOrRef: NameOrRef,
options: DataloaderObjectTypeOptions<
SchemaTypes,
LoadResult,
Key,
Interfaces,
NameOrRef,
CacheKey,
Shape
>,
) {
const name =
typeof nameOrRef === 'string'
? nameOrRef
: ((options as { name?: string }).name ?? (nameOrRef as { name: string }).name);
const ref = new ImplementableLoadableObjectRef<SchemaTypes, Key | Shape, Shape, Key, CacheKey>(
this,
name,
options as never,
);
ref.implement(options);
if (typeof nameOrRef !== 'string') {
this.configStore.associateParamWithRef(nameOrRef, ref);
}
return ref;
};
schemaBuilderProto.loadableInterface = function loadableInterface<
LoadResult,
Key extends DataloaderKey,
const Interfaces extends InterfaceParam<SchemaTypes>[],
NameOrRef extends InterfaceParam<SchemaTypes> | string,
CacheKey = Key,
Shape = ShapeFromLoadResult<LoadResult>,
>(
nameOrRef: NameOrRef,
options: LoadableInterfaceOptions<
SchemaTypes,
LoadResult,
Key,
Interfaces,
NameOrRef,
CacheKey,
Shape
>,
) {
const name =
typeof nameOrRef === 'string'
? nameOrRef
: ((options as { name?: string }).name ?? (nameOrRef as { name: string }).name);
const ref = new ImplementableLoadableInterfaceRef<SchemaTypes, Shape, Shape, Key, CacheKey>(
this,
name,
options as never,
);
ref.implement(options);
if (typeof nameOrRef !== 'string') {
this.configStore.associateParamWithRef(nameOrRef, ref);
}
return ref;
};
schemaBuilderProto.loadableUnion = function loadableUnion<
Key extends DataloaderKey,
Member extends ObjectParam<SchemaTypes>,
CacheKey = Key,
Shape = ShapeFromTypeParam<SchemaTypes, Member, false>,
>(
name: string,
{
load,
toKey,
sort,
cacheResolved,
loaderOptions,
...options
}: LoadableUnionOptions<SchemaTypes, Key, Member, CacheKey, Shape>,
) {
const getDataloader = dataloaderGetter<Key, Shape, CacheKey>(loaderOptions, load, toKey, sort);
const ref = new LoadableUnionRef<SchemaTypes, Shape, Shape, Key, CacheKey>(name, getDataloader);
const unionRef = this.unionType(name, {
...options,
extensions: {
getDataloader,
cacheResolved: typeof cacheResolved === 'function' ? cacheResolved : cacheResolved && toKey,
},
});
this.configStore.associateParamWithRef(ref, unionRef);
return ref;
};
const TloadableNode = schemaBuilderProto.loadableNode;
schemaBuilderProto.loadableNode = function loadableNode<
LoadResult extends NameOrRef extends ObjectParam<SchemaTypes>
? ShapeFromTypeParam<SchemaTypes, NameOrRef, false> | Error
: unknown,
const Interfaces extends InterfaceParam<SchemaTypes>[],
NameOrRef extends ObjectParam<SchemaTypes> | string,
IDShape extends bigint | number | string = string,
Key extends bigint | number | string = IDShape,
CacheKey = Key,
Shape = ShapeFromLoadResult<LoadResult>,
>(
this: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
nameOrRef: NameOrRef,
options: LoadableNodeOptions<
SchemaTypes,
LoadResult,
Interfaces,
NameOrRef,
IDShape,
Key,
CacheKey,
Shape
>,
) {
if (
typeof (this as PothosSchemaTypes.SchemaBuilder<SchemaTypes> & Record<string, unknown>)
.nodeInterfaceRef !== 'function'
) {
throw new PothosSchemaError(
'builder.loadableNode requires @pothos/plugin-relay to be installed',
);
}
const name =
typeof nameOrRef === 'string'
? nameOrRef
: ((options as { name?: string }).name ?? (nameOrRef as { name: string }).name);
const ref = new LoadableNodeRef<SchemaTypes, Shape, Shape, IDShape, Key, CacheKey>(
this,
name,
options as never,
);
(this as typeof this & { node: (ref: unknown, options: unknown) => void }).node(ref, {
...options,
extensions: {
...options.extensions,
pothosParseGlobalID: options.id.parse,
getDataloader: ref.getDataloader,
cacheResolved:
typeof options.cacheResolved === 'function'
? options.cacheResolved
: options.cacheResolved && options.toKey,
},
loadManyWithoutCache: (ids: Key[], context: SchemaTypes['Context']) =>
ref.getDataloader(context).loadMany(ids),
isTypeOf:
options.isTypeOf ??
(typeof nameOrRef === 'function'
? (maybeNode: unknown) => {
if (!maybeNode) {
return false;
}
if (maybeNode instanceof (nameOrRef as Function)) {
return true;
}
const proto = Object.getPrototypeOf(maybeNode) as { constructor: unknown };
try {
if (proto?.constructor) {
const config = this.configStore.getTypeConfig(proto.constructor as OutputRef);
return config.name === name;
}
} catch {
// ignore
}
return false;
}
: undefined),
});
if (typeof nameOrRef !== 'string') {
this.configStore.associateParamWithRef(nameOrRef, ref);
}
return ref;
} as unknown as typeof TloadableNode;