{ query.greet({ name: 'Mish' }) }
136 | } 137 | ``` 138 | 139 | [→ GQty Docs: Usage with React](https://gqty.dev/guides/react/read) 140 | -------------------------------------------------------------------------------- /www/docs/advanced/defer-stream.md: -------------------------------------------------------------------------------- 1 | # Defer and Stream 2 | 3 | ::: warning 4 | Stream and Defer are experimental features. There is no yet a stable specification for the incremental delivery protocol. 5 | ::: 6 | 7 | Stream and defer are directives that allow you to improve latency for clients by sending the most important data as soon as it's ready. 8 | 9 | Install a yoga plugin to enable `@stream` and `@defer` directives (requires yoga v4 and higer) 10 | 11 | ::: code-group 12 | ```sh [npm] 13 | $ npm i @graphql-yoga/plugin-defer-stream 14 | ``` 15 | 16 | ```sh [pnpm] 17 | $ pnpm add @graphql-yoga/plugin-defer-stream 18 | ``` 19 | 20 | ```sh [yarn] 21 | $ yarn add @graphql-yoga/plugin-defer-stream 22 | ``` 23 | 24 | ```sh [bun] 25 | $ bun i @graphql-yoga/plugin-defer-stream 26 | ``` 27 | ::: 28 | 29 | Example Garph schema with streamed and slow fields: 30 | 31 | ```ts 32 | import { g, InferResolvers, buildSchema } from './../src/index' 33 | import { createYoga } from 'graphql-yoga' 34 | import { useDeferStream } from '@graphql-yoga/plugin-defer-stream' 35 | import { createServer } from 'node:http' 36 | 37 | const queryType = g.type('Query', { 38 | alphabet: g.string().list().description(`This field can be @stream'ed`), 39 | fastField: g.string().description('A field that resolves fast.'), 40 | slowField: g 41 | .string() 42 | .optional() 43 | .description( 44 | 'A field that resolves slowly. Maybe you want to @defer this field ;)' 45 | ) 46 | .args({ 47 | waitFor: g.int().default(5000), 48 | }) 49 | }) 50 | 51 | const wait = (time: number) => new Promise(resolve => setTimeout(resolve, time)) 52 | 53 | const resolvers: InferResolvers<{ Query: typeof queryType }, {}> = { 54 | Query: { 55 | async *alphabet() { 56 | for (const character of ['a', 'b', 'c', 'd', 'e', 'f', 'g']) { 57 | yield character 58 | await wait(1000) 59 | } 60 | }, 61 | fastField: async () => { 62 | await wait(100) 63 | return 'I am speed' 64 | }, 65 | slowField: async (_, { waitFor }) => { 66 | await wait(waitFor) 67 | return 'I am slow' 68 | } 69 | } 70 | } 71 | 72 | const schema = buildSchema({ g, resolvers }) 73 | const yoga = createYoga({ 74 | schema, 75 | plugins: [useDeferStream()] 76 | }) 77 | 78 | const server = createServer(yoga) 79 | 80 | server.listen(4000, () => { 81 | console.info('Server is running on http://localhost:4000/graphql') 82 | }) 83 | ``` 84 | 85 | Start the server: 86 | 87 | ``` 88 | npx ts-node server.ts 89 | ``` 90 | 91 | ### Streamed fields 92 | 93 | The `@stream` directive allows you to stream the individual items of a field of the list type as the items are available. 94 | 95 | Visit http://localhost:4000/graphql and paste the following operation into the left panel: 96 | 97 | ```graphql 98 | query StreamAlphabet { 99 | alphabet @stream 100 | } 101 | ``` 102 | 103 | Then press the Play (Execute Query) button. 104 | 105 | Alternatively, you can also send the stream operation via curl: 106 | 107 | ```sh 108 | curl -g -X POST \ 109 | -H "accept:multipart/mixed" \ 110 | -H "content-type: application/json" \ 111 | -d '{"query":"query StreamAlphabet { alphabet @stream }"}' \ 112 | http://localhost:4000/graphql 113 | ``` 114 | 115 | ### Deferred fields 116 | 117 | The `@defer` directive allows you to post-pone the delivery of one or more (slow) fields grouped in an inlined or spread fragment. 118 | 119 | Visit http://localhost:4000/graphql and paste the following operation into the left panel: 120 | 121 | ```graphql 122 | query SlowAndFastFieldWithDefer { 123 | ... on Query @defer { 124 | slowField 125 | } 126 | fastField 127 | } 128 | ``` 129 | 130 | Then press the Play (Execute Query) button. 131 | 132 | Alternatively, you can also send the defer operation via curl: 133 | 134 | ```sh 135 | curl -g -X POST \ 136 | -H "accept:multipart/mixed" \ 137 | -H "content-type: application/json" \ 138 | -d '{"query":"query SlowAndFastFieldWithDefer { ... on Query @defer { slowField } fastField }"}' \ 139 | http://localhost:4000/graphql 140 | ``` 141 | -------------------------------------------------------------------------------- /.vitepress/config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | 3 | // https://vitepress.dev/reference/site-config 4 | export default defineConfig({ 5 | srcDir: 'www', 6 | ignoreDeadLinks: true, 7 | title: "Garph", 8 | description: "GraphQL. Reimagined", 9 | lastUpdated: true, 10 | markdown: { 11 | theme: { 12 | light: 'github-light', 13 | dark: 'github-dark', 14 | }, 15 | }, 16 | themeConfig: { 17 | // https://vitepress.dev/reference/default-theme-config 18 | logo: { 19 | light: '/logo/r6N.svg', 20 | dark: '/logo/r5m.svg', 21 | }, 22 | // logo: { 23 | // light: '/logo/r6p.svg', 24 | // dark: '/logo/r5y.svg' 25 | // }, 26 | siteTitle: false, 27 | nav: [ 28 | { text: 'Home', link: '/' }, 29 | { text: 'Docs', link: '/docs/index.md' }, 30 | { text: 'API', link: '/api/index.md' } 31 | ], 32 | socialLinks: [ 33 | { icon: 'github', link: 'https://github.com/stepci/garph' }, 34 | { icon: 'twitter', link: 'https://twitter.com/ci_step' }, 35 | { icon: 'discord', link: 'https://discord.gg/KqJJzJ3BTu' } 36 | ], 37 | editLink: { 38 | pattern: 'https://github.com/stepci/garph/edit/main/www/:path', 39 | text: 'Edit this page on GitHub' 40 | }, 41 | outline: [2, 3], 42 | sidebar: { 43 | '/docs': [ 44 | { 45 | text: 'Guide', 46 | items: [ 47 | { text: 'Quickstart', link: '/docs/index.md' }, 48 | { text: 'Schemas', link: '/docs/guide/schemas.md' }, 49 | { text: 'Resolvers', link: '/docs/guide/resolvers.md' }, 50 | { text: 'Loaders', link: '/docs/guide/loaders.md' }, 51 | { text: 'Inferring Types', link: '/docs/guide/inferring-types.md' }, 52 | { text: 'Migrate New', link: '/docs/guide/migrate.md' } 53 | ] 54 | }, 55 | { 56 | text: 'Advanced', 57 | items: [ 58 | { text: 'Auth', link: '/docs/advanced/auth.md' }, 59 | { text: 'Context', link: '/docs/advanced/context.md' }, 60 | { text: 'File Uploads', link: '/docs/advanced/file-uploads.md' }, 61 | { text: 'Defer and Stream', link: '/docs/advanced/defer-stream.md' }, 62 | { text: 'Subscriptions', link: '/docs/advanced/subscriptions.md' }, 63 | { text: 'Pagination', link: '/docs/advanced/pagination.md' }, 64 | { text: 'Relay', link: '/docs/advanced/relay.md' }, 65 | { text: 'Validation', link: '/docs/advanced/validation.md' }, 66 | { text: 'Federation', link: '/docs/advanced/federation.md' }, 67 | { text: 'Errors', link: '/docs/advanced/errors.md' }, 68 | { text: 'Testing', link: '/docs/advanced/testing.md' }, 69 | { text: 'Extending Garph', link: '/docs/advanced/extending-garph.md' }, 70 | ] 71 | }, 72 | { 73 | text: 'Integration', 74 | items: [ 75 | { 76 | text: 'Examples', collapsed: true, items: [ 77 | { text: `Next.js Yoga + GQty`, link:'/docs/integration/examples/nextjs.md' }, 78 | { text: 'Nuxt Yoga + Vue Apollo', link:'/docs/integration/examples/nuxt.md' }, 79 | { text: 'Remix Yoga + GQty', link:'/docs/integration/examples/remix.md' }, 80 | ] 81 | }, 82 | { 83 | text: 'Server', collapsed: true, items: [ 84 | { text: 'Yoga', link:'/docs/integration/server/graphql-yoga.md' }, 85 | { text: 'Apollo Server', link:'/docs/integration/server/apollo-server.md' }, 86 | { text: 'Mercurius', link:'/docs/integration/server/mercurius.md' }, 87 | ] 88 | }, 89 | { 90 | text: 'Client', collapsed: true, items: [ 91 | { text: 'GQty — Universal', link:'/docs/integration/client/gqty.md' }, 92 | { text: 'urql — React', link:'/docs/integration/client/urql.md' }, 93 | { text: 'Vue Apollo — Vue', link:'/docs/integration/client/vue-apollo.md' }, 94 | { text: 'Fetch API', link:'/docs/integration/client/fetch.md' }, 95 | ] 96 | }, 97 | // { text: 'ChatGPT', link:'/docs/integration/chatgpt.md' }, 98 | ] 99 | }, 100 | { 101 | text: 'Comparisons', link: '/docs/comparisons.md', 102 | } 103 | ], 104 | } 105 | } 106 | }) 107 | -------------------------------------------------------------------------------- /www/public/logo/r5m.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /www/public/logo/r6N.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /www/public/logo/r5y.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /www/public/logo/r6p.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /www/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: [2, 3] 3 | --- 4 | 5 | # Quickstart 6 | 7 | ## Overview 8 | 9 | Garph is a tool for building GraphQL APIs without codegen. It provides a fullstack TypeScript experience and makes it easy to create and maintain GraphQL APIs. This guide will show you how to install and set up Garph and create a simple GraphQL API. 10 | 11 | **Check out a video introduction to Garph by Jamie Barton** 12 | 13 | 14 | 15 | ## Prerequisites 16 | 17 | Before you begin, make sure you have the following installed: 18 | 19 | - [Node.js](https://nodejs.org/) (LTS and above) 20 | - npm (included with Node) 21 | 22 | ## Tutorial 23 | 24 | ### Step 1: Install Garph 25 | 26 | To install Garph, run the following command in your terminal: 27 | 28 | ::: code-group 29 | ```sh [npm] 30 | $ npm i garph graphql-yoga 31 | ``` 32 | 33 | ```sh [pnpm] 34 | $ pnpm add garph graphql-yoga 35 | ``` 36 | 37 | ```sh [yarn] 38 | $ yarn add garph graphql-yoga 39 | ``` 40 | 41 | ```sh [bun] 42 | $ bun i garph graphql-yoga 43 | ``` 44 | ::: 45 | 46 | This will install Garph and [Yoga](https://the-guild.dev/graphql/yoga-server) in your project. 47 | 48 | ### Step 2: Create a schema 49 | 50 | In GraphQL, schemas are used to define the types and operations that are available in a GraphQL API. 51 | 52 | The "Query" type is the entry point for queries in a GraphQL schema. It defines the top-level fields that can be queried by clients of the API. The fields defined on the "Query" type determine what data can be queried and returned by the server. 53 | 54 | Create a new file called `index.ts` and paste the following contents: 55 | 56 | ::: code-group 57 | ```ts [index.ts] 58 | import { g, InferResolvers, buildSchema } from 'garph' 59 | import { createYoga, YogaInitialContext } from 'graphql-yoga' 60 | import { createServer } from 'http' 61 | 62 | const queryType = g.type('Query', { 63 | greet: g.string() 64 | .args({ 65 | name: g.string().optional().default('Max') 66 | }) 67 | .description('Greets a person') 68 | }) 69 | ``` 70 | ::: 71 | 72 | This will import the required packages and create a new GraphQL schema a Query type, that has a field "greet", that takes an optional "name" argument of type string with the default value "Max". 73 | 74 | The example above produces the following GraphQL schema: 75 | 76 | ```graphql 77 | type Query { 78 | """ 79 | Greets a person 80 | """ 81 | greet(name: String = "Max"): String! 82 | } 83 | ``` 84 | 85 | [→ More about Schemas](./guide/schemas.md) 86 | 87 | ### Step 3: Add resolvers 88 | 89 | GraphQL resolvers are functions for fetching the data for a particular field in a GraphQL query or mutation. When a client makes a GraphQL request, the GraphQL server invokes the corresponding resolver functions to retrieve the data for the requested fields. 90 | 91 | ::: code-group 92 | ```ts{13-17} [index.ts] 93 | import { g, InferResolvers, buildSchema } from 'garph' 94 | import { createYoga, YogaInitialContext } from 'graphql-yoga' 95 | import { createServer } from 'http' 96 | 97 | const queryType = g.type('Query', { 98 | greet: g.string() 99 | .args({ 100 | name: g.string().optional().default('Max') 101 | }) 102 | .description('Greets a person') 103 | }) 104 | 105 | const resolvers: InferResolvers<{ Query: typeof queryType }, { context: YogaInitialContext }> = { 106 | Query: { 107 | greet: (parent, args, context, info) => `Hello, ${args.name}` 108 | } 109 | } 110 | ``` 111 | ::: 112 | 113 | The above code defines a resolver function for a "greet" field on the "Query" type in a GraphQL schema. When a client sends a query requesting the "greet" field, this resolver function will be invoked by the GraphQL server to fetch and return the data for the field. 114 | 115 | The resolver function above simply returns a string that contains a greeting message using the name passed in as an argument. 116 | 117 | [→ More about Resolvers](./guide/resolvers.md) 118 | 119 | ### Step 4: Serve the GraphQL API 120 | 121 | Although Garph holds no opinions of which server is being used to serve the GraphQL API, we **higly recommend** starting out with [Yoga](https://the-guild.dev/graphql/yoga-server) 122 | 123 | GraphQL Yoga can help simplify the process of building a GraphQL server, reduce boilerplate code, and provide useful features and tools to help you develop and debug your server more efficiently 124 | 125 | ::: code-group 126 | ```ts{19-24} [index.ts] 127 | import { g, InferResolvers, buildSchema } from 'garph' 128 | import { createYoga, YogaInitialContext } from 'graphql-yoga' 129 | import { createServer } from 'http' 130 | 131 | const queryType = g.type('Query', { 132 | greet: g.string() 133 | .args({ 134 | name: g.string().optional().default('Max') 135 | }) 136 | .description('Greets a person') 137 | }) 138 | 139 | const resolvers: InferResolvers<{ Query: typeof queryType }, { context: YogaInitialContext }> = { 140 | Query: { 141 | greet: (parent, args, context, info) => `Hello, ${args.name}` 142 | } 143 | } 144 | 145 | const schema = buildSchema({ g, resolvers }) 146 | const yoga = createYoga({ schema }) 147 | const server = createServer(yoga) 148 | server.listen(4000, () => { 149 | console.info('Server is running on http://localhost:4000/graphql') 150 | }) 151 | ``` 152 | ::: 153 | 154 | In the above code, we create a new GraphQL schema from our Garph schema and resolvers. After that, we create a new Yoga instance with the schema provided and serve Yoga using a http server included in Node.js on port 4000 155 | 156 | You can execute the following command to run the code above: 157 | 158 | ```sh 159 | $ npx ts-node index.ts 160 | ``` 161 | 162 | Once the server is up, you should see the following output in the console: 163 | 164 | ``` 165 | Server is running on http://localhost:4000/graphql 166 | ``` 167 | 168 | ### Step 5: Try the GraphQL API 169 | 170 | To try out your new GraphQL API, open your web browser and navigate to: http://localhost:4000/graphql 171 | 172 | This will open the GraphQL Playground (GraphiQL) where you can test your API by running queries. 173 | 174 | To test the "greet" query, enter the following in the left pane: 175 | 176 | ```graphql 177 | query { 178 | greet(name: "Max") 179 | } 180 | ``` 181 | 182 | Then, click the "Play" button to run the query. You should see the following response in the right pane: 183 | 184 | ```json 185 | { 186 | "data": { 187 | "greet": "Hello, Max" 188 | } 189 | } 190 | ``` 191 | 192 | Congratulations, you have created a GraphQL API with Garph! 193 | 194 | ## Conclusion 195 | 196 | In this Quickstart guide, you learned how to install and set up Garph and create a simple GraphQL API. For more information on how to use Garph, see other parts of the documentation 197 | -------------------------------------------------------------------------------- /src/schema.ts: -------------------------------------------------------------------------------- 1 | import { AnyType, Args, GarphSchema } from './index' 2 | import { SchemaComposer } from 'graphql-compose' 3 | import { Factory } from 'single-user-cache' 4 | const factory = new Factory() 5 | const dataLoader = factory.create() 6 | 7 | export type ConverterConfig = { 8 | defaultNullability?: boolean 9 | } 10 | 11 | export function printSchema(g: GarphSchema, config: ConverterConfig = { defaultNullability: false }) { 12 | const schemaComposer = new SchemaComposer(); 13 | g.types.forEach(type => schemaComposer.add(convertToGraphqlType(schemaComposer, type.typeDef.name, type, config))) 14 | return schemaComposer.toSDL() 15 | } 16 | 17 | export function buildSchema({ g, resolvers }: { g: GarphSchema, resolvers?: any }, config: ConverterConfig = { defaultNullability: false }) { 18 | const schemaComposer = new SchemaComposer(); 19 | g.types.forEach(type => schemaComposer.add(convertToGraphqlType(schemaComposer, type.typeDef.name, type, config, resolvers[type.typeDef.name]))) 20 | return schemaComposer.buildSchema() 21 | } 22 | 23 | function isOptional(target: string, type: AnyType, config: ConverterConfig) { 24 | return type.typeDef.isRequired ? `${target}!` : type.typeDef.isOptional ? `${target}` : config.defaultNullability ? `${target}` : `${target}!` 25 | } 26 | 27 | export function getFieldType(schemaComposer: SchemaComposer, type: AnyType, config: ConverterConfig) { 28 | switch (type.typeDef.type) { 29 | case 'String': 30 | return isOptional('String', type, config) 31 | case 'Int': 32 | return isOptional('Int', type, config) 33 | case 'Float': 34 | return isOptional('Float', type, config) 35 | case 'Boolean': 36 | return isOptional('Boolean', type, config) 37 | case 'ID': 38 | return isOptional('ID', type, config) 39 | case 'List': 40 | return isOptional(`[${getFieldType(schemaComposer, type.typeDef.shape, config)}]`, type, config) 41 | case 'PaginatedList': 42 | schemaComposer.createObjectTC({ 43 | name: `${type.typeDef.shape.typeDef.shape.typeDef.name}Edge`, 44 | fields: { 45 | node: { 46 | type: type.typeDef.shape.typeDef.shape.typeDef.name, 47 | }, 48 | cursor: { 49 | type: 'String!', 50 | } 51 | } 52 | }) 53 | 54 | schemaComposer.createObjectTC({ 55 | name: `${type.typeDef.shape.typeDef.shape.typeDef.name}Connection`, 56 | fields: { 57 | edges: { 58 | type: `[${type.typeDef.shape.typeDef.shape.typeDef.name}Edge]`, 59 | }, 60 | pageInfo: { 61 | type: 'PageInfo!', 62 | } 63 | } 64 | }) 65 | 66 | return isOptional(`${type.typeDef.shape.typeDef.shape.typeDef.name}Connection`, type, config) 67 | case 'Ref': 68 | if (!typeof type.typeDef.shape) { 69 | throw new Error('Ref type must be a function or a valid Garph Type') 70 | } 71 | 72 | let shape 73 | if (typeof type.typeDef.shape === 'function') { 74 | shape = type.typeDef.shape() 75 | } else { 76 | shape = type.typeDef.shape 77 | } 78 | 79 | return isOptional(shape.typeDef.name, type, config) 80 | default: 81 | return isOptional(type.typeDef.name, type, config) 82 | } 83 | } 84 | 85 | export function convertToGraphqlType(schemaComposer: SchemaComposer, name: string, type: AnyType, config: ConverterConfig, resolvers?: any) { 86 | switch (type.typeDef.type) { 87 | case 'ObjectType': 88 | const objType = schemaComposer.createObjectTC({ 89 | name, 90 | description: type.typeDef.description, 91 | fields: parseFields(schemaComposer, name, type.typeDef.shape, config, resolvers), 92 | }) 93 | 94 | if (type.typeDef.interfaces) { 95 | type.typeDef.interfaces.forEach(i => { 96 | objType.addFields(parseFields(schemaComposer, name, i.typeDef.shape, config, resolvers)) 97 | objType.addInterface(i.typeDef.name) 98 | }) 99 | } 100 | 101 | if (type.typeDef.extend) { 102 | type.typeDef.extend.forEach(i => { 103 | objType.addFields(parseFields(schemaComposer, name, i as any, config, resolvers)) 104 | }) 105 | } 106 | 107 | return objType 108 | case 'Enum': 109 | return schemaComposer.createEnumTC({ 110 | name, 111 | description: type.typeDef.description, 112 | values: type.typeDef.shape.reduce((acc, val) => { 113 | acc[val] = {} 114 | return acc 115 | }, {}) 116 | }) 117 | case 'Union': 118 | return schemaComposer.createUnionTC({ 119 | name, 120 | description: type.typeDef.description, 121 | types: Object.values(type.typeDef.shape).map((t: AnyType) => t.typeDef.name), 122 | resolveType: resolvers?.resolveType 123 | }) 124 | case 'InputType': 125 | const inputType = schemaComposer.createInputTC({ 126 | name, 127 | description: type.typeDef.description, 128 | fields: parseFields(schemaComposer, name, type.typeDef.shape, config), 129 | }) 130 | 131 | if (type.typeDef.extend) { 132 | type.typeDef.extend.forEach(i => { 133 | inputType.addFields(parseFields(schemaComposer, name, i as any, config, resolvers)) 134 | }) 135 | } 136 | 137 | return inputType 138 | case 'Scalar': 139 | return schemaComposer.createScalarTC({ 140 | name, 141 | description: type.typeDef.description, 142 | serialize: type.typeDef.scalarOptions?.serialize, 143 | parseValue: type.typeDef.scalarOptions?.parseValue, 144 | parseLiteral: type.typeDef.scalarOptions?.parseLiteral, 145 | specifiedByURL: type.typeDef.scalarOptions?.specifiedByUrl 146 | }) 147 | case 'InterfaceType': 148 | const interfaceType = schemaComposer.createInterfaceTC({ 149 | name, 150 | description: type.typeDef.description, 151 | fields: parseFields(schemaComposer, name, type.typeDef.shape, config), 152 | resolveType: resolvers?.resolveType 153 | }) 154 | 155 | if (type.typeDef.interfaces) { 156 | type.typeDef.interfaces.forEach(i => { 157 | interfaceType.addFields(parseFields(schemaComposer, name, i.typeDef.shape, config)) 158 | interfaceType.addInterface(i.typeDef.name) 159 | }) 160 | } 161 | 162 | if (type.typeDef.extend) { 163 | type.typeDef.extend.forEach(i => { 164 | interfaceType.addFields(parseFields(schemaComposer, name, i as any, config, resolvers)) 165 | }) 166 | } 167 | 168 | return interfaceType 169 | } 170 | } 171 | 172 | export function parseFields(schemaComposer: SchemaComposer, name: string, fields: AnyType, config: ConverterConfig, resolvers?: any) { 173 | const fieldsObj = {} 174 | Object.keys(fields).forEach(fieldName => { 175 | const field = fields[fieldName] 176 | 177 | fieldsObj[fieldName] = { 178 | type: getFieldType(schemaComposer, field, config), 179 | args: parseArgs(schemaComposer, field.typeDef.args, config), 180 | defaultValue: field.typeDef.defaultValue, 181 | deprecationReason: field.typeDef.deprecated, 182 | description: field.typeDef.description 183 | } 184 | 185 | if (resolvers?.[fieldName]) { 186 | Object.assign(fieldsObj[fieldName], addResolver(resolvers[fieldName], `${name}.${fieldName}`)) 187 | } 188 | }) 189 | 190 | return fieldsObj 191 | } 192 | 193 | function addResolver (resolver, cacheKey: string) { 194 | if (!resolver) return 195 | if (resolver.resolve || resolver.subscribe) return resolver 196 | 197 | // Loader 198 | if (resolver.load) { 199 | factory.add(cacheKey, { cache: true }, async (queries) => resolver.load(queries)) 200 | 201 | return { 202 | resolve: (parent, args, context, info) => { 203 | return dataLoader[cacheKey]({ parent, args, context, info }) 204 | } 205 | } 206 | } 207 | 208 | // Loader (no cache) 209 | if (resolver.loadBatch) { 210 | factory.add(cacheKey, { cache: false }, async (queries) => resolver.loadBatch(queries)) 211 | 212 | return { 213 | resolve: (parent, args, context, info) => { 214 | return dataLoader[cacheKey]({ parent, args, context, info }) 215 | } 216 | } 217 | } 218 | 219 | return { resolve: resolver } 220 | } 221 | 222 | export function parseArgs(schemaComposer: SchemaComposer, anyArgs: Args, config) { 223 | if (!anyArgs) return 224 | 225 | const args = {} 226 | 227 | Object.keys(anyArgs).forEach(argName => { 228 | const arg = anyArgs[argName] 229 | args[argName] = { 230 | type: getFieldType(schemaComposer, arg, config), 231 | defaultValue: arg.typeDef.defaultValue, 232 | deprecationReason: arg.typeDef.deprecated, 233 | description: arg.typeDef.description 234 | } 235 | }) 236 | 237 | return args 238 | } 239 | -------------------------------------------------------------------------------- /www/api/classes/GarphSchema.md: -------------------------------------------------------------------------------- 1 | [garph](../index.md) / GarphSchema 2 | 3 | # Class: GarphSchema 4 | 5 | ## Constructors 6 | 7 | ### constructor 8 | 9 | • **new GarphSchema**(`«destructured»?`) 10 | 11 | #### Parameters 12 | 13 | | Name | Type | 14 | | :------ | :------ | 15 | | `«destructured»` | `Object` | 16 | | › `types` | [`AnyType`](../index.md#anytype)[] | 17 | 18 | #### Defined in 19 | 20 | [index.ts:625](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L625) 21 | 22 | ## Properties 23 | 24 | ### nodeType 25 | 26 | • **nodeType**: `GInterface`<``"Node"``, { `id`: `GString`<``"ID"``\> }\> 27 | 28 | #### Defined in 29 | 30 | [index.ts:601](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L601) 31 | 32 | ___ 33 | 34 | ### pageInfoArgs 35 | 36 | • **pageInfoArgs**: `Object` 37 | 38 | #### Type declaration 39 | 40 | | Name | Type | 41 | | :------ | :------ | 42 | | `after` | `GOptional`<`GString`<``"ID"``\>\> | 43 | | `before` | `GOptional`<`GString`<``"ID"``\>\> | 44 | | `first` | `GOptional`<`GNumber`<``"Int"``\>\> | 45 | | `last` | `GOptional`<`GNumber`<``"Int"``\>\> | 46 | 47 | #### Defined in 48 | 49 | [index.ts:612](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L612) 50 | 51 | ___ 52 | 53 | ### pageInfoType 54 | 55 | • **pageInfoType**: `GType`<``"PageInfo"``, { `endCursor`: `GOptional`<`GString`<``"String"``\>\> ; `hasNextPage`: `GBoolean` ; `hasPreviousPage`: `GBoolean` ; `startCursor`: `GOptional`<`GString`<``"String"``\>\> }\> 56 | 57 | #### Defined in 58 | 59 | [index.ts:605](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L605) 60 | 61 | ___ 62 | 63 | ### types 64 | 65 | • **types**: `Map`<`string`, [`AnyType`](../index.md#anytype)\> 66 | 67 | #### Defined in 68 | 69 | [index.ts:599](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L599) 70 | 71 | ## Methods 72 | 73 | ### boolean 74 | 75 | ▸ **boolean**(): `GBoolean` 76 | 77 | #### Returns 78 | 79 | `GBoolean` 80 | 81 | #### Defined in 82 | 83 | [index.ts:710](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L710) 84 | 85 | ___ 86 | 87 | ### connection 88 | 89 | ▸ **connection**<`N`, `T`\>(`name`, `shape`): `GType`<`string`, { `edges`: `GList`<`T`\> ; `pageInfo`: `GType`<``"PageInfo"``, { `endCursor`: `GOptional`<`GString`<``"String"``\>\> ; `hasNextPage`: `GBoolean` ; `hasPreviousPage`: `GBoolean` ; `startCursor`: `GOptional`<`GString`<``"String"``\>\> }\> }\> 90 | 91 | #### Type parameters 92 | 93 | | Name | Type | 94 | | :------ | :------ | 95 | | `N` | extends `string` | 96 | | `T` | extends [`Type`](Type.md)<`any`, ``"Ref"``, `T`\> | 97 | 98 | #### Parameters 99 | 100 | | Name | Type | 101 | | :------ | :------ | 102 | | `name` | `N` | 103 | | `shape` | `T` | 104 | 105 | #### Returns 106 | 107 | `GType`<`string`, { `edges`: `GList`<`T`\> ; `pageInfo`: `GType`<``"PageInfo"``, { `endCursor`: `GOptional`<`GString`<``"String"``\>\> ; `hasNextPage`: `GBoolean` ; `hasPreviousPage`: `GBoolean` ; `startCursor`: `GOptional`<`GString`<``"String"``\>\> }\> }\> 108 | 109 | #### Defined in 110 | 111 | [index.ts:641](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L641) 112 | 113 | ___ 114 | 115 | ### edge 116 | 117 | ▸ **edge**<`N`, `T`\>(`name`, `shape`): `GType`<`N`, { `cursor`: [`AnyString`](../index.md#anystring) ; `node`: `T` }\> 118 | 119 | #### Type parameters 120 | 121 | | Name | Type | 122 | | :------ | :------ | 123 | | `N` | extends `string` | 124 | | `T` | extends [`Type`](Type.md)<`any`, ``"Ref"``, `T`\> | 125 | 126 | #### Parameters 127 | 128 | | Name | Type | 129 | | :------ | :------ | 130 | | `name` | `N` | 131 | | `shape` | `T` | 132 | 133 | #### Returns 134 | 135 | `GType`<`N`, { `cursor`: [`AnyString`](../index.md#anystring) ; `node`: `T` }\> 136 | 137 | #### Defined in 138 | 139 | [index.ts:651](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L651) 140 | 141 | ___ 142 | 143 | ### enumType 144 | 145 | ▸ **enumType**<`N`, `T`\>(`name`, `args`): `GEnum`<`N`, `T`\> 146 | 147 | #### Type parameters 148 | 149 | | Name | Type | 150 | | :------ | :------ | 151 | | `N` | extends `string` | 152 | | `T` | extends readonly `string`[] \| `TSEnumType` | 153 | 154 | #### Parameters 155 | 156 | | Name | Type | 157 | | :------ | :------ | 158 | | `name` | `N` | 159 | | `args` | `T` | 160 | 161 | #### Returns 162 | 163 | `GEnum`<`N`, `T`\> 164 | 165 | #### Defined in 166 | 167 | [index.ts:670](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L670) 168 | 169 | ___ 170 | 171 | ### float 172 | 173 | ▸ **float**(): `GNumber`<``"Float"``\> 174 | 175 | #### Returns 176 | 177 | `GNumber`<``"Float"``\> 178 | 179 | #### Defined in 180 | 181 | [index.ts:706](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L706) 182 | 183 | ___ 184 | 185 | ### id 186 | 187 | ▸ **id**(): `GString`<``"ID"``\> 188 | 189 | #### Returns 190 | 191 | `GString`<``"ID"``\> 192 | 193 | #### Defined in 194 | 195 | [index.ts:698](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L698) 196 | 197 | ___ 198 | 199 | ### inputType 200 | 201 | ▸ **inputType**<`N`, `T`\>(`name`, `shape`): `GInput`<`N`, `T`\> 202 | 203 | #### Type parameters 204 | 205 | | Name | Type | 206 | | :------ | :------ | 207 | | `N` | extends `string` | 208 | | `T` | extends [`AnyTypes`](../index.md#anytypes) | 209 | 210 | #### Parameters 211 | 212 | | Name | Type | 213 | | :------ | :------ | 214 | | `name` | `N` | 215 | | `shape` | `T` | 216 | 217 | #### Returns 218 | 219 | `GInput`<`N`, `T`\> 220 | 221 | #### Defined in 222 | 223 | [index.ts:664](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L664) 224 | 225 | ___ 226 | 227 | ### int 228 | 229 | ▸ **int**(): `GNumber`<``"Int"``\> 230 | 231 | #### Returns 232 | 233 | `GNumber`<``"Int"``\> 234 | 235 | #### Defined in 236 | 237 | [index.ts:702](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L702) 238 | 239 | ___ 240 | 241 | ### interface 242 | 243 | ▸ **interface**<`N`, `T`\>(`name`, `shape`): `GInterface`<`N`, `T`\> 244 | 245 | #### Type parameters 246 | 247 | | Name | Type | 248 | | :------ | :------ | 249 | | `N` | extends `string` | 250 | | `T` | extends [`AnyTypes`](../index.md#anytypes) | 251 | 252 | #### Parameters 253 | 254 | | Name | Type | 255 | | :------ | :------ | 256 | | `name` | `N` | 257 | | `shape` | `T` | 258 | 259 | #### Returns 260 | 261 | `GInterface`<`N`, `T`\> 262 | 263 | #### Defined in 264 | 265 | [index.ts:688](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L688) 266 | 267 | ___ 268 | 269 | ### node 270 | 271 | ▸ **node**<`N`, `T`\>(`name`, `shape`): `GType`<`N`, `T` & { `id`: `GString`<``"ID"``\> }\> 272 | 273 | #### Type parameters 274 | 275 | | Name | Type | 276 | | :------ | :------ | 277 | | `N` | extends `string` | 278 | | `T` | extends [`AnyTypes`](../index.md#anytypes) | 279 | 280 | #### Parameters 281 | 282 | | Name | Type | 283 | | :------ | :------ | 284 | | `name` | `N` | 285 | | `shape` | `T` | 286 | 287 | #### Returns 288 | 289 | `GType`<`N`, `T` & { `id`: `GString`<``"ID"``\> }\> 290 | 291 | #### Defined in 292 | 293 | [index.ts:635](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L635) 294 | 295 | ___ 296 | 297 | ### ref 298 | 299 | ▸ **ref**<`T`\>(`ref`): `GRef`<`T`\> 300 | 301 | #### Type parameters 302 | 303 | | Name | 304 | | :------ | 305 | | `T` | 306 | 307 | #### Parameters 308 | 309 | | Name | Type | 310 | | :------ | :------ | 311 | | `ref` | `T` | 312 | 313 | #### Returns 314 | 315 | `GRef`<`T`\> 316 | 317 | #### Defined in 318 | 319 | [index.ts:716](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L716) 320 | 321 | ___ 322 | 323 | ### registerType 324 | 325 | ▸ **registerType**(`type`): `void` 326 | 327 | #### Parameters 328 | 329 | | Name | Type | 330 | | :------ | :------ | 331 | | `type` | [`AnyType`](../index.md#anytype) | 332 | 333 | #### Returns 334 | 335 | `void` 336 | 337 | #### Defined in 338 | 339 | [index.ts:619](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L619) 340 | 341 | ___ 342 | 343 | ### scalarType 344 | 345 | ▸ **scalarType**<`I`, `O`\>(`name`, `options?`): `GScalar`<`I`, `O`\> 346 | 347 | #### Type parameters 348 | 349 | | Name | 350 | | :------ | 351 | | `I` | 352 | | `O` | 353 | 354 | #### Parameters 355 | 356 | | Name | Type | 357 | | :------ | :------ | 358 | | `name` | `string` | 359 | | `options?` | `ScalarOptions`<`I`, `O`\> | 360 | 361 | #### Returns 362 | 363 | `GScalar`<`I`, `O`\> 364 | 365 | #### Defined in 366 | 367 | [index.ts:682](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L682) 368 | 369 | ___ 370 | 371 | ### string 372 | 373 | ▸ **string**(): `GString`<``"String"``\> 374 | 375 | #### Returns 376 | 377 | `GString`<``"String"``\> 378 | 379 | #### Defined in 380 | 381 | [index.ts:694](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L694) 382 | 383 | ___ 384 | 385 | ### type 386 | 387 | ▸ **type**<`N`, `T`\>(`name`, `shape`): `GType`<`N`, `T`\> 388 | 389 | #### Type parameters 390 | 391 | | Name | Type | 392 | | :------ | :------ | 393 | | `N` | extends `string` | 394 | | `T` | extends [`AnyTypes`](../index.md#anytypes) | 395 | 396 | #### Parameters 397 | 398 | | Name | Type | 399 | | :------ | :------ | 400 | | `name` | `N` | 401 | | `shape` | `T` | 402 | 403 | #### Returns 404 | 405 | `GType`<`N`, `T`\> 406 | 407 | #### Defined in 408 | 409 | [index.ts:629](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L629) 410 | 411 | ___ 412 | 413 | ### unionType 414 | 415 | ▸ **unionType**<`N`, `T`\>(`name`, `args`): `GUnion`<`N`, `T`\> 416 | 417 | #### Type parameters 418 | 419 | | Name | Type | 420 | | :------ | :------ | 421 | | `N` | extends `string` | 422 | | `T` | extends [`AnyObjects`](../index.md#anyobjects) | 423 | 424 | #### Parameters 425 | 426 | | Name | Type | 427 | | :------ | :------ | 428 | | `name` | `N` | 429 | | `args` | `T` | 430 | 431 | #### Returns 432 | 433 | `GUnion`<`N`, `T`\> 434 | 435 | #### Defined in 436 | 437 | [index.ts:676](https://github.com/stepci/garph/blob/bc1d582/src/index.ts#L676) 438 | -------------------------------------------------------------------------------- /www/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Garph - GraphQL. Reimagined. 3 | description: Garph provides fullstack TypeScript experience for building GraphQL-APIs without codegen 4 | aside: false 5 | sidebar: false 6 | layout: page 7 | --- 8 | 9 | 272 | 273 |281 | Garph provides fullstack TypeScript experience for building GraphQL-APIs without codegen 282 |
283 |354 | “The ideal GQL stack imo looks like this” 355 |356 |