@@ -0,0 +1,2 @@ | |||
/build/ | |||
/node_modules/ |
@@ -0,0 +1,16 @@ | |||
{ | |||
// Use IntelliSense to learn about possible attributes. | |||
// Hover to view descriptions of existing attributes. | |||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | |||
"version": "0.2.0", | |||
"configurations": [ | |||
{ | |||
"type": "node", | |||
"request": "launch", | |||
"name": "Launch Program", | |||
"program": "${workspaceFolder}/src/index.ts", | |||
"preLaunchTask": "tsc: build - tsconfig.json", | |||
"outFiles": ["${workspaceFolder}/build/**/*.js"] | |||
} | |||
] | |||
} |
@@ -0,0 +1,41 @@ | |||
# Prepation | |||
``npm init`` | |||
See More <https://graphql-code-generator.com/docs/getting-started/installation> | |||
``npm add --save graphql`` | |||
or | |||
``yarn add graphql`` | |||
``npm add --save-dev @graphql-codegen/cli`` | |||
or | |||
``yarn add -D @graphql-codegen/cli`` | |||
``npx graphql-codegen init`` | |||
``npm add --save-dev @graphql-codegen/typescript`` | |||
or | |||
``yarn add -D @graphql-codegen/typescript`` | |||
# Server | |||
``npm add apollo-server`` | |||
``npm install @types/node --save-dev`` | |||
``` | |||
npx tsc --init --rootDir src --outDir build \ | |||
--esModuleInterop --resolveJsonModule --lib es6 \ | |||
--module commonjs --allowJs true --noImplicitAny true | |||
``` | |||
``npm install --save-dev ts-node nodemon rimraf`` | |||
# UI | |||
* init new Angular project ``ng new graphql-demo`` |
@@ -0,0 +1,12 @@ | |||
overwrite: true | |||
schema: ./src/schema/*.ts | |||
generates: | |||
src/generated/graphql.ts: | |||
plugins: | |||
- typescript | |||
- typescript-resolvers | |||
- typescript-graphql-request | |||
- typescript-operations | |||
./graphql.schema.json: | |||
plugins: | |||
- "introspection" |
@@ -0,0 +1,6 @@ | |||
{ | |||
"watch": ["src"], | |||
"ext": ".ts,.js", | |||
"ignore": [], | |||
"exec": "ts-node ./src/index.ts" | |||
} |
@@ -0,0 +1,34 @@ | |||
{ | |||
"name": "graphql-pizza-demo", | |||
"version": "1.0.0", | |||
"description": "", | |||
"main": "build/index.js", | |||
"dependencies": { | |||
"apollo-server": "^2.15.1", | |||
"graphql": "^15.3.0", | |||
"typescript": "^3.9.6" | |||
}, | |||
"devDependencies": { | |||
"@graphql-codegen/cli": "1.16.3", | |||
"@graphql-codegen/introspection": "1.16.3", | |||
"@graphql-codegen/typescript": "^1.16.3", | |||
"@graphql-codegen/typescript-graphql-request": "^1.16.3", | |||
"@graphql-codegen/typescript-operations": "^1.16.3", | |||
"@graphql-codegen/typescript-resolvers": "1.16.3", | |||
"@types/node": "^14.0.20", | |||
"nodemon": "^2.0.4", | |||
"rimraf": "^3.0.2", | |||
"ts-node": "^8.10.2" | |||
}, | |||
"scripts": { | |||
"prebuild": "npm run generate", | |||
"prestart": "npm run generate", | |||
"test": "echo \"Error: no test specified\" && exit 1", | |||
"generate": "graphql-codegen --config codegen.yml", | |||
"start": "nodemon", | |||
"start:live": "npm run build && node build/index.js", | |||
"build": "rimraf ./build && tsc" | |||
}, | |||
"author": "", | |||
"license": "ISC" | |||
} |
@@ -0,0 +1,29 @@ | |||
import { Pizza } from "../generated/graphql"; | |||
import { ToppingList } from "./topping-list"; | |||
import { ToppingResolver } from "../resolver/topping-resolver"; | |||
export const PizzaList: Pizza[] = [ | |||
{ | |||
id: "0", | |||
name: "Tonno", | |||
toppings: [ | |||
ToppingResolver.getByName("Tunna"), | |||
ToppingResolver.getByName("Onion") | |||
] | |||
}, | |||
{ | |||
id: "1", | |||
name: "Salami", | |||
toppings: [ | |||
ToppingResolver.getByName("Salami"), | |||
] | |||
}, | |||
{ | |||
id: "2", | |||
name: "420", | |||
toppings: [ | |||
ToppingResolver.getByName("Pineapple"), | |||
] | |||
} | |||
] | |||
@@ -0,0 +1,27 @@ | |||
import { Pizza, Topping } from "../generated/graphql"; | |||
export const ToppingList: Topping[] = [ | |||
{ | |||
id: "1", | |||
name: "Tunna", | |||
}, | |||
{ | |||
id: "2", | |||
name: "Onion", | |||
}, | |||
{ | |||
id: "3", | |||
name: "Pineapple", | |||
}, | |||
{ | |||
id: "4", | |||
name: "Salami", | |||
}, | |||
{ | |||
id: "5", | |||
name: "fish sticks", | |||
} | |||
] | |||
@@ -0,0 +1,202 @@ | |||
import { GraphQLResolveInfo } from 'graphql'; | |||
import { GraphQLClient } from 'graphql-request'; | |||
import { print } from 'graphql'; | |||
import gql from 'graphql-tag'; | |||
export type Maybe<T> = T | null; | |||
export type Exact<T extends { [key: string]: any }> = { [K in keyof T]: T[K] }; | |||
export type RequireFields<T, K extends keyof T> = { [X in Exclude<keyof T, K>]?: T[X] } & { [P in K]-?: NonNullable<T[P]> }; | |||
/** All built-in and custom scalars, mapped to their actual values */ | |||
export type Scalars = { | |||
ID: string; | |||
String: string; | |||
Boolean: boolean; | |||
Int: number; | |||
Float: number; | |||
}; | |||
export type Pizza = { | |||
__typename?: 'Pizza'; | |||
id: Scalars['ID']; | |||
name: Scalars['String']; | |||
toppings: Array<Topping>; | |||
}; | |||
export type Topping = { | |||
__typename?: 'Topping'; | |||
id: Scalars['ID']; | |||
name: Scalars['String']; | |||
}; | |||
export type Query = { | |||
__typename?: 'Query'; | |||
getPizzaById: Pizza; | |||
getToppingById: Topping; | |||
getPizzaByName: Pizza; | |||
getToppingByName: Topping; | |||
listPizza: Array<Maybe<Pizza>>; | |||
listTopping: Array<Maybe<Topping>>; | |||
}; | |||
export type QueryGetPizzaByIdArgs = { | |||
pizzaId: Scalars['ID']; | |||
}; | |||
export type QueryGetToppingByIdArgs = { | |||
toppingId: Scalars['ID']; | |||
}; | |||
export type QueryGetPizzaByNameArgs = { | |||
pizzaName: Scalars['ID']; | |||
}; | |||
export type QueryGetToppingByNameArgs = { | |||
toppingName: Scalars['ID']; | |||
}; | |||
export type ResolverTypeWrapper<T> = Promise<T> | T; | |||
export type LegacyStitchingResolver<TResult, TParent, TContext, TArgs> = { | |||
fragment: string; | |||
resolve: ResolverFn<TResult, TParent, TContext, TArgs>; | |||
}; | |||
export type NewStitchingResolver<TResult, TParent, TContext, TArgs> = { | |||
selectionSet: string; | |||
resolve: ResolverFn<TResult, TParent, TContext, TArgs>; | |||
}; | |||
export type StitchingResolver<TResult, TParent, TContext, TArgs> = LegacyStitchingResolver<TResult, TParent, TContext, TArgs> | NewStitchingResolver<TResult, TParent, TContext, TArgs>; | |||
export type Resolver<TResult, TParent = {}, TContext = {}, TArgs = {}> = | |||
| ResolverFn<TResult, TParent, TContext, TArgs> | |||
| StitchingResolver<TResult, TParent, TContext, TArgs>; | |||
export type ResolverFn<TResult, TParent, TContext, TArgs> = ( | |||
parent: TParent, | |||
args: TArgs, | |||
context: TContext, | |||
info: GraphQLResolveInfo | |||
) => Promise<TResult> | TResult; | |||
export type SubscriptionSubscribeFn<TResult, TParent, TContext, TArgs> = ( | |||
parent: TParent, | |||
args: TArgs, | |||
context: TContext, | |||
info: GraphQLResolveInfo | |||
) => AsyncIterator<TResult> | Promise<AsyncIterator<TResult>>; | |||
export type SubscriptionResolveFn<TResult, TParent, TContext, TArgs> = ( | |||
parent: TParent, | |||
args: TArgs, | |||
context: TContext, | |||
info: GraphQLResolveInfo | |||
) => TResult | Promise<TResult>; | |||
export interface SubscriptionSubscriberObject<TResult, TKey extends string, TParent, TContext, TArgs> { | |||
subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>; | |||
resolve?: SubscriptionResolveFn<TResult, { [key in TKey]: TResult }, TContext, TArgs>; | |||
} | |||
export interface SubscriptionResolverObject<TResult, TParent, TContext, TArgs> { | |||
subscribe: SubscriptionSubscribeFn<any, TParent, TContext, TArgs>; | |||
resolve: SubscriptionResolveFn<TResult, any, TContext, TArgs>; | |||
} | |||
export type SubscriptionObject<TResult, TKey extends string, TParent, TContext, TArgs> = | |||
| SubscriptionSubscriberObject<TResult, TKey, TParent, TContext, TArgs> | |||
| SubscriptionResolverObject<TResult, TParent, TContext, TArgs>; | |||
export type SubscriptionResolver<TResult, TKey extends string, TParent = {}, TContext = {}, TArgs = {}> = | |||
| ((...args: any[]) => SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>) | |||
| SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>; | |||
export type TypeResolveFn<TTypes, TParent = {}, TContext = {}> = ( | |||
parent: TParent, | |||
context: TContext, | |||
info: GraphQLResolveInfo | |||
) => Maybe<TTypes> | Promise<Maybe<TTypes>>; | |||
export type IsTypeOfResolverFn<T = {}> = (obj: T, info: GraphQLResolveInfo) => boolean | Promise<boolean>; | |||
export type NextResolverFn<T> = () => Promise<T>; | |||
export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs = {}> = ( | |||
next: NextResolverFn<TResult>, | |||
parent: TParent, | |||
args: TArgs, | |||
context: TContext, | |||
info: GraphQLResolveInfo | |||
) => TResult | Promise<TResult>; | |||
/** Mapping between all available schema types and the resolvers types */ | |||
export type ResolversTypes = { | |||
Pizza: ResolverTypeWrapper<Pizza>; | |||
ID: ResolverTypeWrapper<Scalars['ID']>; | |||
String: ResolverTypeWrapper<Scalars['String']>; | |||
Topping: ResolverTypeWrapper<Topping>; | |||
Query: ResolverTypeWrapper<{}>; | |||
Boolean: ResolverTypeWrapper<Scalars['Boolean']>; | |||
}; | |||
/** Mapping between all available schema types and the resolvers parents */ | |||
export type ResolversParentTypes = { | |||
Pizza: Pizza; | |||
ID: Scalars['ID']; | |||
String: Scalars['String']; | |||
Topping: Topping; | |||
Query: {}; | |||
Boolean: Scalars['Boolean']; | |||
}; | |||
export type PizzaResolvers<ContextType = any, ParentType extends ResolversParentTypes['Pizza'] = ResolversParentTypes['Pizza']> = { | |||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>; | |||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>; | |||
toppings?: Resolver<Array<ResolversTypes['Topping']>, ParentType, ContextType>; | |||
__isTypeOf?: IsTypeOfResolverFn<ParentType>; | |||
}; | |||
export type ToppingResolvers<ContextType = any, ParentType extends ResolversParentTypes['Topping'] = ResolversParentTypes['Topping']> = { | |||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>; | |||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>; | |||
__isTypeOf?: IsTypeOfResolverFn<ParentType>; | |||
}; | |||
export type QueryResolvers<ContextType = any, ParentType extends ResolversParentTypes['Query'] = ResolversParentTypes['Query']> = { | |||
getPizzaById?: Resolver<ResolversTypes['Pizza'], ParentType, ContextType, RequireFields<QueryGetPizzaByIdArgs, 'pizzaId'>>; | |||
getToppingById?: Resolver<ResolversTypes['Topping'], ParentType, ContextType, RequireFields<QueryGetToppingByIdArgs, 'toppingId'>>; | |||
getPizzaByName?: Resolver<ResolversTypes['Pizza'], ParentType, ContextType, RequireFields<QueryGetPizzaByNameArgs, 'pizzaName'>>; | |||
getToppingByName?: Resolver<ResolversTypes['Topping'], ParentType, ContextType, RequireFields<QueryGetToppingByNameArgs, 'toppingName'>>; | |||
listPizza?: Resolver<Array<Maybe<ResolversTypes['Pizza']>>, ParentType, ContextType>; | |||
listTopping?: Resolver<Array<Maybe<ResolversTypes['Topping']>>, ParentType, ContextType>; | |||
}; | |||
export type Resolvers<ContextType = any> = { | |||
Pizza?: PizzaResolvers<ContextType>; | |||
Topping?: ToppingResolvers<ContextType>; | |||
Query?: QueryResolvers<ContextType>; | |||
}; | |||
/** | |||
* @deprecated | |||
* Use "Resolvers" root object instead. If you wish to get "IResolvers", add "typesPrefix: I" to your config. | |||
*/ | |||
export type IResolvers<ContextType = any> = Resolvers<ContextType>; | |||
export type SdkFunctionWrapper = <T>(action: () => Promise<T>) => Promise<T>; | |||
const defaultWrapper: SdkFunctionWrapper = sdkFunction => sdkFunction(); | |||
export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) { | |||
return { | |||
}; | |||
} | |||
export type Sdk = ReturnType<typeof getSdk>; |
@@ -0,0 +1,58 @@ | |||
import { ApolloServer } from "apollo-server"; | |||
import { Resolvers } from "./generated/graphql"; | |||
import { pizzaSchema } from "./schema/pizza"; | |||
import { PizzaResolver } from "./resolver/pizza-resolver"; | |||
import { ToppingResolver } from "./resolver/topping-resolver"; | |||
const resolvers: Resolvers = { | |||
Topping: { | |||
id: (root, args, context) => { | |||
return root.id | |||
}, | |||
name: (root, args, context) => { | |||
return root.name | |||
}, | |||
}, | |||
Pizza: { | |||
id: (root, args, context) => { | |||
return root.id | |||
}, | |||
name: (root, args, context) => { | |||
return root.name | |||
}, | |||
toppings: (root, args, context) => { | |||
return root.toppings | |||
}, | |||
}, | |||
Query: { | |||
getPizzaById: (root, args, context) => { | |||
return PizzaResolver.getById(args.pizzaId); | |||
}, | |||
getPizzaByName: (root, args, context) => { | |||
return PizzaResolver.getByName(args.pizzaName); | |||
}, | |||
listPizza: (root, args, context) => { | |||
return PizzaResolver.list(); | |||
}, | |||
getToppingById: (root, args, context) => { | |||
return ToppingResolver.getById(args.toppingId); | |||
}, | |||
getToppingByName: (root, args, context) => { | |||
return ToppingResolver.getByName(args.toppingName); | |||
}, | |||
listTopping: (root, args, context) => { | |||
return ToppingResolver.list(); | |||
}, | |||
} | |||
} | |||
const server = new ApolloServer({ | |||
typeDefs: pizzaSchema, | |||
resolvers: resolvers as any | |||
}); | |||
server.listen().then(({ url }) => { | |||
console.log(`🚀 Server ready at ${url}`) | |||
}); |
@@ -0,0 +1,17 @@ | |||
import { Topping, Pizza, } from "../generated/graphql"; | |||
import { PizzaList } from "../data/pizza-list"; | |||
export class PizzaResolver { | |||
static getById = (id: string): Pizza => { | |||
return PizzaList.filter(pizza => pizza.id === id)[0]; | |||
}; | |||
static list = (): Pizza[] => { | |||
return PizzaList; | |||
}; | |||
static getByName = (name: string): Pizza => { | |||
return PizzaList.filter(pizza => pizza.name === name)[0]; | |||
}; | |||
} |
@@ -0,0 +1,16 @@ | |||
import { Topping, } from "../generated/graphql"; | |||
import { ToppingList } from "../data/topping-list"; | |||
export class ToppingResolver { | |||
static getById = (id: string): Topping => { | |||
return ToppingList.filter(topping => topping.id === id)[0]; | |||
}; | |||
static getByName = (name: string): Topping => { | |||
return ToppingList.filter(topping => topping.name === name)[0]; | |||
}; | |||
static list = (): Topping[] => { | |||
return ToppingList; | |||
}; | |||
} |
@@ -0,0 +1,24 @@ | |||
import gql from "graphql-tag"; | |||
export const pizzaSchema = gql` | |||
type Pizza { | |||
id: ID! | |||
name: String! | |||
toppings: [Topping!]! | |||
} | |||
type Topping { | |||
id: ID! | |||
name: String! | |||
} | |||
type Query { | |||
getPizzaById(pizzaId: ID!): Pizza! | |||
getToppingById(toppingId: ID!): Topping! | |||
getPizzaByName(pizzaName: ID!): Pizza! | |||
getToppingByName(toppingName: ID!): Topping! | |||
listPizza: [Pizza]! | |||
listTopping: [Topping]! | |||
} | |||
`; |
@@ -0,0 +1,20 @@ | |||
{ | |||
"compilerOptions": { | |||
"target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, | |||
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, | |||
"lib": [ | |||
"es6" | |||
] /* Specify library files to be included in the compilation. */, | |||
"allowJs": true /* Allow javascript files to be compiled. */, | |||
"outDir": "build" /* Redirect output structure to the directory. */, | |||
"rootDir": "src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | |||
"strict": true /* Enable all strict type-checking options. */, | |||
"skipLibCheck": true, | |||
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, | |||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, | |||
"resolveJsonModule": true /* Include modules imported with '.json' extension */, | |||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, | |||
"sourceMap": true, | |||
} | |||
} |