| @@ -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, | |||
| } | |||
| } | |||