diff --git a/docs/directives.md b/docs/directives.md index 52e295b580..b9377595d0 100644 --- a/docs/directives.md +++ b/docs/directives.md @@ -12,20 +12,21 @@ Here is a list of all the custom directives supported by Tailcall: -| Operator | Description | -| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------ | -| [`@addField`](./directives/addField.md) | Simplifies data structures and queries by adding, inlining, or flattening fields or nodes within the schema. | -| [`@cache`](./directives/cache.md) | Enables caching for the query, field or type applied to. | -| [`@call`](./directives/call.md) | Invokes a query or mutation from another query or mutation field. | -| [`@expr`](./directives/expr.md) | Allows embedding of a constant response within the schema. | -| [`@graphQL`](./directives/graphQL.md) | Resolves a field or node by a GraphQL API. | -| [`@grpc`](./directives/grpc.md) | Resolves a field or node by a gRPC API. | -| [`@http`](./directives/http.md) | Resolves a field or node by a REST API. | -| [`@link`](./directives/link.md) | Imports external resources such as config files, certs, protobufs, etc in the schema. | -| [`@modify`](./directives/modify.md) | Enables changes to attributes of fields or nodes in the schema. | -| [`@omit`](./directives/omit.md) | Excludes fields or nodes from the generated schema, making them inaccessible through the GraphQL API. | -| [`@protected`](./directives/protected.md) | Adds authentication and authorization controls to fields or nodes in the schema. | -| [`@rest`](./directives/rest.md) | Allows exposing REST endpoints on top of GraphQL. | -| [`@server`](./directives/server.md) | Provides server configurations for behavior tuning and tailcall optimization in specific use-cases. | -| [`@telemetry`](./directives/telemetry.md) | Integrates with open-telemetry to provide observability of the running tailcall service. | -| [`@upstream`](./directives/upstream.md) | Controls aspects of the upstream server connection, including timeouts and keep-alive settings. | +| Operator | Description | +| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| [`@addField`](./directives/addField.md) | Simplifies data structures and queries by adding, inlining, or flattening fields or nodes within the schema. | +| [`@cache`](./directives/cache.md) | Enables caching for the query, field or type applied to. | +| [`@call`](./directives/call.md) | Invokes a query or mutation from another query or mutation field. | +| [`@discriminate`](./directives/discriminate.md) | Allows to customize the discriminator while working with union types. | +| [`@expr`](./directives/expr.md) | Allows embedding of a constant response within the schema. | +| [`@graphQL`](./directives/graphQL.md) | Resolves a field or node by a GraphQL API. | +| [`@grpc`](./directives/grpc.md) | Resolves a field or node by a gRPC API. | +| [`@http`](./directives/http.md) | Resolves a field or node by a REST API. | +| [`@link`](./directives/link.md) | Imports external resources such as config files, certs, protobufs, etc in the schema. | +| [`@modify`](./directives/modify.md) | Enables changes to attributes of fields or nodes in the schema. | +| [`@omit`](./directives/omit.md) | Excludes fields or nodes from the generated schema, making them inaccessible through the GraphQL API. | +| [`@protected`](./directives/protected.md) | Adds authentication and authorization controls to fields or nodes in the schema. | +| [`@rest`](./directives/rest.md) | Allows exposing REST endpoints on top of GraphQL. | +| [`@server`](./directives/server.md) | Provides server configurations for behavior tuning and tailcall optimization in specific use-cases. | +| [`@telemetry`](./directives/telemetry.md) | Integrates with open-telemetry to provide observability of the running tailcall service. | +| [`@upstream`](./directives/upstream.md) | Controls aspects of the upstream server connection, including timeouts and keep-alive settings. | diff --git a/docs/directives/discriminate.md b/docs/directives/discriminate.md new file mode 100644 index 0000000000..c13601e1ec --- /dev/null +++ b/docs/directives/discriminate.md @@ -0,0 +1,82 @@ +--- +title: "@discriminate" +description: The `@discriminate` directive is used to customize decoding of union types. +slug: ../discriminate +--- + +By default a union type expects an object with a wrapper key representing the value type. For example say we have the following GraphQL schema: + +```graphql showLineNumbers +type Query { + fooBar: [FooBar] + @http(url: "https://api.example.com/foobar") +} + +union FooBar = Foo | Bar + +type Foo { + foo: String! +} + +type Bar { + bar: String! +} +``` + +The API is expected to respond with an object that is wrapped with a key representing the type of the value. For example for `Foo` the response should look like: + +```json +[ + // API Response + {"Foo": {"foo": "Hello"}}, + {"Bar": {"bar": "World"}} +] +``` + +:::note +The **key** is always case sensitive and should match the type name. +::: + +This allows Tailcall to correctly decode the response and resolve with the exact variant of the union type. However its also a common practice to have a special field to specify the type. For example: + +```json +[ + {"type": "Foo", "foo": "Hello"}, + {"type": "Boo", "bar": "World"} +] +``` + +This can be achieved by modifying the schema to leverage the `@discriminate` directive: + +```graphql {4} +type Query { + fooBar: FooBar + @http(url: "https://api.example.com/foobar") + @discriminate +} +``` + +The `@discriminate` directive is used to indicate explicitly that the union type should be resolved using a discriminator field. + +The directive can be further customized by providing the discriminator field `name`: + +```graphql {4} +type Query { + fooBar: FooBar + @http(url: "https://api.example.com/foobar") + @discriminate(name: "ty") +} +``` + +In this case the API is expected to respond with an object that has a key `ty` representing the type of the value. For example for `Foo` the response should look like: + +```json +{"ty": "Foo","foo": "Hello"} +{"ty": "Bar","bar": "World"} +``` + +:::note +The value of the discriminator field should match the type name in a case sensitive manner. +::: + +Great! Congratulations on learning how to use the `@discriminate` directive to customize decoding of union types. Now you can confidently work with union types in your GraphQL schema. 🎉 diff --git a/publish-externals/snapshot.json b/publish-externals/snapshot.json index 770fdd3117..3a84dad324 100644 --- a/publish-externals/snapshot.json +++ b/publish-externals/snapshot.json @@ -1,3 +1,316 @@ { - "blogs": {} + "blogs": { + "graphql-conf-2023": { + "file": "blog/2023-graphql-conf-2023-09-29.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "no-one-talks-about-api-orchestration": { + "file": "blog/api-orchestration-2023-06-12.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "api-strategy": { + "file": "blog/api-strategy-2024-08-13.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "the-truth-about-scaling-automatic-persisted-queries": { + "file": "blog/automatic-persisted-queries-2023-08-11.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "dream11-graphql-case-study": { + "file": "blog/bff-case-study-2024-08-30.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "unraveling-the-challenges-of-bff-federation": { + "file": "blog/bff-challenges-2023-06-19.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-match-microservices": { + "file": "blog/graphql-and-ms-match-2024-08-18.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-angular-client": { + "file": "blog/graphql-angular-clients-2024-07-20.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-introspection-security": { + "file": "blog/graphql-introspection-security-2024-7-12.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-microservices-migration": { + "file": "blog/graphql-microservices-migration-2024-08-15.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-schema": { + "file": "blog/graphql-schema-2024-07-11.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-schema-part-2-1": { + "file": "blog/graphql-schema-part-2-1-2024-07-21.mdx", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-schema-part-2-2": { + "file": "blog/graphql-schema-part-2-2-2024-07-22.mdx", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-schema-part-2-3": { + "file": "blog/graphql-schema-part-2-3-2024-07-23.mdx", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-vs-openapi-part-1": { + "file": "blog/graphql-vs-openapi-part-1-2024-07-29.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-vs-openapi-part-2": { + "file": "blog/graphql-vs-openapi-part-2-2024-07-30.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-vs-openapi-part-3": { + "file": "blog/graphql-vs-openapi-part-3-2024-07-31.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-vs-rest-vs-grpc": { + "file": "blog/graphql-vs-rest-vs-grpc-2024-03-30.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-vue-client": { + "file": "blog/graphql-vue-clients-2024-08-01.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "graphql-vs-grpc": { + "file": "blog/grpc-vs-graphql-2024-07-26.mdx", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "exploring-graphiql": { + "file": "blog/guide-on-graphiql-2024-07-24.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "writing-a-graphql-backend-by-hand-is-long-gone": { + "file": "blog/no-code-graphql-2024-05-30.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "tailcall-n+1-identification-algorithm": { + "file": "blog/tailcall-n+1-working-2024-08-04.md", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + }, + "what-is-grpc": { + "file": "blog/what-is-grpc-2024-07-13.mdx", + "platforms": { + "Hashnode": { + "published": false, + "lastUpdatePublished": false + }, + "Dev.to": { + "published": false, + "lastUpdatePublished": false + } + } + } + } } diff --git a/sidebars.ts b/sidebars.ts index 7aa74da165..fab822e26a 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -42,6 +42,7 @@ const sidebars: SidebarsConfig = { "directives/server", "directives/telemetry", "directives/upstream", + "directives/discriminate", ].sort(), },