Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.format.enable": false,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.formatOnSave": true,
"importSorter.generalConfiguration.sortOnBeforeSave": true,
"importSorter.importStringConfiguration.hasSemicolon": false,
"importSorter.importStringConfiguration.maximumNumberOfImportExpressionsPerLine.type": "newLineEachExpressionAfterCountLimitExceptIfOnlyOne",
"importSorter.importStringConfiguration.maximumNumberOfImportExpressionsPerLine.count": 80,
"importSorter.importStringConfiguration.tabSize": 2,
"importSorter.importStringConfiguration.quoteMark": "single"
}
}
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"dependencies": {
"dataloader": "^2.0.0",
"graphql": "^14.5.8",
"graphql-middleware": "^4.0.2",
"memcached": "^2.2.2"
},
"devDependencies": {
Expand All @@ -39,10 +40,8 @@
"@nexus/schema": "^0.13.1",
"@types/memcached": "^2.2.6",
"apollo-server": "^2.13.1",
"graphql-middleware": "^4.0.2",
"graphql-tools": "^5.0.0",
"link-module-alias": "^1.2.0",
"prettier": "^2.0.5",
"rimraf": "^3.0.2",
"tsc-watch": "^4.2.5",
"typescript": "^3.9.2"
Expand Down
4 changes: 2 additions & 2 deletions src/examples/with-graphql-tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApolloServer } from 'apollo-server'
import fs from 'fs'
import { cached } from 'graphql-cached'
import { cached } from '../../index'
import { applyMiddleware } from 'graphql-middleware'
import { makeExecutableSchema } from 'graphql-tools'
import Memcached from 'memcached'
Expand Down Expand Up @@ -34,7 +34,7 @@ const cachedSchema = applyMiddleware(
Query: {
user: {
lifetime: 10,
key(_parent: any, args: any) {
key(parent, args) {
return JSON.stringify(args)
},
},
Expand Down
8 changes: 6 additions & 2 deletions src/examples/with-nexus/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApolloServer } from 'apollo-server'
import { cached } from 'graphql-cached'
import { cached } from '../../index'
import { applyMiddleware } from 'graphql-middleware'
import Memcached from 'memcached'
import path from 'path'
Expand Down Expand Up @@ -46,16 +46,20 @@ const cachedSchema = applyMiddleware(
cached<Context, ResolversFromNexusGenFieldTypes<NexusGenFieldTypes>>(
{
Query: {
// @ts-ignore
user: {
lifetime: 10,
key(_parent: any, args: any) {
// @ts-ignore
key(_parent, args) {
return JSON.stringify(args)
},
},
},
User: {
// @ts-ignore
image: {
lifetime: 1,
// @ts-ignore
key(parent) {
return parent.imageId
},
Expand Down
43 changes: 24 additions & 19 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import DataLoader from 'dataloader'
import { GraphQLResolveInfo } from 'graphql'
import { promisify } from 'util'

import { CacheField, Config, ResolversBase, T } from './types'
import type { Config, T, ResolverLike, CacheField } from './types'
import type { IMiddlewareTypeMap } from 'graphql-middleware'

const DEFAULT_LIFETIME = 10

export function cached<Context, Resolvers = ResolversBase>(
t: T<Required<Resolvers>>,
export function cached<Context, Resolvers>(
t: T<Resolvers>,
config: Config<Context>
) {
const _t: any = t

): IMiddlewareTypeMap<any, Context> {
/**
* Initialize cache getter(loader), setter
*/
Expand All @@ -28,17 +27,19 @@ export function cached<Context, Resolvers = ResolversBase>(
}
)

const resolveWithCache = async (
resolve: any,
parent: any,
args: any,
const resolveWithCache = async<Type, Parent, Arg>(
resolve: ResolverLike<Type, Parent, Arg, Context>,
parent: Parent,
args: Arg,
context: Context,
info: GraphQLResolveInfo
) => {
const _typeName = info.parentType.toString()
const _fieldName = info.fieldName
): Promise<Type> => {
const _typeName = info.parentType.toString() as keyof typeof t;
const _type = t[_typeName];

const _fieldName = info.fieldName as keyof typeof _type;
const _field = _type[_fieldName] as CacheField<Type, Parent, Arg, Context>;

const _field: CacheField<any, any, any> = _t[_typeName][_fieldName]
const _fieldKey = 'key' in _field ? _field.key : _field
const _fieldSerializer = 'serializer' in _field ? _field.serializer : null
const _fieldLifetime = 'lifetime' in _field ? _field.lifetime : null
Expand All @@ -64,7 +65,7 @@ export function cached<Context, Resolvers = ResolversBase>(
* Get cache from cache storage
*/
config.beforeGet?.(key, null)
const cachedItem = (await cacheLoader.load(key)) || null
const cachedItem = (await cacheLoader.load(key) as unknown as Type) || null
config.afterGet?.(key, cachedItem)

if (cachedItem) {
Expand Down Expand Up @@ -104,12 +105,16 @@ export function cached<Context, Resolvers = ResolversBase>(
}
}

const resolvers: any = {}
const resolvers: any = {};

for (const typeName of Object.keys(_t)) {
resolvers[typeName] = {}
for (const typeName of Object.keys(t)) {
resolvers[typeName] = {};

for (const fieldName of Object.keys(_t[typeName])) {
const _type = t[typeName as keyof typeof t];
if (!_type) {
continue;
}
for (const fieldName of Object.keys(_type as NonNullable<typeof _type>)) {
resolvers[typeName][fieldName] = resolveWithCache
}
}
Expand Down
17 changes: 8 additions & 9 deletions src/types/resolvers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
export interface ResolversBase {
[typeName: string]:
| {
[fieldName: string]:
| ((parent: any, args: any, context: any, info: any) => any)
| undefined
}
| undefined
}
import type { GraphQLResolveInfo } from "graphql";

export type ResolverLike<Type, Parent, Arg, Context = any> = (
root: Parent,
args: Arg,
context: Context,
info: GraphQLResolveInfo
) => Type | Promise<Type>;
51 changes: 22 additions & 29 deletions src/types/t.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,50 @@
import { GraphQLResolveInfo } from 'graphql'
import type { ResolverLike } from "./resolvers";

export type T<RequiredResolvers> = {
[TypeName in keyof RequiredResolvers]?: {
[FieldName in keyof RequiredResolvers[TypeName]]?: CacheField<
Parameters<RequiredResolvers[TypeName][FieldName]>[0],
Parameters<RequiredResolvers[TypeName][FieldName]>[1],
Parameters<RequiredResolvers[TypeName][FieldName]>[2]
>
}
}
export type T<Resolvers> = (
Resolvers extends { [key: string]: infer _ }
? { [P in keyof Resolvers]?: T<Resolvers[P]> }
: Resolvers extends ResolverLike<infer Type, infer Parent, infer Arg, infer Context>
? CacheField<Type, Parent, Arg, Context>
: never
);

export type CacheField<Parent, Arg, Context> =
export type CacheField<Type, Parent, Arg, Context> = (
| CacheFieldKey<Parent, Arg, Context>
| {
/**
/**
* Create a cache key by combining parent, args, context and info.
* @param {Object} parent
* @param {Object} args
* @param {Object} context
* @param {Object} info
*/
key: CacheFieldKey<Parent, Arg, Context>
key: CacheFieldKey<Parent, Arg, Context>;

/**
/**
* How much time to keep the cache (seconds)
*/
lifetime?: number
lifetime?: number;

/**
/**
* Preprocess item before storing in cache and after fetching from cache
*/
serializer?: CacheFieldSerializer
}

export type CacheFieldKey<Parent, Arg, Context> = (
parent: Parent,
args: Arg,
context: Context,
info: GraphQLResolveInfo
) => string
serializer?: CacheFieldSerializer<Type>;
}
);

export type CacheFieldSerializer = {
export type CacheFieldKey<Parent, Arg, Context> = ResolverLike<string, Parent, Arg, Context>;
export type CacheFieldSerializer<Type> = {
/**
* Preprocess item before storing it in the cache
* @param {Object} item
* @returns {Object} Serialized item
*/
serialize(item: any): any
serialize(item: Type): any;

/**
* Preprocess item after fetching from cache
* @param {Object} Serialized item
* @returns {Object} Item
*/
deserialize(serializedItem: any): any
}
deserialize(serializedItem: any): Type;
};
5 changes: 0 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3959,11 +3959,6 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=

prettier@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4"
integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==

prisma-json-schema@0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/prisma-json-schema/-/prisma-json-schema-0.1.3.tgz#6c302db8f464f8b92e8694d3f7dd3f41ac9afcbe"
Expand Down