From aca20e2de5749d6b739cc6d9ceb12d8147453cb3 Mon Sep 17 00:00:00 2001 From: Author Date: Wed, 25 Sep 2024 06:09:49 +0000 Subject: [PATCH 01/12] [ci skip] update snapshot --- publish-externals/snapshot.json | 317 +++++++++++++++++++++++++++++++- 1 file changed, 315 insertions(+), 2 deletions(-) diff --git a/publish-externals/snapshot.json b/publish-externals/snapshot.json index 770fdd3117..48c77ea3c6 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 + } + } + } + } +} \ No newline at end of file From 2502cc51fd61cdc961f6d46228022fb50fc8d528 Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Wed, 25 Sep 2024 07:50:29 +0000 Subject: [PATCH 02/12] docs(510): split directive configuration file --- docs/directives.md | 2108 +--------------------------------- docs/directives/add-field.md | 94 ++ docs/directives/cache.md | 141 +++ docs/directives/call.md | 240 ++++ docs/directives/expr.md | 73 ++ docs/directives/graphql.md | 131 +++ docs/directives/grpc.md | 155 +++ docs/directives/http.md | 199 ++++ docs/directives/js.md | 77 ++ docs/directives/link.md | 123 ++ docs/directives/modify.md | 36 + docs/directives/omit.md | 38 + docs/directives/protected.md | 29 + docs/directives/rest.md | 54 + docs/directives/server.md | 350 ++++++ docs/directives/telemetry.md | 143 +++ docs/directives/upstream.md | 257 +++++ sidebars.ts | 16 + 18 files changed, 2172 insertions(+), 2092 deletions(-) create mode 100644 docs/directives/add-field.md create mode 100644 docs/directives/cache.md create mode 100644 docs/directives/call.md create mode 100644 docs/directives/expr.md create mode 100644 docs/directives/graphql.md create mode 100644 docs/directives/grpc.md create mode 100644 docs/directives/http.md create mode 100644 docs/directives/js.md create mode 100644 docs/directives/link.md create mode 100644 docs/directives/modify.md create mode 100644 docs/directives/omit.md create mode 100644 docs/directives/protected.md create mode 100644 docs/directives/rest.md create mode 100644 docs/directives/server.md create mode 100644 docs/directives/telemetry.md create mode 100644 docs/directives/upstream.md diff --git a/docs/directives.md b/docs/directives.md index 8cda223903..cb6b0ef943 100644 --- a/docs/directives.md +++ b/docs/directives.md @@ -12,2095 +12,19 @@ Here is a list of all the custom directives supported by Tailcall: -| Operator | Description | -| ------------------------------------ | ------------------------------------------------------------------------------------------------------------ | -| [`@addField`](#addfield-directive) | Simplifies data structures and queries by adding, inlining, or flattening fields or nodes within the schema. | -| [`@cache`](#cache-directive) | Enables caching for the query, field or type applied to. | -| [`@call`](#call-directive) | Invokes a query or mutation from another query or mutation field. | -| [`@expr`](#expr-directive) | Allows embedding of a constant response within the schema. | -| [`@graphQL`](#graphql-directive) | Resolves a field or node by a GraphQL API. | -| [`@grpc`](#grpc-directive) | Resolves a field or node by a gRPC API. | -| [`@http`](#http-directive) | Resolves a field or node by a REST API. | -| [`@link`](#link-directive) | Imports external resources such as config files, certs, protobufs, etc in the schema. | -| [`@modify`](#modify-directive) | Enables changes to attributes of fields or nodes in the schema. | -| [`@omit`](#omit-directive) | Excludes fields or nodes from the generated schema, making them inaccessible through the GraphQL API. | -| [`@rest`](#rest-directive) | Allows exposing REST endpoints on top of GraphQL. | -| [`@server`](#server-directive) | Provides server configurations for behavior tuning and tailcall optimization in specific use-cases. | -| [`@telemetry`](#telemetry-directive) | Integrates with open-telemetry to provide observability of the running tailcall service. | -| [`@upstream`](#upstream-directive) | Controls aspects of the upstream server connection, including timeouts and keep-alive settings. | - -## @addField Directive - -The `@addField` directive simplifies data structures and queries by adding a field that _inline_ or flattens a nested field or node within your schema. It modifies the schema and the data transformation process, making nested data more accessible and straightforward to present. - -For instance, consider a schema: - -```graphql showLineNumbers -schema { - query: Query -} - -type User - @addField(name: "street", path: ["address", "street"]) { - id: Int! - name: String! - username: String! - email: String! - phone: String - website: String - address: Address @modify(omit: true) -} - -type Address { - street: String! - city: String! - state: String! -} - -type Query { - user(id: Int!): User @http(path: "/users/{{.args.id}}") -} -``` - -Suppose we focus on the `street` field in `Address`. - -In this case, applying the `@addField` directive to the `User` type creates a `street` field within the `User` type. It uses a `path` argument to specify the sequence of fields from a declared field (`address`), leading to the `Address` field to add. We also can apply `@modify(omit: true)` to remove the `address` field from the schema, as the `street` field from `Address` is now directly accessible on the `User` type. - -Post application, the schema becomes: - -```graphql showLineNumbers -schema { - query: Query -} - -type User { - id: Int! - name: String! - username: String! - email: String! - phone: String - website: String - street: String -} - -type Query { - user(id: Int): Post! -} -``` - -In the above example, since we added a `@modify(omit: true)` on the `address` field, the schema no longer includes the `Address` type. - -The `@addField` directive also take cares of nullablity of the fields. If any of the fields in the path is nullable, the resulting type will be nullable. - -`@addField` also supports indexing, allowing for the specification of an array index for inline inclusion. For instance, if a field `posts` is of type `[Post]`, and the goal is to access the title of the first post, specify the path as [`"posts"`,`"0"`,`"title"`]. - -```graphql showLineNumbers -type User - @addField( - name: "firstPostTitle" - path: ["posts", "0", "title"] - ) { - id: Int! - name: String! - username: String! - email: String! - phone: String - website: String - posts: Post @http(path: "/users/{{.value.id}}/posts") -} - -type Post { - id: Int! - userId: Int! - title: String! - body: String! -} -``` - -In conclusion, the `@addField` directive helps tidy up your schema and streamline data fetching by reducing query depth, promoting better performance and simplicity. - -## @cache Directive - -The `@cache` directive provides a protocol agnostic mechanism for caching the results of fields within a GraphQL schema. Like any other cache implementation, this feature is useful for optimizing performance by reducing the need to fetch data that doesn't change frequently. - -### maxAge - -```graphql -@cache(maxAge: Int) -``` - -This parameter is a non-zero unsigned integer specifying the duration, in milliseconds, that retains the cached value. - -### Usage - -Consider the following GraphQL schema example: - -```graphql -type Query { - posts: [Post] @http(path: "/posts") -} - -type Post { - id: Int - title: String - userId: Int @cache(maxAge: 100) - user: User - @http(path: "/user/{{.value.userId}}") - @cache(maxAge: 200) -} - -type User { - id: Int - name: String - email: String -} -``` - -In this configuration, the system caches the result of the `user` field due to its association with an HTTP resolver. But it does not cache the values of `userId` and `title` because they lack individual resolvers; the resolver for the `posts` field retrieves their values, employing the `@http(path: "/posts")` directive. - -Applying the `@cache` directive at the type level affects all fields within that type. For example: - -```graphql -type Query { - posts: [Post] @http(path: "/posts") -} - -type Post @cache(maxAge: 100) { - id: Int - title: String - userId: Int - user: User @http(path: "/user/{{.value.userId}}") -} - -type User { - id: Int - name: String - email: String -} -``` - -You can simplify this configuration to show that applying the `@cache` directive to a type means every field within that type inherits it: - -```graphql -type Query { - posts: [Post] @http(path: "/posts") -} - -type Post { - id: Int @cache(maxAge: 100) - title: String @cache(maxAge: 100) - userId: Int @cache(maxAge: 100) - user: User - @http(path: "/user/{{.value.userId}}") - @cache(maxAge: 100) -} - -type User { - id: Int - name: String - email: String -} -``` - -Since the `@cache` directive does not affect fields without resolvers, the effective configuration can be further reduced as follows: - -```graphql -type Query { - posts: [Post] @http(path: "/posts") -} - -type Post { - id: Int - title: String - userId: Int - user: User - @http(path: "/user/{{.value.userId}}") - @cache(maxAge: 100) -} - -type User { - id: Int - name: String - email: String -} -``` - -When applying the `@cache` directive both at the type level and on individual fields within that type, the field-level directive takes precedence: - -```graphql -type Query { - posts: [Post] @http(path: "/posts") -} - -type Post @cache(maxAge: 200) { - id: Int - title: String - userId: Int - user: User - @http(path: "/user/{{.value.userId}}") - @cache(maxAge: 100) -} - -type User { - id: Int - name: String - email: String -} -``` - -Thus, in the configuration above, while all fields inherit the `@cache(maxAge: 200)` directive at the type level, the `user` field's explicit `@cache(maxAge: 100)` directive takes precedence. - -### Cache Key - -The caching mechanism generates a hash based on information related to the applied query to serve as the cache key for the corresponding value. - -For instance, the system caches the `user` field in the following configuration, using the hash of the interpolated string `"/user/{{.value.userId}}"` as the cache key. For example, if `Post.userId` equals `1`, the system generates the cache key by hashing the string `"/users/1"`. - -## @call Directive - -The `@call` directive in GraphQL signifies a shift towards more efficient configuration management by introducing a methodology akin to function invocations in conventional programming. This directive is pivotal for developers navigating the intricacies of elaborate GraphQL schemas, where minimizing redundancy and adhering to the DRY (Don't Repeat Yourself) principle are paramount. Consider the following schema example: - -```graphql showLineNumbers -schema - @upstream( - baseURL: "https://jsonplaceholder.typicode.com" - ) { - query: Query -} - -type Query { - # highlight-start - user(id: Int!): User @http(path: "/users/{{.args.id}}") - # highlight-end - posts: [Post] @http(path: "/posts") -} - -type Post { - id: Int! - userId: Int! - title: String! - body: String! - # highlight-start - user: User @http(path: "/users/{{.value.userId}}") - # highlight-end -} - -type User { - id: Int! - name: String! - email: String! -} -``` - -In this schema, at lines `9` and `18`, a pattern of configuration duplication emerges when fetching user's data by its id, demonstrating a prime use case for the `@call` directive. Through refactoring the `Post` type to incorporate the `@call` directive, we can eliminate this redundancy. - -```graphql showLineNumbers -type Post { - id: Int! - userId: Int! - title: String! - body: String! - # highlight-start - user: User - @call( - steps: [ - {query: "user", args: {id: "{{.value.userId}}"}} - ] - ) - # highlight-end -} -``` - -Here, the `@call` directive invokes the `user` query from the `Query` type, leveraging the data-fetching process that's already defined in the root `query`. The `query` parameter specifies the target field, while the `args` parameter delineates the arguments to be passed. - -### steps - -`@call` directive can compose together other resolvers, allowing to create a chain of resolvers that can be executed in sequence. This is done by using the `steps` parameter, which is an array of objects that define the operations to be executed. - -### query - -Specify the root **query** field to invoke, alongside the requisite arguments, using the `@call` directive for a concise and efficient query structure. - -```graphql showLineNumbers -type Post { - userId: Int! - user: User - @call( - steps: [ - {query: "user", args: {id: "{{.value.userId}}"}} - ] - ) -} -``` - -### mutation - -Similarly, the `@call` directive can facilitate calling a mutation from another mutation field, employing the `mutation` parameter for field specification and the `args` parameter for argument delineation. - -```graphql showLineNumbers -type Mutation { - insertPost(input: PostInput, overwrite: Boolean): Post - @http( - body: "{{.args.input}}" - method: "POST" - path: "/posts" - query: {overwrite: "{{.args.overwrite}}"} - ) - - upsertPost(input: PostInput): Post - @call( - steps: [ - { - mutation: "insertPost" - args: {input: "{{.args.input}}", overwrite: true} - } - ] - ) -} -``` - -### args - -The `args` parameter in the `@call` directive facilitates passing arguments to the targeted query or mutation, represented as a key-value mapping where each key corresponds to an argument name and its associated value. - -```graphql showLineNumbers -type Post { - userId: Int! - user: User - @call( - steps: [ - {query: "user", args: {id: "{{.value.userId}}"}} - ] - ) -} -``` - -:::tip -The `@call` directive is predominantly advantageous in complex, large-scale configurations. For those new to GraphQL or Tailcall, it may be beneficial to explore this directive after familiarizing yourself with the foundational aspects of GraphQL. -::: - -### Composition - -`@call` directive provides the ability to express a sequence of steps that one might need to compose. These steps are executed such that the result of each step is passed as an argument to the next step. The `query` and `mutation` parameters are used to specify the target field, while the `args` parameter is used to pass arguments to the target field. - -Let's explain this with an example: - -```graphql showLineNumbers -schema @server { - query: Query -} - -type Query { - a(input: JSON): JSON - @expr(body: {value: "{{.args.input.a}}"}) - - b(input: JSON): JSON - @expr(body: {value: "{{.args.input.b}}"}) - - c(input: JSON): JSON - @expr(body: {value: "{{.args.input.c}}"}) -} -``` - -Here we have defined there operations viz. `a`, `b` & `c` each of them pluck their respective keys from the given input value. Let's run this query with some test input: - -```graphql -{ - a(input: {a: 100}) - b(input: {b: 200}) - c(input: {c: 300}) -} -``` - -Here is how the response would look like: - -```json -{ - "data": { - "a": { - "value": 100 - }, - "b": { - "value": 200 - }, - "c": { - "value": 300 - } - } -} -``` - -As you can see the [`@expr`](#expr-directive) directive plucks the inner value and returns the result. How about we implement an `abc` operation that could leverage the existing operations and unwrap the following input value: - -```json -{"a": {"b": {"c": {"d": 1000}}}} -``` - -Given the above input if we wish to extract the last inner number `1000` then we could define a new operation as follows - -```graphql showLineNumbers -schema @server { - query: Query -} - -type Query { - a(input: JSON): JSON - @expr(body: {value: "{{.args.input.a}}"}) - - b(input: JSON): JSON - @expr(body: {value: "{{.args.input.b}}"}) - - c(input: JSON): JSON - @expr(body: {value: "{{.args.input.c}}"}) - - # highlight-start - abc(input: JSON): JSON - @call( - steps: [ - {query: "a", args: {input: "{{.args.input}}"}} - {query: "b", args: {input: "{{.args.value}}"}} - {query: "c", args: {input: "{{.args.value}}"}} - ] - ) - # highlight-end -} -``` - -We use the `@call` directive to compose the operations together. The `args` specify how we would like to pass the arguments to the operation and the result of that operation is passed to the next step. We can test the new `abc` operation with the following query: - -```graphql -query { - abc(input: {a: {b: {c: 1000}}}) -} -``` - -The server returns the response that we expected: - -```json -{ - "data": { - "abc": { - "value": 100 - } - } -} -``` - -This way you can compose combine multiple operations can compose them together using the `@call` directive. - -:::note -We use `JSON` scalar here because we don't care about the type safety of this option. In a real world example you might want to use proper input and output types. -::: - -## @expr Directive - -The `@expr` directive in GraphQL is a powerful tool for embedding data directly into your schema, offering two primary functionalities: - -### Static - -This feature allows for the inclusion of a constant response within the schema definition itself. It is useful for scenarios where the response is unchanging. e.g: - -```graphql -schema { - query: Query -} - -type Query { - user: User @expr(body: {name: "John", age: 12}) -} - -type User { - name: String - age: Int -} -``` - -The `@expr` directive also checks the provided value at compile time to ensure it matches the field's schema. If not, the console displays a descriptive error message. - -### Dynamic - -Beyond static data embedding, the `@expr` directive extends its utility to support dynamic data injection through Mustache template syntax. This feature enables the use of placeholders within the constant data, which are then dynamically replaced with actual values at runtime. It supports both scalar values and complex objects, including lists and nested objects, offering flexibility in tailoring responses to specific needs. e.g: - -```graphql -schema { - query: Query -} - -type Query { - user: User - @expr( - body: { - name: "John" - workEmail: "john@xyz.com" - personalEmail: "john@xyz.com" - } - ) -} - -type User { - name: String - age: Int - personalEmail: String - workEmail: String - emails: Emails - @expr( - body: { - emails: { - workEmail: "{{.value.workEmail}}" - personalEmail: "{{.value.personalEmail}}" - } - } - ) -} - -type Emails { - workEmail: String - personalEmail: String -} -``` - -In this example, the `@expr` directive dynamically generate an `Emails` object based on the provided template data. The placeholders within the template (`{{.value.workEmail}}` and `{{.value.personalEmail}}`) gets replaced with the actual values specified in the `User` type, allowing for dynamic content generation while still adhering to the schema's structure. - -## @graphQL Directive - -The `@graphQL` directive allows to specify GraphQL API server request to fetch data from. - -```graphql showLineNumbers -type Query { - users: [User] @graphQL(name: "userList") -} -``` - -The `@graphQL` directive facilitates fetching a list of users from the GraphQL API upstream. The [name](#name) argument specifies the root field's name on the upstream server. The upcoming request to the GraphQL server determines the `User` type's inner fields for the request. Depending on the operation type within which one finds the `@graphQL` directive, the GraphQL configuration determines the query's operation type. - -For the next request with the config above: - -```graphql showLineNumbers -query { - users { - id - name - } -} -``` - -Tailcall will request the next query for the upstream: - -```graphql showLineNumbers -query { - userList { - id - name - } -} -``` - -### baseURL - -This refers to the base URL of the API. If not specified, the default base URL is the one specified in the [`@upstream`](#upstream-directive) directive. - -```graphql showLineNumbers -type Query { - users: [User] - @graphQL( - name: "users" - baseURL: "https://graphqlzero.almansi.me/api" - ) -} -``` - -### name - -The root field's name on the upstream to request data from. For example: - -```graphql showLineNumbers -type Query { - users: [User] @graphQL(name: "userList") -} -``` - -When Tailcall receives a query for the `users` field, it will request a query for `userList` from the upstream. - -### args - -Named arguments for the requested field. For example: - -```graphql showLineNumbers -type Query { - user: User - @graphQL( - name: "user" - args: [{key: "id", value: "{{.value.userId}}"}] - ) -} -``` - -Will request the next query from the upstream for the first user's name: - -```graphql showLineNumbers -query { - user(id: 1) { - name - } -} -``` - -### headers - -The `headers` parameter allows customizing the headers of the GraphQL request made by the `@graphQL` directive. Specifying a key-value map of header names and their values achieves this. - -For instance: - -```graphql showLineNumbers -type Mutation { - users: User - @graphQL( - name: "users" - headers: [{key: "X-Server", value: "Tailcall"}] - ) -} -``` - -In this example, a request to `/users` will include the HTTP header `X-Server` with the value `Tailcall`. - -### batch - -In case the upstream GraphQL server supports request batching, we can specify the `batch` argument to batch requests to a single upstream into a single batch request. For example: - -```graphql showLineNumbers -schema - @upstream( - batch: { - maxSize: 1000 - delay: 10 - headers: ["X-Server", "Authorization"] - } - ) { - query: Query - mutation: Mutation -} - -type Query { - users: [User] @graphQL(name: "users", batch: true) - posts: [Post] @graphQL(name: "posts", batch: true) -} -``` - -Make sure you have also specified batch settings to the `@upstream` and to the `@graphQL` directive. - -## @grpc Directive - -The `@grpc` directive enables the resolution of GraphQL fields via gRPC services. Below is an illustrative example of how to apply this directive within a GraphQL schema: - -```graphql -schema @link(src: "./users.proto", type: Protobuf) { - query: Query -} - -type Query { - users: [User] @grpc(method: "users.UserService.ListUsers") -} -``` - -This schema snippet demonstrates the directive's application, where a query for `users` triggers a gRPC request to the `UserService`'s `ListUsers` method, thereby fetching the user data. - -The `.proto` file delineates the structure and methods of the gRPC service. A simplified example of such a file is as follows: - -```proto -syntax = "proto3"; - -package users; - -service UserService { - rpc ListUsers (UserListRequest) returns (UserListReply) {} - rpc GetUser (UserGetRequest) returns (UserGetReply) {} -} - -message UserListRequest { - // Definitions of request parameters -} - -message UserListReply { - // Structure of the reply -} - -message UserGetRequest { - // Definitions of request parameters -} - -message UserGetReply { - // Structure of the reply -} -``` - -:::important -It is mandatory to have a package name in a protobuf file. -::: - -Linking this file within a GraphQL schema is facilitated by the `@link` directive, as shown below: - -```graphql -schema @link(src: "./users.proto", type: Protobuf) { - query: Query -} -``` - -Tailcall automatically resolves the protobuf file for any methods referenced in the `@grpc` directive. - -### method - -This parameter specifies the gRPC service and method to be invoked, formatted as `..`: - -```graphql -type Query { - users: [User] - @grpc(method: "proto.users.UserService.ListUsers") -} -``` - -### baseURL - -Defines the base URL for the gRPC API. If not specified, the URL set in the `@upstream` directive is used by default: - -```graphql -type Query { - users: [User] - @grpc( - baseURL: "https://grpc-server.example.com" - method: "proto.users.UserService.ListUsers" - ) -} -``` - -### body - -This parameter outlines the arguments for the gRPC call, allowing for both static and dynamic inputs: - -```graphql -type UserInput { - id: ID -} - -type Query { - user(id: UserInput!): User - @grpc( - body: "{{.args.id}}" - method: "proto.users.UserService.GetUser" - ) -} -``` - -### headers - -Custom headers for the gRPC request can be defined, facilitating the transmission of authentication tokens or other contextual data: - -```graphql -type Query { - users: [User] - @grpc( - headers: [ - {key: "X-CUSTOM-HEADER", value: "custom-value"} - ] - method: "proto.users.UserService.ListUsers" - ) -} -``` - -### batchKey - -This argument is employed to optimize batch requests by grouping them based on specified response keys, enhancing performance in scenarios requiring multiple, similar requests: - -```graphql -type Query { - users(id: UserInput!): [User] - @grpc( - batchKey: ["id"] - method: "proto.users.UserService.ListUsers" - baseURL: "https://grpc-server.example.com" - ) -} -``` - -:::info -Read about [n + 1](./N+1.md) to learn how to use the `batchKey` setting. -::: - -### onResponseBody - -This hook allows you to intercept and modify the response body from upstream services before it's processed by Tailcall. Like [onRequest](/docs/directives.md#onrequest), it accepts a string value representing a middleware function defined in a JavaScript file. This function can be used to transform or validate the response data. - -```graphql showLineNumbers -type Query { - news: NewsData! - @grpc( - method: "news.NewsService.GetAllNews" - onResponseBody: "onResponse" - ) -} -``` - -## @http Directive - -The `@http` directive indicates a field or node relies on a REST API. For example: - -```graphql showLineNumbers -type Query { - users: [User] @http(path: "/users") -} -``` - -In this example, adding the `@http` directive to the `users` field of the `Query` type indicates reliance on a REST API for the `users` field. The [path](#path) argument specifies the REST API's path, which is `/users` in this scenario.Querying the `users` field prompts the GraphQL server to issue a GET request to `https://jsonplaceholder.typicode.com/users`. - -### baseURL - -Specifies the API's base URL. If unspecified, it defaults to the URL in the [`@upstream`](#upstream-directive) directive. - -```graphql showLineNumbers -type Query { - users: [User] - @http( - path: "/users" - baseURL: "https://jsonplaceholder.typicode.com" - ) -} -``` - -### path - -Refers to the API endpoint, for example, `https://jsonplaceholder.typicode.com/users`. - -```graphql showLineNumbers -type Query { - users: [User] @http(path: "/users") -} -``` - -If your API endpoint contains dynamic segments, you can substitute variables using Mustache templates. For example, to fetch a specific user, you can write the path as `/users/{{.args.id}}`. - -```graphql showLineNumbers -type Query { - user(id: ID!): User @http(path: "/users/{{.args.id}}") -} -``` - -### method - -Specifies the HTTP method for the API call. The default method is GET if not specified. - -```graphql showLineNumbers -type Mutation { - createUser(input: UserInput!): User - @http(method: "POST", path: "/users") -} -``` - -### query - -Represents the API call's query parameters, either as a static object or with dynamic parameters using Mustache templates. These parameters append to the URL. - -```graphql showLineNumbers -type Query { - userPosts(id: ID!): [Post] - @http( - path: "/posts" - query: [ - { - key: "userId" - value: "{{.args.id}}" - skipEmpty: false - } - ] - ) -} -``` - -The `query` field and be further configured using the following fields: - -1. **key** : Represents the name of the query parameter. -2. **value** : A string literal or a mustache template representing the value of query parameter. -3. **skipEmpty** : When set to `true` the query parameter is skipped if the value of the parameter is null, defaults to false. - -:::important -When `batchKey` is present, Tailcall considers the first `query` parameter to be the batch query key, so remember to adjust the order of the items accordingly. -::: - -### body - -Defines the API call's body, necessary for methods like POST or PUT. Pass it as a static object or use Mustache templates for variable substitution from the GraphQL variables. - -```graphql showLineNumbers -type Mutation { - createUser(input: UserInput!): User - @http( - method: "POST" - path: "/users" - body: "{{.args.input}}" - ) -} -``` - -In the example above, the `createUser` mutation sends a POST request to `/users`, with the input object converted to JSON and included in the request body. - -### headers - -Customizes the HTTP request headers made by the `@http` directive. Specify a key-value map of header names and values. - -For instance: - -```graphql showLineNumbers -type Mutation { - createUser(input: UserInput!): User - @http( - path: "/users" - headers: [{key: "X-Server", value: "Tailcall"}] - ) -} -``` - -In this example, a request to `/users` will include a HTTP header `X-Server` with the value `Tailcall`. - -You can make use of mustache templates to provide dynamic values for headers, derived from the arguments or [context] provided in the request. For example: - -[context]: /docs/graphql-resolver-context-tailcall - -```graphql showLineNumbers -type Mutation { - users(name: String): User - @http( - path: "/users" - headers: [ - {key: "X-Server", value: "Tailcall"} - {key: "User-Name", value: "{{.args.name}}"} - ] - ) -} -``` - -In this scenario, the `User-Name` header's value will dynamically adjust according to the `name` argument passed in the request. - -### batchKey - -Groups data requests into a single call, enhancing efficiency. Refer to our [n + 1 guide](./N+1.md) for more details. - -:::important -When `batchKey` is present, Tailcall considers the first `query` parameter to be the batch query key, so remember to adjust the order of the items accordingly. Whereas, the last item from `batchKey` is used to instruct which field is the ID of an object. In case that the returned result is a nested property `batchKey` can be used as a path to extract and group the items for the returned result. -::: - -```graphql showLineNumbers -type Post { - id: Int! - name: String! - user: User - @http( - path: "/users" - query: [{key: "user_id", value: "{{.value.userId}}"}] - batchKey: ["users", "id"] - ) -} -``` - -- `query: {key: "user_id", value: "{{.value.userId}}"}]`: Instructs Tailcall CLI to generate a URL aligning the user id with `userId` from the parent `Post`, compiling a single URL for a batch of posts, such as `/users?user_id=1&user_id=2&user_id=3...user_id=10`, consolidating requests into one. - -### onRequest - -The `onRequest` property accepts a string value representing the remote function to be called every time an HTTP request is initiated. Typically the remote function is defined in a linked JavaScript worker file. - -:::note -For defining a request middleware globally for all requests, refer to the [upstream directive documentation](/docs/directives.md#onrequest-1). -::: - -```graphql showLineNumbers -type Query { - userPosts(id: ID!): [Post] - @http( - path: "/posts" - query: [{key: "userId", value: "{{.args.id}}"}] - onRequest: "someFunctionName" - ) -} -``` - -### onResponseBody - -This hook allows you to intercept and modify the response body from upstream services before it's processed by Tailcall. Like [onRequest](/docs/directives.md#onrequest), it accepts a string value representing a middleware function defined in a JavaScript file. This function can be used to transform or validate the response data. - -```graphql showLineNumbers -type Query { - user(id: Int!): User - @http( - path: "/users/{{.args.id}}" - onResponseBody: "onResponse" - ) -} -``` - -## @js Directive - -The `@js` directive allows you to use JavaScript functions to resolve fields in your GraphQL schema. This can be useful -for custom data transformations or complex field resolutions. - -### Usage - -The `@js` directive is used to specify a JavaScript function that will resolve the value of a field. The directive takes -a single argument, `name`, which is the name of the JavaScript function to be used. - -### Syntax - -```graphql showLineNumbers -fieldName: FieldType @js(name: "functionName") -``` - -### Example - -Let's consider a `foo.js` file which contains a `resolve` function: - -```js -function resolve(val) { - let json = JSON.parse(val) - return JSON.stringify(json.id) -} -``` - -Here is an example of how the `@js` directive is used within a GraphQL schema: - -```gql showLineNumbers -schema - @link(type: Script, src: "./scripts/foo.js") - @server(port: 8000) - @upstream( - baseURL: "http://jsonplaceholder.typicode.com" - httpCache: true - ) { - query: Query -} - -type Query { - posts: [Post] @http(path: "/posts") -} - -type Post { - id: Int! - idx: Int! @js(name: "resolve") - userId: Int! - title: String! - body: String! -} -``` - -### Error Handling - -When using the `@js` directive, it is important to handle errors within your JavaScript functions. For example, you can use try-catch blocks to catch and handle any errors that occur during the resolution process. - -```javascript -function resolve(val) { - try { - let json = JSON.parse(val) - return JSON.stringify(json.id) - } catch (error) { - console.error("Error resolving value:", error) - throw new Error("Failed to resolve value") - } -} -``` - -### Performance Considerations - -When using the `@js` directive, keep in mind that JavaScript functions can introduce performance overhead, especially if they perform complex operations or are called frequently. To minimize performance impact, ensure that your functions are optimized and avoid unnecessary computations. - -## @link Directive - -The `@link` directive is used for bringing external resources into your GraphQL schema. It makes it easier to include configurations, .proto files for gRPC services, and other files into your schema. With this directive, external resources are either merged with or used effectively in the importing configuration. - -### How it Works - -The `@link` directive requires specifying a source `src`, the resource's type `type`, and an optional identifier `id`. - -- `src`: The source of the link is defined here. It can be either a URL or a file path. When a file path is given, it's relative to the file's location that is importing the link. - -- `type`: This specifies the link's type, which determines how the imported resource is integrated into the schema. For a list of supported types, see the [Supported Types](#supported-types) section. - -- `id`: This is an optional field that assigns a unique identifier to the link. It's helpful for referring to the link within the schema. - -### Example - -The following example illustrates how to utilize the `@link` directive to incorporate a Protocol Buffers (.proto) file for a gRPC service into your GraphQL schema. - -```graphql showLineNumbers -schema - @server(port: 8000) - @upstream( - baseURL: "http://news.local" - httpCache: 42 - batch: {delay: 10} - ) - @link( - id: "news" - src: "./src/grpc/news.proto" - type: Protobuf - ) { - query: Query -} - -type Query { - news: NewsData! - @grpc(method: "news.NewsService.GetAllNews") -} - -type News { - id: Int - title: String - body: String - postImage: String -} - -type NewsData { - news: [News]! -} -``` - -### Supported Types - -The `@link` directive enriches your configuration by supporting the integration of external resources. Each link type is designed to serve a specific purpose, enhancing the functionality and flexibility of your schema. Below is a detailed overview of each supported link type: - -### Config - -The `Config` link type is essential for importing other configuration files. This feature enables a modular approach to schema management by allowing configurations from the imported file to override overlapping settings in the main schema. This functionality is useful in large projects, where maintaining a single monolithic schema file becomes impractical. By using `Config`, developers can split their schema configurations into manageable pieces, thus promoting better organization and scalability. - -Example use case: - -- Modularizing schema configurations for different environments (development, staging, production). -- Reusing common configurations across multiple schema files. - -### Protobuf - -The `Protobuf` link type integrates Protocol Buffers definitions by importing .proto files. This integration is crucial for Tailcall to communicate with gRPC services. By including `.proto` definitions, the GraphQL server can directly interact with gRPC services, allowing for efficient and type-safe communication. - -For detailed integration steps and best practices, refer to the [gRPC Integration Guide](/docs/grpc.md). - -### Script - -The `Script` link type allows the config to link to an external JavaScript file. This file can contain custom logic that is executed in response to HTTP request-response events. This feature enables developers to implement custom behaviors, such as adding headers to responses or filtering requests based on specific criteria. - -Example script for adding a custom header to all outgoing requests: - -```javascript showLineNumbers -function onRequest({request}) { - // Add a custom header for all outgoing requests - request.headers["X-Custom-Header"] = "Processed" - - // Return the updated request - return {request} -} -``` - -### Cert - -The `Cert` link type is designed for importing SSL/TLS certificates, a crucial component for enabling HTTPS in your GraphQL server. This link type ensures that the server can expose connections over HTTPS. - -:::tip -When using the `Cert` link type, specify the path to the certificate file. Ensure the certificate is up-to-date and issued by a trusted certificate authority (CA) to avoid security warnings or connection issues. -::: - -Example use case: - -- Securing communication between the GraphQL server and clients. -- Enhancing privacy and security by encrypting data in transit. - -### Key - -The `Key` link type imports the private key associated with your SSL/TLS certificate, enabling HTTPS for your GraphQL server. The private key is a critical security element that decrypts information encrypted by the corresponding public key in the SSL/TLS certificate. - -When configuring the `Key` link type, provide the path to your private key file. Ensure the private key matches the imported certificate specified by the [Cert](#cert) link above, and is protected by appropriate file permissions to maintain security. - -### Operation - -The `Operation` link type connects your schema to a set of predefined, GraphQL spec-compliant queries and mutations. This functionality allows for the validation and optimization of these operations by the GraphQL server. - -Each type serves a specific purpose, enabling the flexible integration of external resources into your GraphQL schema. - -### Htpasswd - -The `Htpasswd` link type allows the importation of an [`htpasswd`](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) file. This file is utilized to set up [Basic authentication](./auth.md#basic-authentication). - -### Jwks - -The `Jwks` link type enables the importation of a [`JWKS`](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-key-sets) file. This file facilitates the provision of detailed access control through [JWT authentication](./auth.md#jwt-authentication). - -## @modify Directive - -The `@modify` directive in GraphQL provides the flexibility to alter the attributes of a field or a node within your GraphQL schema. Here's how you can use this directive: - -### name - -You can rename a field or a node in your GraphQL schema using the `name` argument in the `@modify` directive. This can be helpful when the field name in your underlying data source doesn't match the desired field name in your schema. For instance: - -```graphql showLineNumbers -type User { - id: Int! @modify(name: "userId") -} -``` - -`@modify(name: "userId")` informs GraphQL to present the field known as `id` in the underlying data source as `userId` in your schema. - -### omit - -You can exclude a field or a node from your GraphQL schema using the `omit` argument in the `@modify` directive. This can be useful if you want to keep certain data hidden from the client. For instance: - -```graphql showLineNumbers -type User { - id: Int! @modify(omit: true) -} -``` - -`@modify(omit: true)` instructs GraphQL to exclude the `id` field from the schema, making it inaccessible to the client. - -:::tip -`@omit` is a standalone directive and is an alias/shorthand for `modify(omit: true)` checkout [documentation](/docs/directives.md#omit-directive) -::: - -## @omit Directive - -Within a GraphQL schema, the `@omit` directive excludes fields or nodes from the generated schema, making them inaccessible through the GraphQL API. This directive is useful for hiding sensitive information or simplifying your API by removing unnecessary fields. - -### How it works - -When applied to a field or node, the `@omit` directive instructs the Tailcall not to include that field or node in the schema. This means that clients cannot query or mutate data in those fields. - -### Example - -Consider a scenario where you have a `User` type with an embedded `Address` type. If you want to exclude the `Address` type from the schema to simplify the API, you can use the `@omit` directive: - -```graphql showLineNumbers -type Address { - city: String - street: String -} - -type User { - name: String - address: Address @omit -} -``` - -In this example, the `address` field will not be accessible or visible through the GraphQL API. - -### Comparison with `modify` - -The `@omit` directive and `@modify(omit: true)` essentially serve the same purpose in excluding fields from the schema, but they differ in syntax and flexibility. In fact, one can consider `@omit` as a shorthand or alias for the more verbose `@modify(omit: true)`. - -- `@omit` offers a concise way to directly exclude a field or node without additional arguments. - -- `@modify(omit: true)`, as part of the broader [`@modify`](/docs/directives.md#omit-directive) directive, provides more options, such as field renaming through the `name` argument. This makes it a more flexible choice when you need more than field exclusion. - -## @protected Directive - -The `@protected` annotation designates a type or field as protected, meaning that a user must be authenticated to access that data. - -```graphql -type Query { - protected: String! @protected - protectedType: ProtectedType -} - -type ProtectedType @protected { - name: String! - nested: String! -} -``` - -:::important -To utilize the `@protected` directive, you must link at least one authentication provider in the configuration using the [`@link`](#link-directive) directive (`Htpasswd` or `Jwks`). -::: - -### How It Works - -- When a field is annotated with `@protected`, an authentication check is performed upon receiving the request. Depending on the authentication result, either the requested data is provided in the response, or an authentication error is returned. -- If a type is annotated with `@protected`, all fields within that type inherit the protection, requiring user authentication for any field that's queried. - -## @rest Directive - -API orchestration is essential, yet not all can adopt GraphQL despite its benefits. The Tailcall DSL feature leverages GraphQL at compile time to generate REST endpoints, aligning with traditional API infrastructure like CDNs and Gateways. - -### Usage - -- **method**: Specifies the HTTP method (GET, POST, etc.). -- **path**: Sets the endpoint URL, with support for dynamic values from query arguments. -- **query**: Defines the query parameters as key-value pairs. - -### Example - -Define GraphQL types and queries, using the `@rest` directive to map fields to REST API endpoints. - -`schema.graphql` - -```graphql -schema - @upstream(baseURL: "https://jsonplaceholder.typicode.com") - @link(type: Operation, src: "user-operation.graphql") { - query: Query -} - -type Query { - user(id: Int!): User - @rest(method: "GET", path: "/users/{{.args.id}}") -} - -type User { - id: Int! - name: String! - email: String! -} -``` - -`user-operation.graphql` - -```graphql -query ($id: Int!) @rest(method: GET, path: "/user/$id") { - user(id: $id) { - id - name - } -} -``` - -![REST Demo](/images/docs/rest-user.png) - -This example demonstrates how to define a simple query to fetch user data from a REST endpoint using the `@rest` directive. By leveraging `@rest`, GraphQL can serve as a layer over RESTful services, combining REST's simplicity with GraphQL's flexibility. - -## @server Directive - -The `@server` directive, applied at the schema level, provides a comprehensive set of server configurations. It dictates server behavior and helps tune Tailcall for a range of use-cases. - -```graphql showLineNumbers -schema @server(...[ServerSettings]...){ - query: Query - mutation: Mutation -} -``` - -In this templated structure, replace `...[ServerSettings]...` with specific configurations tailored to your project's needs. Adjust and expand these settings as necessary. - -The `ServerSettings` options and their details appear below. - -### workers - -Setting `workers` to `32` means that the GraphQL server will use 32 worker threads. - -```graphql showLineNumbers -schema @server(workers: 32) { - query: Query - mutation: Mutation -} -``` - -This example sets the `workers` to `32`, meaning the GraphQL server will use 32 worker threads. - -### port - -Setting the `port` to `8090` means that Tailcall will be accessible at `http://localhost:8000`. - -```graphql showLineNumbers -schema @server(port: 8090) { - query: Query - mutation: Mutation -} -``` - -This example sets the `port` to `8090`, making Tailcall accessible at `http://localhost:8090`. - -:::tip -Always choose non-standard ports, avoiding typical ones like 80 or 8080. Make sure your chosen port is free. -::: - -### headers - -Allows intelligent configuration of the final response headers that's produced by Tailcall. - -### cacheControl - -Activating the `cacheControl` configuration directs Tailcall to send [Cache-Control] headers in its responses. The `max-age` value in the header matches the lowest of the values in the responses that Tailcall receives from its upstream. By default, this is `false`, which means Tailcall does not set any header. - -[cache-control]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control - -```graphql showLineNumbers -schema @server(headers: {cacheControl: true}) { - query: Query - mutation: Mutation -} -``` - -### custom - -The `custom` is an array of key-value pairs. These headers get added to the response of every request made to the server. This can be useful for adding headers like `Access-Control-Allow-Origin` to allow cross-origin requests, or some headers like `X-Allowed-Roles` for use by downstream services. - -```graphql showLineNumbers -schema - @server( - headers: { - custom: [ - {key: "X-Allowed-Roles", value: "admin,user"} - ] - } - ) { - query: Query - mutation: Mutation -} -``` - -### experimental - -When the `experimental` configuration is enabled, Tailcall can include headers starting with `X-` in its responses, which are sourced from its upstream. By default, this feature is disabled (`[]`), meaning Tailcall does not forward any such headers unless explicitly configured to do so. - -```graphql showLineNumbers -schema - @server( - headers: {experimental: ["X-Experimental-Header"]} - ) { - query: Query - mutation: Mutation -} -``` - -### setCookies - -Enabling the `setCookies` option instructs Tailcall to include `set-cookie` headers in its responses, which are obtained from the headers of upstream responses. - -[set-cookie]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/set-cookie - -```graphql showLineNumbers -schema @server(headers: {setCookies: true}) { - query: Query - mutation: Mutation -} -``` - -### cors - -The `cors` configuration allows you to enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) on Tailcall. This is useful when you want to access Tailcall in the browser. Here is a simple configuration to get started with cors: - -```graphql showLineNumbers -schema - @server( - headers: { - cors: {allowHeaders: ["*"], allowOrigins: ["*"]} - } - ) { - query: Query -} -``` - -The above setting will enable CORS on the server for all headers, origins & methods. You can further configure the cors settings to make it more secure with the following fields: - -- `allowCredentials`: Indicates whether the server allows credentials (e.g., cookies, authorization headers) to be sent in cross-origin requests. -- `allowHeaders`: A list of allowed headers in cross-origin requests. This can be used to specify custom headers that are allowed to be included in cross-origin requests. -- `allowMethods`: A list of allowed HTTP methods in cross-origin requests. These methods specify the actions that are permitted in cross-origin requests. -- `allowOrigins`: A list of origins that are allowed to access the server's resources in cross-origin requests. An origin can be a domain, a subdomain, or even 'null' for local file schemes. -- `allowPrivateNetwork`: Indicates whether requests from private network addresses are allowed in cross-origin requests. Private network addresses typically include IP addresses reserved for internal networks. -- `exposeHeaders`: A list of headers that the server exposes to the browser in cross-origin responses. Exposing certain headers allows client-side code to access them in the response. -- `maxAge`: The maximum time (in seconds) that the client should cache preflight OPTIONS requests to avoid sending excessive requests to the server. -- `vary`: A list of header names that indicate the values of which might cause the server's response to vary, potentially affecting caching. - -```graphql showLineNumbers -schema - @server( - port: 8000 - hostname: "0.0.0.0" - headers: { - cors: { - allowCredentials: false - allowHeaders: ["Authorization"] - allowMethods: [POST, GET, OPTIONS] - allowOrigins: ["abc.xyz"] - allowPrivateNetwork: true - exposeHeaders: ["Content-Type"] - maxAge: 360 - vary: ["Origin"] - } - } - ) { - query: Query -} -``` - -### vars - -This configuration allows defining local variables for use during the server's operations. These variables are handy for storing constant configurations, secrets, or other shared information that operations might need. - -```graphql showLineNumbers -schema - @server( - vars: {key: "apiKey", value: "YOUR_API_KEY_HERE"} - ) { - query: Query - mutation: Mutation -} - -type Query { - externalData: Data - @http( - path: "/external-api/data" - headers: [ - { - key: "Authorization" - value: "Bearer {{.vars.apiKey}}" - } - ] - ) -} -``` - -In the provided example, setting a variable named `apiKey` with a placeholder value of "YOUR_API_KEY_HERE" implies that whenever Tailcall fetches data from the `externalData` endpoint, it includes the `apiKey` in the Authorization header of the HTTP request. - -:::tip -Local variables, like `apiKey`, are instrumental in securing access to external services or providing a unified place for configurations. Ensure that sensitive information stored this way is well protected and not exposed unintentionally, if your GraphQL configuration is publicly accessible. -::: - -### introspection - -This setting controls the server's allowance of introspection queries. Introspection, a core feature of GraphQL, allows clients to directly fetch schema information. This capability proves crucial for tools and client applications in comprehending the available types, fields, and operations. By default, the server enables this setting (`true`). - -```graphql showLineNumbers -schema @server(introspection: false) { - query: Query - mutation: Mutation -} -``` - -:::tip -Although introspection is beneficial during development and debugging stages, consider disabling it in production environments. Turning off introspection in live deployments can enhance security by preventing potential attackers from discerning the schema and any associated business logic or data structures. -::: - -### queryValidation - -The `queryValidation` configuration determines if the server checks incoming GraphQL queries against the defined schema. Each query check ensures it matches the schema, preventing errors from incorrect or malformed queries. In some situations, you might want to disable it, notably to **enhance server performance** at the cost of these checks. This defaults to `false` if not specified. - -```graphql showLineNumbers -schema @server(queryValidation: true) { - query: Query - mutation: Mutation -} -``` - -The example above sets `queryValidation` to `true`, enabling the validation phase for incoming queries. - -:::tip -Enable this in the development environment to ensure the queries sent are correct and validated. In the production environment, consider disabling it for improved performance. -::: - -### responseValidation - -Tailcall can automatically infer the schema of the HTTP endpoints for you. This information can check responses received from the upstream services. Enabling this setting allows you to do that. If not specified, the default setting for `responseValidation` is `false`. - -```graphql showLineNumbers -schema @server(responseValidation: true) { - query: Query - mutation: Mutation -} -``` - -:::tip -Disabling this setting will offer major performance improvements, but at the potential expense of data integrity. -::: - -### globalResponseTimeout - -The `globalResponseTimeout` configuration sets the max duration a query can run before the server terminates it. Essentially, it acts as a safeguard against long-running queries that could strain resources or pose security concerns. - -If not explicitly defined, there might be a system-specific or default value that applies. - -```graphql showLineNumbers -schema @server(globalResponseTimeout: 5000) { - query: Query - mutation: Mutation -} -``` - -In this given example, setting the `globalResponseTimeout` to `5000` milliseconds, or 5 seconds, means any query execution taking longer than this duration will be automatically terminated by - -:::tip -Setting an appropriate response timeout in production environments is crucial. This optimizes resource use and serves as a security measure against potential denial-of-service attacks, where adversaries might run complex queries to exhaust server resources. -::: - -### version - -The server uses the HTTP version. If not specified, the default value is `HTTP1`. The available options are `HTTP1` and `HTTP2`. - -```graphql showLineNumbers -schema @server(version: HTTP2) { - query: Query - mutation: Mutation -} -``` - -### cert - -The path to certificate(s) for running the server over HTTP2 (HTTPS). If not specified, the default value is `null`. - -```graphql showLineNumbers -schema @server(cert: "./cert.pem") { - query: Query - mutation: Mutation -} -``` - - - -:::tip -The certificate can be of any extension, but it's highly recommended to use standards (`pem`, `crt`, `key`). -::: - -### key - -The path to the key for running the server over HTTP2 (HTTPS). If not specified, the default value is `null`. - -```graphql showLineNumbers -schema @server(key: "./key.pem") { - query: Query - mutation: Mutation -} -``` - -:::tip -The key can be of any extension, but it's highly recommended to use standards (`pem`, `crt`, `key`). -::: - -### showcase - -The `@server` directive's `showcase` option allows for hands-on experimentation with server configurations in a controlled environment. This feature simplifies the process of exploring and testing different settings. - -```graphql showLineNumbers -schema @server(showcase: true) { - query: Query -} -``` - -### batchRequests - -Batching in GraphQL combines requests into one, reducing server round trips. - -```graphql showLineNumbers -schema @server( - port: 8000 - batchRequests: true -) -``` - -:::tip -Batching can improve performance but may introduce latency if one request in the batch takes longer. It also makes network traffic debugging harder. -::: - -### dedupe - -A boolean flag, if set to `true`, will enable deduplication of IO operations to enhance performance. This flag prevents duplicate IO requests from being executed concurrently, reducing resource load. If not specified, this feature defaults to `false`. - -```graphql showLineNumbers -schema @server( - port: 8000 - dedupe: true -) -``` - -### routes - -This optional field allows you to customize the server's endpoint paths, enabling you to override the default values for the GraphQL and status endpoints. If not specified, the following default paths will be used: - -- graphQL: `/graphql` -- status: `/status` - -```graphql showLineNumbers -schema @server(routes: {graphQL: "/tailcall-gql", status: "/health"}) -``` - -In this example, the GraphQL endpoint is changed to `/tailcall-gql` and the status endpoint to `/health`. - -## @telemetry Directive - -The `@telemetry` directive facilitates seamless integration with [OpenTelemetry](https://open-telemetry.io), enhancing the observability of your GraphQL services powered by Tailcall. By leveraging this directive, developers gain access to valuable insights into the performance and behavior of their applications. - -### Traces - -Here are the traces that are captured by the `@telemetry` directive: - -| Trace Name | Description | -| --------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| request | Captures the span for processing the HTTP request on the server side, providing foundational observability. | -| graphQL | _Only for GraphQL ingress_. Span for processing GraphQL call | -| `REST ` | _Only for REST ingress_. Span for processing REST API call | -| `` | Denotes spans for fields with defined resolvers, offering insights into field names and execution times for resolver logic. | -| `` | Nested within the `` spans, these granulated spans detail the execution of expressions in resolving a field, highlighting the hierarchical execution pattern of nested expressions. | -| upstream_request | Request that were made from tailcall service to upstream | - -### Metrics - -The `@telemetry` directive also captures the following metrics: - -| Metric | Description | -| ------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------- | -| cache.hit_rate | Reflects the cache hit rate for the cache powered by the [`@cache`](#cache-directive) directive | -| http.server.request.count | Counts the number of incoming requests made to specific route. Optionally enriched with selected headers by [`requestHeaders`](#requestheaders) | -| http.client.request.count | Counts the number of outgoing requests to specific upstream | - -### export - -The `export` field defines how the open-telemetry data should be exported and in which format. The following are the supported formats: - -### otlp - -Utilizes the OTLP format to export telemetry data to backend systems, supported by most modern tracing and analytics platforms. Here is an example using [honeycomb.io]: - -[honecomb.io]: https://www.honeycomb.io/ - -```graphql -schema - @telemetry( - export: { - otlp: { - url: "https://api.honeycomb.io:443" - headers: [ - { - key: "x-honeycomb-team" - value: "{{.env.HONEYCOMB_API_KEY}}" - } - {key: "x-honeycomb-dataset", value: "tailcall"} - ] - } - } - ) { - query: Query -} -``` - -You can configure the OTLP exporter with the following options: - -| Field | Description | -| ------: | ----------------------------------------------------------- | -| url | Defines the URL for the OTLP Collector. | -| headers | Sets additional headers for requests to the OTLP Collector. | - -### prometheus - -Facilitates metrics export in a Prometheus compatible format, providing a dedicated endpoint for metrics. - -```graphql -schema - @telemetry(export: {prometheus: {path: "/metrics"}}) { - query: Query -} -``` - -You can configure the Prometheus exporter with the following options: - -| Field | Description | -| -----: | ---------------------------------------------------------------------------------- | -| path | Designates the endpoint path for Prometheus metrics, defaulting to `/metrics`. | -| format | Controls the format viz. **text** or **protobuf**, for sending data to Prometheus. | - -### stdout - -Outputs all telemetry data to stdout, ideal for testing or local development environments. - -```graphql -schema @telemetry(export: {stdout: {pretty: true}}) { - query: Query -} -``` - -You can configure the stdout exporter with the following options: - -| Field | Description | -| -----: | -------------------------------------------------------------------- | -| pretty | Enables formatted output of telemetry data for enhanced readability. | - -### requestHeaders - -Specifies list of headers of ingress request the value of which will be sent to the telemetry as attributes. - -```graphql -schema @telemetry(requestHeaders: ["X-User-Id"]) { - query: Query -} -``` - -### apollo - -Facilitates seamless integration with [Apollo Studio](https://studio.apollographql.com/), enhancing the observability of GraphQL services. By leveraging this field, developers gain access to valuable insights into the performance and behavior of their GraphQL APIs. - -```graphql -schema - @telemetry( - export: { - otlp: { - api_key: "{{.env.APOLLO_API_KEY}}" - graph_ref: "graph-id@current" - platform: "website.com" - version: "1.0.0" - } - } - ) { - query: Query -} -``` - -You can configure the apollo exporter with the following options: - -| Field | Description | -| --------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| api_key | The API Key generated from Apollo Studio. | -| graph_ref | The Graph Ref, which is the `graph_id` and the `variant` concatenated using `@`(i.e. \@\) | -| platform | An arbitrary value which can contain the name of your website or some other value to identify your deployment uniqely, in case you have multiple deployments. | -| version | Version of Apollo which is being used. | - -By integrating the `@telemetry` directive into your GraphQL schema, you empower your development teams with critical insights into application performance, enabling proactive optimization and maintenance. - -## @upstream Directive - -The `upstream` directive enables control over specific aspects of the upstream server connection, including settings such as connection timeouts, keep-alive intervals, and more. The system applies default values if you do not specify them. - -```graphql showLineNumbers -schema @upstream(...[UpstreamSetting]...){ - query: Query - mutation: Mutation -} -``` - -The document below details the options for `UpstreamSetting`. - -### poolIdleTimeout - -The connection pool waits for this duration in seconds before closing idle connections. - -```graphql showLineNumbers -schema - @upstream( - poolIdleTimeout: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### poolMaxIdlePerHost - -The max number of idle connections each host will maintain. - -```graphql showLineNumbers -schema - @upstream( - poolMaxIdlePerHost: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### keepAliveInterval - -The time in seconds between each keep-alive message sent to maintain the connection. - -```graphql showLineNumbers -schema - @upstream( - keepAliveInterval: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### keepAliveTimeout - -The time in seconds that the connection will wait for a keep-alive message before closing. - -```graphql showLineNumbers -schema - @upstream( - keepAliveTimeout: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### keepAliveWhileIdle - -A boolean value that determines whether to send keep-alive messages while the connection is idle. - -```graphql showLineNumbers -schema - @upstream( - keepAliveWhileIdle: false - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### proxy - -The `proxy` setting defines an intermediary server that routes upstream requests before they reach their intended endpoint. By specifying a proxy URL, you introduce a layer, enabling custom routing and security policies. - -```graphql showLineNumbers -schema - @upstream( - proxy: {url: "http://localhost:3000"} - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -In the provided example, we've set the proxy's `url` to "http://localhost:3000". This configuration ensures that all requests aimed at the designated `baseURL` first go through this proxy. To illustrate, if the `baseURL` is "http://jsonplaceholder.typicode.com", any request targeting it initially goes to "http://localhost:3000" before the proxy redirects it to its final destination. - -### connectTimeout - -The time in seconds that the connection will wait for a response before timing out. - -```graphql showLineNumbers -schema - @upstream( - connectTimeout: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### timeout - -The max time in seconds that the connection will wait for a response. - -```graphql showLineNumbers -schema - @upstream( - timeout: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### tcpKeepAlive - -The time in seconds between each TCP keep-alive message sent to maintain the connection. - -```graphql showLineNumbers -schema - @upstream( - tcpKeepAlive: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### userAgent - -The User-Agent header value for HTTP requests. - -```graphql showLineNumbers -schema - @upstream( - userAgent: "Tailcall/1.0" - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### allowedHeaders - -The `allowedHeaders` configuration defines a set of whitelisted HTTP headers that can be forwarded to upstream services during requests. -Without specifying `allowedHeaders`, the system will not forward any incoming headers to upstream services, offering an extra security layer but potentially limiting necessary data flow. Tailcall compares the provided whitelisted headers in a case-insensitive format. - -```graphql showLineNumbers -schema - @upstream( - allowedHeaders: ["Authorization", "X-Api-Key"] - ) { - query: Query - mutation: Mutation -} -``` - -In the example above, the configuration for `allowedHeaders` permits `Authorization` and `X-Api-Key` headers. Thus, requests with these headers will forward them to upstream services; the system ignores all others. This configuration ensures communication of the expected headers to dependent services, emphasizing security and consistency. - -### baseURL - -This refers to the default base URL for your APIs. If it's not explicitly mentioned in the `@upstream` directive, then each [`@http`](#http-directive) directive must specify its own `baseURL`. If neither `@upstream` nor [`@http`](#http-directive) provides a `baseURL`, it results in a compilation error. - -```graphql showLineNumbers -schema - @upstream( - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -In this representation, `http://jsonplaceholder.typicode.com` serves as the `baseURL`. Thus, all API calls made by `@http` prepend this URL to their respective paths. - -:::tip -Ensure that your base URL remains free from specific path segments. - -- **GOOD:** `@upstream(baseURL: http://jsonplaceholder.typicode.com)` -- **BAD:** `@upstream(baseURL: http://jsonplaceholder.typicode.com/api)` - -::: - -### httpCache - -When httpCache passed with value greater than 0 it directs Tailcall to use HTTP caching mechanisms, following the [HTTP Caching RFC](https://tools.ietf.org/html/rfc7234) to enhance performance by minimizing unnecessary data fetches. If left unspecified, this feature defaults to `0` disabling the caching mechanism. - -```graphql showLineNumbers -schema @upstream(httpCache: 42) { - query: Query - mutation: Mutation -} -``` - -### Tips - -- Use batching when other optimization techniques fail to resolve performance issues. -- Apply batching and thoroughly assess its impact. -- Understand that batching may make debugging more challenging. - -### batch - -An object that specifies the batch settings, including `maxSize` (the max size of the batch), `delay` (the delay in milliseconds between each batch), and `headers` (an array of HTTP headers that the batch will include). - -```graphql showLineNumbers -schema - @upstream( - batch: { - maxSize: 1000 - delay: 10 - headers: ["X-Server", "Authorization"] - } - ) { - query: Query - mutation: Mutation -} -``` - -### onRequest - -Similar to the [@http](#http-directive) property, this accepts a string value representing a middleware function defined in a JavaScript file. It intercepts all outgoing HTTP requests from the server. This interceptor, written in JavaScript, can be used to modify outgoing requests and also generate artificial responses to customize the behavior of the GraphQL server. - -```graphql showLineNumbers -schema @upstream(onRequest: 'someFunctionName') -@link(type: Script, src: "path_to/worker.js") { - query: Query - mutation: Mutation -} -``` +| Operator | Description | +| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | +| [`@addField`](/docs/directives/add-field-directive/) | Simplifies data structures and queries by adding, inlining, or flattening fields or nodes within the schema. | +| [`@cache`](/docs/directives/cache-directive/) | Enables caching for the query, field or type applied to. | +| [`@call`](/docs/directives/call-directive/) | Invokes a query or mutation from another query or mutation field. | +| [`@expr`](/docs/directives/expr-directive/) | Allows embedding of a constant response within the schema. | +| [`@graphQL`](/docs/directives/graphql-directive/) | Resolves a field or node by a GraphQL API. | +| [`@grpc`](/docs/directives/grpc-directive/) | Resolves a field or node by a gRPC API. | +| [`@http`](/docs/directives/http-directive/) | Resolves a field or node by a REST API. | +| [`@link`](/docs/directives/link-directive/) | Imports external resources such as config files, certs, protobufs, etc in the schema. | +| [`@modify`](/docs/directives/modify-directive/) | Enables changes to attributes of fields or nodes in the schema. | +| [`@omit`](/docs/directives/omit-directive/) | Excludes fields or nodes from the generated schema, making them inaccessible through the GraphQL API. | +| [`@rest`](/docs/directives/rest-directive/) | Allows exposing REST endpoints on top of GraphQL. | +| [`@server`](/docs/directives/server-directive/) | Provides server configurations for behavior tuning and tailcall optimization in specific use-cases. | +| [`@telemetry`](/docs/directives/telemetry-directive/) | Integrates with open-telemetry to provide observability of the running tailcall service. | +| [`@upstream`](/docs/directives/upstream-directive/) | Controls aspects of the upstream server connection, including timeouts and keep-alive settings. | diff --git a/docs/directives/add-field.md b/docs/directives/add-field.md new file mode 100644 index 0000000000..83beec14c9 --- /dev/null +++ b/docs/directives/add-field.md @@ -0,0 +1,94 @@ +--- +title: Adding Custom Fields with the @addField Directive +description: Simplify your GraphQL schema using the @addField directive to flatten nested fields and streamline data queries for better performance. +slug: add-field-directive +sidebar_label: Adding Custom Fields +--- + +The `@addField` directive simplifies data structures and queries by adding a field that _inline_ or flattens a nested field or node within your schema. It modifies the schema and the data transformation process, making nested data more accessible and straightforward to present. + +For instance, consider a schema: + +```graphql showLineNumbers +schema { + query: Query +} + +type User + @addField(name: "street", path: ["address", "street"]) { + id: Int! + name: String! + username: String! + email: String! + phone: String + website: String + address: Address @modify(omit: true) +} + +type Address { + street: String! + city: String! + state: String! +} + +type Query { + user(id: Int!): User @http(path: "/users/{{.args.id}}") +} +``` + +Suppose we focus on the `street` field in `Address`. + +In this case, applying the `@addField` directive to the `User` type creates a `street` field within the `User` type. It uses a `path` argument to specify the sequence of fields from a declared field (`address`), leading to the `Address` field to add. We also can apply `@modify(omit: true)` to remove the `address` field from the schema, as the `street` field from `Address` is now directly accessible on the `User` type. + +Post application, the schema becomes: + +```graphql showLineNumbers +schema { + query: Query +} + +type User { + id: Int! + name: String! + username: String! + email: String! + phone: String + website: String + street: String +} + +type Query { + user(id: Int): Post! +} +``` + +In the above example, since we added a `@modify(omit: true)` on the `address` field, the schema no longer includes the `Address` type. + +The `@addField` directive also take cares of nullablity of the fields. If any of the fields in the path is nullable, the resulting type will be nullable. + +`@addField` also supports indexing, allowing for the specification of an array index for inline inclusion. For instance, if a field `posts` is of type `[Post]`, and the goal is to access the title of the first post, specify the path as [`"posts"`,`"0"`,`"title"`]. + +```graphql showLineNumbers +type User + @addField( + name: "firstPostTitle" + path: ["posts", "0", "title"] + ) { + id: Int! + name: String! + username: String! + email: String! + phone: String + website: String + posts: Post @http(path: "/users/{{.value.id}}/posts") +} + +type Post { + id: Int! + userId: Int! + title: String! + body: String! +} +``` + +In conclusion, the `@addField` directive helps tidy up your schema and streamline data fetching by reducing query depth, promoting better performance and simplicity. diff --git a/docs/directives/cache.md b/docs/directives/cache.md new file mode 100644 index 0000000000..9dfc7c1d99 --- /dev/null +++ b/docs/directives/cache.md @@ -0,0 +1,141 @@ +--- +title: Optimizing GraphQL with the @cache Directive +description: Enhance GraphQL performance by caching field results using the @cache directive, specifying caching durations and optimizing data fetching. +slug: cache-directive +sidebar_label: Caching Fields +--- + +The `@cache` directive provides a protocol agnostic mechanism for caching the results of fields within a GraphQL schema. Like any other cache implementation, this feature is useful for optimizing performance by reducing the need to fetch data that doesn't change frequently. + +### maxAge + +```graphql +@cache(maxAge: Int) +``` + +This parameter is a non-zero unsigned integer specifying the duration, in milliseconds, that retains the cached value. + +### Usage + +Consider the following GraphQL schema example: + +```graphql +type Query { + posts: [Post] @http(path: "/posts") +} + +type Post { + id: Int + title: String + userId: Int @cache(maxAge: 100) + user: User + @http(path: "/user/{{.value.userId}}") + @cache(maxAge: 200) +} + +type User { + id: Int + name: String + email: String +} +``` + +In this configuration, the system caches the result of the `user` field due to its association with an HTTP resolver. But it does not cache the values of `userId` and `title` because they lack individual resolvers; the resolver for the `posts` field retrieves their values, employing the `@http(path: "/posts")` directive. + +Applying the `@cache` directive at the type level affects all fields within that type. For example: + +```graphql +type Query { + posts: [Post] @http(path: "/posts") +} + +type Post @cache(maxAge: 100) { + id: Int + title: String + userId: Int + user: User @http(path: "/user/{{.value.userId}}") +} + +type User { + id: Int + name: String + email: String +} +``` + +You can simplify this configuration to show that applying the `@cache` directive to a type means every field within that type inherits it: + +```graphql +type Query { + posts: [Post] @http(path: "/posts") +} + +type Post { + id: Int @cache(maxAge: 100) + title: String @cache(maxAge: 100) + userId: Int @cache(maxAge: 100) + user: User + @http(path: "/user/{{.value.userId}}") + @cache(maxAge: 100) +} + +type User { + id: Int + name: String + email: String +} +``` + +Since the `@cache` directive does not affect fields without resolvers, the effective configuration can be further reduced as follows: + +```graphql +type Query { + posts: [Post] @http(path: "/posts") +} + +type Post { + id: Int + title: String + userId: Int + user: User + @http(path: "/user/{{.value.userId}}") + @cache(maxAge: 100) +} + +type User { + id: Int + name: String + email: String +} +``` + +When applying the `@cache` directive both at the type level and on individual fields within that type, the field-level directive takes precedence: + +```graphql +type Query { + posts: [Post] @http(path: "/posts") +} + +type Post @cache(maxAge: 200) { + id: Int + title: String + userId: Int + user: User + @http(path: "/user/{{.value.userId}}") + @cache(maxAge: 100) +} + +type User { + id: Int + name: String + email: String +} +``` + +Thus, in the configuration above, while all fields inherit the `@cache(maxAge: 200)` directive at the type level, the `user` field's explicit `@cache(maxAge: 100)` directive takes precedence. + +### Cache Key + +The caching mechanism generates a hash based on information related to the applied query to serve as the cache key for the corresponding value. + +For instance, the system caches the `user` field in the following configuration, using the hash of the interpolated string `"/user/{{.value.userId}}"` as the cache key. For example, if `Post.userId` equals `1`, the system generates the cache key by hashing the string `"/users/1"`. diff --git a/docs/directives/call.md b/docs/directives/call.md new file mode 100644 index 0000000000..4486cf2311 --- /dev/null +++ b/docs/directives/call.md @@ -0,0 +1,240 @@ +--- +title: Efficient Schema Refactoring with the @call Directive in GraphQL +description: Leverage the @call directive to reduce redundancy in GraphQL schemas by composing queries and mutations, streamlining operations and enhancing code maintainability. +slug: call-directive +sidebar_label: Function Invocations +--- + +The `@call` directive in GraphQL signifies a shift towards more efficient configuration management by introducing a methodology akin to function invocations in conventional programming. This directive is pivotal for developers navigating the intricacies of elaborate GraphQL schemas, where minimizing redundancy and adhering to the DRY (Don't Repeat Yourself) principle are paramount. Consider the following schema example: + +```graphql showLineNumbers +schema + @upstream( + baseURL: "https://jsonplaceholder.typicode.com" + ) { + query: Query +} + +type Query { + # highlight-start + user(id: Int!): User @http(path: "/users/{{.args.id}}") + # highlight-end + posts: [Post] @http(path: "/posts") +} + +type Post { + id: Int! + userId: Int! + title: String! + body: String! + # highlight-start + user: User @http(path: "/users/{{.value.userId}}") + # highlight-end +} + +type User { + id: Int! + name: String! + email: String! +} +``` + +In this schema, at lines `9` and `18`, a pattern of configuration duplication emerges when fetching user's data by its id, demonstrating a prime use case for the `@call` directive. Through refactoring the `Post` type to incorporate the `@call` directive, we can eliminate this redundancy. + +```graphql showLineNumbers +type Post { + id: Int! + userId: Int! + title: String! + body: String! + # highlight-start + user: User + @call( + steps: [ + {query: "user", args: {id: "{{.value.userId}}"}} + ] + ) + # highlight-end +} +``` + +Here, the `@call` directive invokes the `user` query from the `Query` type, leveraging the data-fetching process that's already defined in the root `query`. The `query` parameter specifies the target field, while the `args` parameter delineates the arguments to be passed. + +### steps + +`@call` directive can compose together other resolvers, allowing to create a chain of resolvers that can be executed in sequence. This is done by using the `steps` parameter, which is an array of objects that define the operations to be executed. + +### query + +Specify the root **query** field to invoke, alongside the requisite arguments, using the `@call` directive for a concise and efficient query structure. + +```graphql showLineNumbers +type Post { + userId: Int! + user: User + @call( + steps: [ + {query: "user", args: {id: "{{.value.userId}}"}} + ] + ) +} +``` + +### mutation + +Similarly, the `@call` directive can facilitate calling a mutation from another mutation field, employing the `mutation` parameter for field specification and the `args` parameter for argument delineation. + +```graphql showLineNumbers +type Mutation { + insertPost(input: PostInput, overwrite: Boolean): Post + @http( + body: "{{.args.input}}" + method: "POST" + path: "/posts" + query: {overwrite: "{{.args.overwrite}}"} + ) + + upsertPost(input: PostInput): Post + @call( + steps: [ + { + mutation: "insertPost" + args: {input: "{{.args.input}}", overwrite: true} + } + ] + ) +} +``` + +### args + +The `args` parameter in the `@call` directive facilitates passing arguments to the targeted query or mutation, represented as a key-value mapping where each key corresponds to an argument name and its associated value. + +```graphql showLineNumbers +type Post { + userId: Int! + user: User + @call( + steps: [ + {query: "user", args: {id: "{{.value.userId}}"}} + ] + ) +} +``` + +:::tip +The `@call` directive is predominantly advantageous in complex, large-scale configurations. For those new to GraphQL or Tailcall, it may be beneficial to explore this directive after familiarizing yourself with the foundational aspects of GraphQL. +::: + +### Composition + +`@call` directive provides the ability to express a sequence of steps that one might need to compose. These steps are executed such that the result of each step is passed as an argument to the next step. The `query` and `mutation` parameters are used to specify the target field, while the `args` parameter is used to pass arguments to the target field. + +Let's explain this with an example: + +```graphql showLineNumbers +schema @server { + query: Query +} + +type Query { + a(input: JSON): JSON + @expr(body: {value: "{{.args.input.a}}"}) + + b(input: JSON): JSON + @expr(body: {value: "{{.args.input.b}}"}) + + c(input: JSON): JSON + @expr(body: {value: "{{.args.input.c}}"}) +} +``` + +Here we have defined there operations viz. `a`, `b` & `c` each of them pluck their respective keys from the given input value. Let's run this query with some test input: + +```graphql +{ + a(input: {a: 100}) + b(input: {b: 200}) + c(input: {c: 300}) +} +``` + +Here is how the response would look like: + +```json +{ + "data": { + "a": { + "value": 100 + }, + "b": { + "value": 200 + }, + "c": { + "value": 300 + } + } +} +``` + +As you can see the [`@expr`](#expr-directive) directive plucks the inner value and returns the result. How about we implement an `abc` operation that could leverage the existing operations and unwrap the following input value: + +```json +{"a": {"b": {"c": {"d": 1000}}}} +``` + +Given the above input if we wish to extract the last inner number `1000` then we could define a new operation as follows + +```graphql showLineNumbers +schema @server { + query: Query +} + +type Query { + a(input: JSON): JSON + @expr(body: {value: "{{.args.input.a}}"}) + + b(input: JSON): JSON + @expr(body: {value: "{{.args.input.b}}"}) + + c(input: JSON): JSON + @expr(body: {value: "{{.args.input.c}}"}) + + # highlight-start + abc(input: JSON): JSON + @call( + steps: [ + {query: "a", args: {input: "{{.args.input}}"}} + {query: "b", args: {input: "{{.args.value}}"}} + {query: "c", args: {input: "{{.args.value}}"}} + ] + ) + # highlight-end +} +``` + +We use the `@call` directive to compose the operations together. The `args` specify how we would like to pass the arguments to the operation and the result of that operation is passed to the next step. We can test the new `abc` operation with the following query: + +```graphql +query { + abc(input: {a: {b: {c: 1000}}}) +} +``` + +The server returns the response that we expected: + +```json +{ + "data": { + "abc": { + "value": 100 + } + } +} +``` + +This way you can compose combine multiple operations can compose them together using the `@call` directive. + +:::note +We use `JSON` scalar here because we don't care about the type safety of this option. In a real world example you might want to use proper input and output types. +::: diff --git a/docs/directives/expr.md b/docs/directives/expr.md new file mode 100644 index 0000000000..5785583584 --- /dev/null +++ b/docs/directives/expr.md @@ -0,0 +1,73 @@ +--- +title: Enhancing GraphQL Responses with the @expr Directive +description: Learn how to use the @expr directive in GraphQL for embedding data into your schema, providing flexibility and efficiency for custom responses. +slug: expr-directive +sidebar_label: Embedding Data +--- + +The `@expr` directive in GraphQL is a powerful tool for embedding data directly into your schema, offering two primary functionalities: + +### Static + +This feature allows for the inclusion of a constant response within the schema definition itself. It is useful for scenarios where the response is unchanging. e.g: + +```graphql +schema { + query: Query +} + +type Query { + user: User @expr(body: {name: "John", age: 12}) +} + +type User { + name: String + age: Int +} +``` + +The `@expr` directive also checks the provided value at compile time to ensure it matches the field's schema. If not, the console displays a descriptive error message. + +### Dynamic + +Beyond static data embedding, the `@expr` directive extends its utility to support dynamic data injection through Mustache template syntax. This feature enables the use of placeholders within the constant data, which are then dynamically replaced with actual values at runtime. It supports both scalar values and complex objects, including lists and nested objects, offering flexibility in tailoring responses to specific needs. e.g: + +```graphql +schema { + query: Query +} + +type Query { + user: User + @expr( + body: { + name: "John" + workEmail: "john@xyz.com" + personalEmail: "john@xyz.com" + } + ) +} + +type User { + name: String + age: Int + personalEmail: String + workEmail: String + emails: Emails + @expr( + body: { + emails: { + workEmail: "{{.value.workEmail}}" + personalEmail: "{{.value.personalEmail}}" + } + } + ) +} + +type Emails { + workEmail: String + personalEmail: String +} +``` + +In this example, the `@expr` directive dynamically generate an `Emails` object based on the provided template data. The placeholders within the template (`{{.value.workEmail}}` and `{{.value.personalEmail}}`) gets replaced with the actual values specified in the `User` type, allowing for dynamic content generation while still adhering to the schema's structure. diff --git a/docs/directives/graphql.md b/docs/directives/graphql.md new file mode 100644 index 0000000000..df8a38151a --- /dev/null +++ b/docs/directives/graphql.md @@ -0,0 +1,131 @@ +--- +title: Efficient Data Fetching with GraphQL Directive +description: Streamline data retrieval from upstream GraphQL APIs using the @graphQL directive for enhanced schema efficiency. +slug: graphQL-directive +sidebar_label: GraphQL API Fetching +--- + +The `@graphQL` directive allows to specify GraphQL API server request to fetch data from. + +```graphql showLineNumbers +type Query { + users: [User] @graphQL(name: "userList") +} +``` + +The `@graphQL` directive facilitates fetching a list of users from the GraphQL API upstream. The [name](#name) argument specifies the root field's name on the upstream server. The upcoming request to the GraphQL server determines the `User` type's inner fields for the request. Depending on the operation type within which one finds the `@graphQL` directive, the GraphQL configuration determines the query's operation type. + +For the next request with the config above: + +```graphql showLineNumbers +query { + users { + id + name + } +} +``` + +Tailcall will request the next query for the upstream: + +```graphql showLineNumbers +query { + userList { + id + name + } +} +``` + +### baseURL + +This refers to the base URL of the API. If not specified, the default base URL is the one specified in the [`@upstream`](#upstream-directive) directive. + +```graphql showLineNumbers +type Query { + users: [User] + @graphQL( + name: "users" + baseURL: "https://graphqlzero.almansi.me/api" + ) +} +``` + +### name + +The root field's name on the upstream to request data from. For example: + +```graphql showLineNumbers +type Query { + users: [User] @graphQL(name: "userList") +} +``` + +When Tailcall receives a query for the `users` field, it will request a query for `userList` from the upstream. + +### args + +Named arguments for the requested field. For example: + +```graphql showLineNumbers +type Query { + user: User + @graphQL( + name: "user" + args: [{key: "id", value: "{{.value.userId}}"}] + ) +} +``` + +Will request the next query from the upstream for the first user's name: + +```graphql showLineNumbers +query { + user(id: 1) { + name + } +} +``` + +### headers + +The `headers` parameter allows customizing the headers of the GraphQL request made by the `@graphQL` directive. Specifying a key-value map of header names and their values achieves this. + +For instance: + +```graphql showLineNumbers +type Mutation { + users: User + @graphQL( + name: "users" + headers: [{key: "X-Server", value: "Tailcall"}] + ) +} +``` + +In this example, a request to `/users` will include the HTTP header `X-Server` with the value `Tailcall`. + +### batch + +In case the upstream GraphQL server supports request batching, we can specify the `batch` argument to batch requests to a single upstream into a single batch request. For example: + +```graphql showLineNumbers +schema + @upstream( + batch: { + maxSize: 1000 + delay: 10 + headers: ["X-Server", "Authorization"] + } + ) { + query: Query + mutation: Mutation +} + +type Query { + users: [User] @graphQL(name: "users", batch: true) + posts: [Post] @graphQL(name: "posts", batch: true) +} +``` + +Make sure you have also specified batch settings to the `@upstream` and to the `@graphQL` directive. diff --git a/docs/directives/grpc.md b/docs/directives/grpc.md new file mode 100644 index 0000000000..464a029107 --- /dev/null +++ b/docs/directives/grpc.md @@ -0,0 +1,155 @@ +--- +title: Integrating gRPC Services +description: Explore how the @grpc directive facilitates the resolution of GraphQL fields using gRPC services, enabling efficient data fetching and seamless integration of gRPC in your GraphQL schema. +slug: grpc-directive +sidebar_label: gRPC Integration +--- + +The `@grpc` directive enables the resolution of GraphQL fields via gRPC services. Below is an illustrative example of how to apply this directive within a GraphQL schema: + +```graphql +schema @link(src: "./users.proto", type: Protobuf) { + query: Query +} + +type Query { + users: [User] @grpc(method: "users.UserService.ListUsers") +} +``` + +This schema snippet demonstrates the directive's application, where a query for `users` triggers a gRPC request to the `UserService`'s `ListUsers` method, thereby fetching the user data. + +The `.proto` file delineates the structure and methods of the gRPC service. A simplified example of such a file is as follows: + +```proto +syntax = "proto3"; + +package users; + +service UserService { + rpc ListUsers (UserListRequest) returns (UserListReply) {} + rpc GetUser (UserGetRequest) returns (UserGetReply) {} +} + +message UserListRequest { + // Definitions of request parameters +} + +message UserListReply { + // Structure of the reply +} + +message UserGetRequest { + // Definitions of request parameters +} + +message UserGetReply { + // Structure of the reply +} +``` + +:::important +It is mandatory to have a package name in a protobuf file. +::: + +Linking this file within a GraphQL schema is facilitated by the `@link` directive, as shown below: + +```graphql +schema @link(src: "./users.proto", type: Protobuf) { + query: Query +} +``` + +Tailcall automatically resolves the protobuf file for any methods referenced in the `@grpc` directive. + +### method + +This parameter specifies the gRPC service and method to be invoked, formatted as `..`: + +```graphql +type Query { + users: [User] + @grpc(method: "proto.users.UserService.ListUsers") +} +``` + +### baseURL + +Defines the base URL for the gRPC API. If not specified, the URL set in the `@upstream` directive is used by default: + +```graphql +type Query { + users: [User] + @grpc( + baseURL: "https://grpc-server.example.com" + method: "proto.users.UserService.ListUsers" + ) +} +``` + +### body + +This parameter outlines the arguments for the gRPC call, allowing for both static and dynamic inputs: + +```graphql +type UserInput { + id: ID +} + +type Query { + user(id: UserInput!): User + @grpc( + body: "{{.args.id}}" + method: "proto.users.UserService.GetUser" + ) +} +``` + +### headers + +Custom headers for the gRPC request can be defined, facilitating the transmission of authentication tokens or other contextual data: + +```graphql +type Query { + users: [User] + @grpc( + headers: [ + {key: "X-CUSTOM-HEADER", value: "custom-value"} + ] + method: "proto.users.UserService.ListUsers" + ) +} +``` + +### batchKey + +This argument is employed to optimize batch requests by grouping them based on specified response keys, enhancing performance in scenarios requiring multiple, similar requests: + +```graphql +type Query { + users(id: UserInput!): [User] + @grpc( + batchKey: ["id"] + method: "proto.users.UserService.ListUsers" + baseURL: "https://grpc-server.example.com" + ) +} +``` + +:::info +Read about [n + 1](./N+1.md) to learn how to use the `batchKey` setting. +::: + +### onResponseBody + +This hook allows you to intercept and modify the response body from upstream services before it's processed by Tailcall. Like [onRequest](/docs/directives.md#onrequest), it accepts a string value representing a middleware function defined in a JavaScript file. This function can be used to transform or validate the response data. + +```graphql showLineNumbers +type Query { + news: NewsData! + @grpc( + method: "news.NewsService.GetAllNews" + onResponseBody: "onResponse" + ) +} +``` diff --git a/docs/directives/http.md b/docs/directives/http.md new file mode 100644 index 0000000000..8fbde28f15 --- /dev/null +++ b/docs/directives/http.md @@ -0,0 +1,199 @@ +--- +title: Using @http Directive +description: Learn how to leverage the @http directive in GraphQL to interact with REST APIs, including customizable request methods, dynamic paths, and query parameters for efficient data fetching. +slug: http-directive +sidebar_label: HTTP Directive +--- + +The `@http` directive indicates a field or node relies on a REST API. For example: + +```graphql showLineNumbers +type Query { + users: [User] @http(path: "/users") +} +``` + +In this example, adding the `@http` directive to the `users` field of the `Query` type indicates reliance on a REST API for the `users` field. The [path](#path) argument specifies the REST API's path, which is `/users` in this scenario.Querying the `users` field prompts the GraphQL server to issue a GET request to `https://jsonplaceholder.typicode.com/users`. + +### baseURL + +Specifies the API's base URL. If unspecified, it defaults to the URL in the [`@upstream`](#upstream-directive) directive. + +```graphql showLineNumbers +type Query { + users: [User] + @http( + path: "/users" + baseURL: "https://jsonplaceholder.typicode.com" + ) +} +``` + +### path + +Refers to the API endpoint, for example, `https://jsonplaceholder.typicode.com/users`. + +```graphql showLineNumbers +type Query { + users: [User] @http(path: "/users") +} +``` + +If your API endpoint contains dynamic segments, you can substitute variables using Mustache templates. For example, to fetch a specific user, you can write the path as `/users/{{.args.id}}`. + +```graphql showLineNumbers +type Query { + user(id: ID!): User @http(path: "/users/{{.args.id}}") +} +``` + +### method + +Specifies the HTTP method for the API call. The default method is GET if not specified. + +```graphql showLineNumbers +type Mutation { + createUser(input: UserInput!): User + @http(method: "POST", path: "/users") +} +``` + +### query + +Represents the API call's query parameters, either as a static object or with dynamic parameters using Mustache templates. These parameters append to the URL. + +```graphql showLineNumbers +type Query { + userPosts(id: ID!): [Post] + @http( + path: "/posts" + query: [ + { + key: "userId" + value: "{{.args.id}}" + skipEmpty: false + } + ] + ) +} +``` + +The `query` field and be further configured using the following fields: + +1. **key** : Represents the name of the query parameter. +2. **value** : A string literal or a mustache template representing the value of query parameter. +3. **skipEmpty** : When set to `true` the query parameter is skipped if the value of the parameter is null, defaults to false. + +:::important +When `batchKey` is present, Tailcall considers the first `query` parameter to be the batch query key, so remember to adjust the order of the items accordingly. +::: + +### body + +Defines the API call's body, necessary for methods like POST or PUT. Pass it as a static object or use Mustache templates for variable substitution from the GraphQL variables. + +```graphql showLineNumbers +type Mutation { + createUser(input: UserInput!): User + @http( + method: "POST" + path: "/users" + body: "{{.args.input}}" + ) +} +``` + +In the example above, the `createUser` mutation sends a POST request to `/users`, with the input object converted to JSON and included in the request body. + +### headers + +Customizes the HTTP request headers made by the `@http` directive. Specify a key-value map of header names and values. + +For instance: + +```graphql showLineNumbers +type Mutation { + createUser(input: UserInput!): User + @http( + path: "/users" + headers: [{key: "X-Server", value: "Tailcall"}] + ) +} +``` + +In this example, a request to `/users` will include a HTTP header `X-Server` with the value `Tailcall`. + +You can make use of mustache templates to provide dynamic values for headers, derived from the arguments or [context] provided in the request. For example: + +[context]: /docs/graphql-resolver-context-tailcall + +```graphql showLineNumbers +type Mutation { + users(name: String): User + @http( + path: "/users" + headers: [ + {key: "X-Server", value: "Tailcall"} + {key: "User-Name", value: "{{.args.name}}"} + ] + ) +} +``` + +In this scenario, the `User-Name` header's value will dynamically adjust according to the `name` argument passed in the request. + +### batchKey + +Groups data requests into a single call, enhancing efficiency. Refer to our [n + 1 guide](./N+1.md) for more details. + +:::important +When `batchKey` is present, Tailcall considers the first `query` parameter to be the batch query key, so remember to adjust the order of the items accordingly. Whereas, the last item from `batchKey` is used to instruct which field is the ID of an object. In case that the returned result is a nested property `batchKey` can be used as a path to extract and group the items for the returned result. +::: + +```graphql showLineNumbers +type Post { + id: Int! + name: String! + user: User + @http( + path: "/users" + query: [{key: "user_id", value: "{{.value.userId}}"}] + batchKey: ["users", "id"] + ) +} +``` + +- `query: {key: "user_id", value: "{{.value.userId}}"}]`: Instructs Tailcall CLI to generate a URL aligning the user id with `userId` from the parent `Post`, compiling a single URL for a batch of posts, such as `/users?user_id=1&user_id=2&user_id=3...user_id=10`, consolidating requests into one. + +### onRequest + +The `onRequest` property accepts a string value representing the remote function to be called every time an HTTP request is initiated. Typically the remote function is defined in a linked JavaScript worker file. + +:::note +For defining a request middleware globally for all requests, refer to the [upstream directive documentation](/docs/directives.md#onrequest-1). +::: + +```graphql showLineNumbers +type Query { + userPosts(id: ID!): [Post] + @http( + path: "/posts" + query: [{key: "userId", value: "{{.args.id}}"}] + onRequest: "someFunctionName" + ) +} +``` + +### onResponseBody + +This hook allows you to intercept and modify the response body from upstream services before it's processed by Tailcall. Like [onRequest](/docs/directives.md#onrequest), it accepts a string value representing a middleware function defined in a JavaScript file. This function can be used to transform or validate the response data. + +```graphql showLineNumbers +type Query { + user(id: Int!): User + @http( + path: "/users/{{.args.id}}" + onResponseBody: "onResponse" + ) +} +``` diff --git a/docs/directives/js.md b/docs/directives/js.md new file mode 100644 index 0000000000..0c440b108d --- /dev/null +++ b/docs/directives/js.md @@ -0,0 +1,77 @@ +--- +title: Custom Field Resolvers with @js Directive +description: Discover how to utilize the @js directive in GraphQL to incorporate JavaScript functions for custom data transformations and complex field resolutions, enhancing the flexibility of your API. +slug: js-directive +sidebar_label: JavaScript Field Resolution +--- + +The `@js` directive allows you to use JavaScript functions to resolve fields in your GraphQL schema. This can be useful +for custom data transformations or complex field resolutions. + +### Usage + +The `@js` directive is used to specify a JavaScript function that will resolve the value of a field. The directive takes +a single argument, `name`, which is the name of the JavaScript function to be used. + +### Syntax + +```graphql showLineNumbers +fieldName: FieldType @js(name: "functionName") +``` + +### Example + +Let's consider a `foo.js` file which contains a `resolve` function: + +```js +function resolve(val) { + let json = JSON.parse(val) + return JSON.stringify(json.id) +} +``` + +Here is an example of how the `@js` directive is used within a GraphQL schema: + +```gql showLineNumbers +schema + @link(type: Script, src: "./scripts/foo.js") + @server(port: 8000) + @upstream( + baseURL: "http://jsonplaceholder.typicode.com" + httpCache: true + ) { + query: Query +} + +type Query { + posts: [Post] @http(path: "/posts") +} + +type Post { + id: Int! + idx: Int! @js(name: "resolve") + userId: Int! + title: String! + body: String! +} +``` + +### Error Handling + +When using the `@js` directive, it is important to handle errors within your JavaScript functions. For example, you can use try-catch blocks to catch and handle any errors that occur during the resolution process. + +```javascript +function resolve(val) { + try { + let json = JSON.parse(val) + return JSON.stringify(json.id) + } catch (error) { + console.error("Error resolving value:", error) + throw new Error("Failed to resolve value") + } +} +``` + +### Performance Considerations + +When using the `@js` directive, keep in mind that JavaScript functions can introduce performance overhead, especially if they perform complex operations or are called frequently. To minimize performance impact, ensure that your functions are optimized and avoid unnecessary computations. diff --git a/docs/directives/link.md b/docs/directives/link.md new file mode 100644 index 0000000000..2fc5e5a93e --- /dev/null +++ b/docs/directives/link.md @@ -0,0 +1,123 @@ +--- +title: Integrating External Resources with Link Directive +description: Explore how to use the @link directive in GraphQL to integrate external resources like configurations, Protobuf files, and scripts into your schema, enhancing flexibility and modularity. +slug: link-directive +sidebar_label: Linking Resources +--- + +The `@link` directive is used for bringing external resources into your GraphQL schema. It makes it easier to include configurations, .proto files for gRPC services, and other files into your schema. With this directive, external resources are either merged with or used effectively in the importing configuration. + +### How it Works + +The `@link` directive requires specifying a source `src`, the resource's type `type`, and an optional identifier `id`. + +- `src`: The source of the link is defined here. It can be either a URL or a file path. When a file path is given, it's relative to the file's location that is importing the link. + +- `type`: This specifies the link's type, which determines how the imported resource is integrated into the schema. For a list of supported types, see the [Supported Types](#supported-types) section. + +- `id`: This is an optional field that assigns a unique identifier to the link. It's helpful for referring to the link within the schema. + +### Example + +The following example illustrates how to utilize the `@link` directive to incorporate a Protocol Buffers (.proto) file for a gRPC service into your GraphQL schema. + +```graphql showLineNumbers +schema + @server(port: 8000) + @upstream( + baseURL: "http://news.local" + httpCache: 42 + batch: {delay: 10} + ) + @link( + id: "news" + src: "./src/grpc/news.proto" + type: Protobuf + ) { + query: Query +} + +type Query { + news: NewsData! + @grpc(method: "news.NewsService.GetAllNews") +} + +type News { + id: Int + title: String + body: String + postImage: String +} + +type NewsData { + news: [News]! +} +``` + +### Supported Types + +The `@link` directive enriches your configuration by supporting the integration of external resources. Each link type is designed to serve a specific purpose, enhancing the functionality and flexibility of your schema. Below is a detailed overview of each supported link type: + +### Config + +The `Config` link type is essential for importing other configuration files. This feature enables a modular approach to schema management by allowing configurations from the imported file to override overlapping settings in the main schema. This functionality is useful in large projects, where maintaining a single monolithic schema file becomes impractical. By using `Config`, developers can split their schema configurations into manageable pieces, thus promoting better organization and scalability. + +Example use case: + +- Modularizing schema configurations for different environments (development, staging, production). +- Reusing common configurations across multiple schema files. + +### Protobuf + +The `Protobuf` link type integrates Protocol Buffers definitions by importing .proto files. This integration is crucial for Tailcall to communicate with gRPC services. By including `.proto` definitions, the GraphQL server can directly interact with gRPC services, allowing for efficient and type-safe communication. + +For detailed integration steps and best practices, refer to the [gRPC Integration Guide](/docs/grpc.md). + +### Script + +The `Script` link type allows the config to link to an external JavaScript file. This file can contain custom logic that is executed in response to HTTP request-response events. This feature enables developers to implement custom behaviors, such as adding headers to responses or filtering requests based on specific criteria. + +Example script for adding a custom header to all outgoing requests: + +```javascript showLineNumbers +function onRequest({request}) { + // Add a custom header for all outgoing requests + request.headers["X-Custom-Header"] = "Processed" + + // Return the updated request + return {request} +} +``` + +### Cert + +The `Cert` link type is designed for importing SSL/TLS certificates, a crucial component for enabling HTTPS in your GraphQL server. This link type ensures that the server can expose connections over HTTPS. + +:::tip +When using the `Cert` link type, specify the path to the certificate file. Ensure the certificate is up-to-date and issued by a trusted certificate authority (CA) to avoid security warnings or connection issues. +::: + +Example use case: + +- Securing communication between the GraphQL server and clients. +- Enhancing privacy and security by encrypting data in transit. + +### Key + +The `Key` link type imports the private key associated with your SSL/TLS certificate, enabling HTTPS for your GraphQL server. The private key is a critical security element that decrypts information encrypted by the corresponding public key in the SSL/TLS certificate. + +When configuring the `Key` link type, provide the path to your private key file. Ensure the private key matches the imported certificate specified by the [Cert](#cert) link above, and is protected by appropriate file permissions to maintain security. + +### Operation + +The `Operation` link type connects your schema to a set of predefined, GraphQL spec-compliant queries and mutations. This functionality allows for the validation and optimization of these operations by the GraphQL server. + +Each type serves a specific purpose, enabling the flexible integration of external resources into your GraphQL schema. + +### Htpasswd + +The `Htpasswd` link type allows the importation of an [`htpasswd`](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) file. This file is utilized to set up [Basic authentication](./auth.md#basic-authentication). + +### Jwks + +The `Jwks` link type enables the importation of a [`JWKS`](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-key-sets) file. This file facilitates the provision of detailed access control through [JWT authentication](./auth.md#jwt-authentication). diff --git a/docs/directives/modify.md b/docs/directives/modify.md new file mode 100644 index 0000000000..31a9bad926 --- /dev/null +++ b/docs/directives/modify.md @@ -0,0 +1,36 @@ +--- +title: Altering Schema Attributes with Modify Directive +description: Learn how to use the @modify directive in GraphQL to alter the fields in your schema, allowing for better alignment between your data source and GraphQL API design. +slug: modify-directive +sidebar_label: Modifying Fields +--- + +The `@modify` directive in GraphQL provides the flexibility to alter the attributes of a field or a node within your GraphQL schema. Here's how you can use this directive: + +### name + +You can rename a field or a node in your GraphQL schema using the `name` argument in the `@modify` directive. This can be helpful when the field name in your underlying data source doesn't match the desired field name in your schema. For instance: + +```graphql showLineNumbers +type User { + id: Int! @modify(name: "userId") +} +``` + +`@modify(name: "userId")` informs GraphQL to present the field known as `id` in the underlying data source as `userId` in your schema. + +### omit + +You can exclude a field or a node from your GraphQL schema using the `omit` argument in the `@modify` directive. This can be useful if you want to keep certain data hidden from the client. For instance: + +```graphql showLineNumbers +type User { + id: Int! @modify(omit: true) +} +``` + +`@modify(omit: true)` instructs GraphQL to exclude the `id` field from the schema, making it inaccessible to the client. + +:::tip +`@omit` is a standalone directive and is an alias/shorthand for `modify(omit: true)` checkout [documentation](/docs/directives.md#omit-directive) +::: diff --git a/docs/directives/omit.md b/docs/directives/omit.md new file mode 100644 index 0000000000..94eda1ca3c --- /dev/null +++ b/docs/directives/omit.md @@ -0,0 +1,38 @@ +--- +title: Excluding Fields with Omit Directive +description: Utilize the @omit directive in GraphQL to exclude specific fields from your schema, enhancing API security and simplifying client interactions by hiding unnecessary data. +slug: omit-directive +sidebar_label: Omitting Fields +--- + +Within a GraphQL schema, the `@omit` directive excludes fields or nodes from the generated schema, making them inaccessible through the GraphQL API. This directive is useful for hiding sensitive information or simplifying your API by removing unnecessary fields. + +### How it works + +When applied to a field or node, the `@omit` directive instructs the Tailcall not to include that field or node in the schema. This means that clients cannot query or mutate data in those fields. + +### Example + +Consider a scenario where you have a `User` type with an embedded `Address` type. If you want to exclude the `Address` type from the schema to simplify the API, you can use the `@omit` directive: + +```graphql showLineNumbers +type Address { + city: String + street: String +} + +type User { + name: String + address: Address @omit +} +``` + +In this example, the `address` field will not be accessible or visible through the GraphQL API. + +### Comparison with `modify` + +The `@omit` directive and `@modify(omit: true)` essentially serve the same purpose in excluding fields from the schema, but they differ in syntax and flexibility. In fact, one can consider `@omit` as a shorthand or alias for the more verbose `@modify(omit: true)`. + +- `@omit` offers a concise way to directly exclude a field or node without additional arguments. + +- `@modify(omit: true)`, as part of the broader [`@modify`](/docs/directives.md#omit-directive) directive, provides more options, such as field renaming through the `name` argument. This makes it a more flexible choice when you need more than field exclusion. diff --git a/docs/directives/protected.md b/docs/directives/protected.md new file mode 100644 index 0000000000..2fe5ce9525 --- /dev/null +++ b/docs/directives/protected.md @@ -0,0 +1,29 @@ +--- +title: Enforcing Authentication with Protected Directive +description: The @protected directive restricts access to specific fields or types in your GraphQL schema, ensuring that only authenticated users can retrieve sensitive data. +slug: protected-directive +sidebar_label: Protected Fields +--- + +The `@protected` annotation designates a type or field as protected, meaning that a user must be authenticated to access that data. + +```graphql +type Query { + protected: String! @protected + protectedType: ProtectedType +} + +type ProtectedType @protected { + name: String! + nested: String! +} +``` + +:::important +To utilize the `@protected` directive, you must link at least one authentication provider in the configuration using the [`@link`](#link-directive) directive (`Htpasswd` or `Jwks`). +::: + +### How It Works + +- When a field is annotated with `@protected`, an authentication check is performed upon receiving the request. Depending on the authentication result, either the requested data is provided in the response, or an authentication error is returned. +- If a type is annotated with `@protected`, all fields within that type inherit the protection, requiring user authentication for any field that's queried. diff --git a/docs/directives/rest.md b/docs/directives/rest.md new file mode 100644 index 0000000000..26b71e7a70 --- /dev/null +++ b/docs/directives/rest.md @@ -0,0 +1,54 @@ +--- +title: Integrating REST APIs +description: The @rest directive enables GraphQL to orchestrate RESTful services by mapping queries to REST endpoints, facilitating seamless integration with existing API infrastructures. +slug: rest-directive +sidebar_label: REST Integration +--- + +API orchestration is essential, yet not all can adopt GraphQL despite its benefits. The Tailcall DSL feature leverages GraphQL at compile time to generate REST endpoints, aligning with traditional API infrastructure like CDNs and Gateways. + +### Usage + +- **method**: Specifies the HTTP method (GET, POST, etc.). +- **path**: Sets the endpoint URL, with support for dynamic values from query arguments. +- **query**: Defines the query parameters as key-value pairs. + +### Example + +Define GraphQL types and queries, using the `@rest` directive to map fields to REST API endpoints. + +`schema.graphql` + +```graphql +schema + @upstream(baseURL: "https://jsonplaceholder.typicode.com") + @link(type: Operation, src: "user-operation.graphql") { + query: Query +} + +type Query { + user(id: Int!): User + @rest(method: "GET", path: "/users/{{.args.id}}") +} + +type User { + id: Int! + name: String! + email: String! +} +``` + +`user-operation.graphql` + +```graphql +query ($id: Int!) @rest(method: GET, path: "/user/$id") { + user(id: $id) { + id + name + } +} +``` + +![REST Demo](/images/docs/rest-user.png) + +This example demonstrates how to define a simple query to fetch user data from a REST endpoint using the `@rest` directive. By leveraging `@rest`, GraphQL can serve as a layer over RESTful services, combining REST's simplicity with GraphQL's flexibility. diff --git a/docs/directives/server.md b/docs/directives/server.md new file mode 100644 index 0000000000..7c06b02f57 --- /dev/null +++ b/docs/directives/server.md @@ -0,0 +1,350 @@ +--- +title: Configuring Server Settings with @server +description: The @server directive allows comprehensive server configurations, influencing behavior and performance. Tailor settings such as worker threads, ports, CORS, and more to optimize your GraphQL API. +slug: server-directive +sidebar_label: Server Configuration +--- + +The `@server` directive, applied at the schema level, provides a comprehensive set of server configurations. It dictates server behavior and helps tune Tailcall for a range of use-cases. + +```graphql showLineNumbers +schema @server(...[ServerSettings]...){ + query: Query + mutation: Mutation +} +``` + +In this templated structure, replace `...[ServerSettings]...` with specific configurations tailored to your project's needs. Adjust and expand these settings as necessary. + +The `ServerSettings` options and their details appear below. + +### workers + +Setting `workers` to `32` means that the GraphQL server will use 32 worker threads. + +```graphql showLineNumbers +schema @server(workers: 32) { + query: Query + mutation: Mutation +} +``` + +This example sets the `workers` to `32`, meaning the GraphQL server will use 32 worker threads. + +### port + +Setting the `port` to `8090` means that Tailcall will be accessible at `http://localhost:8000`. + +```graphql showLineNumbers +schema @server(port: 8090) { + query: Query + mutation: Mutation +} +``` + +This example sets the `port` to `8090`, making Tailcall accessible at `http://localhost:8090`. + +:::tip +Always choose non-standard ports, avoiding typical ones like 80 or 8080. Make sure your chosen port is free. +::: + +### headers + +Allows intelligent configuration of the final response headers that's produced by Tailcall. + +### cacheControl + +Activating the `cacheControl` configuration directs Tailcall to send [Cache-Control] headers in its responses. The `max-age` value in the header matches the lowest of the values in the responses that Tailcall receives from its upstream. By default, this is `false`, which means Tailcall does not set any header. + +[cache-control]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control + +```graphql showLineNumbers +schema @server(headers: {cacheControl: true}) { + query: Query + mutation: Mutation +} +``` + +### custom + +The `custom` is an array of key-value pairs. These headers get added to the response of every request made to the server. This can be useful for adding headers like `Access-Control-Allow-Origin` to allow cross-origin requests, or some headers like `X-Allowed-Roles` for use by downstream services. + +```graphql showLineNumbers +schema + @server( + headers: { + custom: [ + {key: "X-Allowed-Roles", value: "admin,user"} + ] + } + ) { + query: Query + mutation: Mutation +} +``` + +### experimental + +When the `experimental` configuration is enabled, Tailcall can include headers starting with `X-` in its responses, which are sourced from its upstream. By default, this feature is disabled (`[]`), meaning Tailcall does not forward any such headers unless explicitly configured to do so. + +```graphql showLineNumbers +schema + @server( + headers: {experimental: ["X-Experimental-Header"]} + ) { + query: Query + mutation: Mutation +} +``` + +### setCookies + +Enabling the `setCookies` option instructs Tailcall to include `set-cookie` headers in its responses, which are obtained from the headers of upstream responses. + +[set-cookie]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/set-cookie + +```graphql showLineNumbers +schema @server(headers: {setCookies: true}) { + query: Query + mutation: Mutation +} +``` + +### cors + +The `cors` configuration allows you to enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) on Tailcall. This is useful when you want to access Tailcall in the browser. Here is a simple configuration to get started with cors: + +```graphql showLineNumbers +schema + @server( + headers: { + cors: {allowHeaders: ["*"], allowOrigins: ["*"]} + } + ) { + query: Query +} +``` + +The above setting will enable CORS on the server for all headers, origins & methods. You can further configure the cors settings to make it more secure with the following fields: + +- `allowCredentials`: Indicates whether the server allows credentials (e.g., cookies, authorization headers) to be sent in cross-origin requests. +- `allowHeaders`: A list of allowed headers in cross-origin requests. This can be used to specify custom headers that are allowed to be included in cross-origin requests. +- `allowMethods`: A list of allowed HTTP methods in cross-origin requests. These methods specify the actions that are permitted in cross-origin requests. +- `allowOrigins`: A list of origins that are allowed to access the server's resources in cross-origin requests. An origin can be a domain, a subdomain, or even 'null' for local file schemes. +- `allowPrivateNetwork`: Indicates whether requests from private network addresses are allowed in cross-origin requests. Private network addresses typically include IP addresses reserved for internal networks. +- `exposeHeaders`: A list of headers that the server exposes to the browser in cross-origin responses. Exposing certain headers allows client-side code to access them in the response. +- `maxAge`: The maximum time (in seconds) that the client should cache preflight OPTIONS requests to avoid sending excessive requests to the server. +- `vary`: A list of header names that indicate the values of which might cause the server's response to vary, potentially affecting caching. + +```graphql showLineNumbers +schema + @server( + port: 8000 + hostname: "0.0.0.0" + headers: { + cors: { + allowCredentials: false + allowHeaders: ["Authorization"] + allowMethods: [POST, GET, OPTIONS] + allowOrigins: ["abc.xyz"] + allowPrivateNetwork: true + exposeHeaders: ["Content-Type"] + maxAge: 360 + vary: ["Origin"] + } + } + ) { + query: Query +} +``` + +### vars + +This configuration allows defining local variables for use during the server's operations. These variables are handy for storing constant configurations, secrets, or other shared information that operations might need. + +```graphql showLineNumbers +schema + @server( + vars: {key: "apiKey", value: "YOUR_API_KEY_HERE"} + ) { + query: Query + mutation: Mutation +} + +type Query { + externalData: Data + @http( + path: "/external-api/data" + headers: [ + { + key: "Authorization" + value: "Bearer {{.vars.apiKey}}" + } + ] + ) +} +``` + +In the provided example, setting a variable named `apiKey` with a placeholder value of "YOUR_API_KEY_HERE" implies that whenever Tailcall fetches data from the `externalData` endpoint, it includes the `apiKey` in the Authorization header of the HTTP request. + +:::tip +Local variables, like `apiKey`, are instrumental in securing access to external services or providing a unified place for configurations. Ensure that sensitive information stored this way is well protected and not exposed unintentionally, if your GraphQL configuration is publicly accessible. +::: + +### introspection + +This setting controls the server's allowance of introspection queries. Introspection, a core feature of GraphQL, allows clients to directly fetch schema information. This capability proves crucial for tools and client applications in comprehending the available types, fields, and operations. By default, the server enables this setting (`true`). + +```graphql showLineNumbers +schema @server(introspection: false) { + query: Query + mutation: Mutation +} +``` + +:::tip +Although introspection is beneficial during development and debugging stages, consider disabling it in production environments. Turning off introspection in live deployments can enhance security by preventing potential attackers from discerning the schema and any associated business logic or data structures. +::: + +### queryValidation + +The `queryValidation` configuration determines if the server checks incoming GraphQL queries against the defined schema. Each query check ensures it matches the schema, preventing errors from incorrect or malformed queries. In some situations, you might want to disable it, notably to **enhance server performance** at the cost of these checks. This defaults to `false` if not specified. + +```graphql showLineNumbers +schema @server(queryValidation: true) { + query: Query + mutation: Mutation +} +``` + +The example above sets `queryValidation` to `true`, enabling the validation phase for incoming queries. + +:::tip +Enable this in the development environment to ensure the queries sent are correct and validated. In the production environment, consider disabling it for improved performance. +::: + +### responseValidation + +Tailcall can automatically infer the schema of the HTTP endpoints for you. This information can check responses received from the upstream services. Enabling this setting allows you to do that. If not specified, the default setting for `responseValidation` is `false`. + +```graphql showLineNumbers +schema @server(responseValidation: true) { + query: Query + mutation: Mutation +} +``` + +:::tip +Disabling this setting will offer major performance improvements, but at the potential expense of data integrity. +::: + +### globalResponseTimeout + +The `globalResponseTimeout` configuration sets the max duration a query can run before the server terminates it. Essentially, it acts as a safeguard against long-running queries that could strain resources or pose security concerns. + +If not explicitly defined, there might be a system-specific or default value that applies. + +```graphql showLineNumbers +schema @server(globalResponseTimeout: 5000) { + query: Query + mutation: Mutation +} +``` + +In this given example, setting the `globalResponseTimeout` to `5000` milliseconds, or 5 seconds, means any query execution taking longer than this duration will be automatically terminated by + +:::tip +Setting an appropriate response timeout in production environments is crucial. This optimizes resource use and serves as a security measure against potential denial-of-service attacks, where adversaries might run complex queries to exhaust server resources. +::: + +### version + +The server uses the HTTP version. If not specified, the default value is `HTTP1`. The available options are `HTTP1` and `HTTP2`. + +```graphql showLineNumbers +schema @server(version: HTTP2) { + query: Query + mutation: Mutation +} +``` + +### cert + +The path to certificate(s) for running the server over HTTP2 (HTTPS). If not specified, the default value is `null`. + +```graphql showLineNumbers +schema @server(cert: "./cert.pem") { + query: Query + mutation: Mutation +} +``` + + + +:::tip +The certificate can be of any extension, but it's highly recommended to use standards (`pem`, `crt`, `key`). +::: + +### key + +The path to the key for running the server over HTTP2 (HTTPS). If not specified, the default value is `null`. + +```graphql showLineNumbers +schema @server(key: "./key.pem") { + query: Query + mutation: Mutation +} +``` + +:::tip +The key can be of any extension, but it's highly recommended to use standards (`pem`, `crt`, `key`). +::: + +### showcase + +The `@server` directive's `showcase` option allows for hands-on experimentation with server configurations in a controlled environment. This feature simplifies the process of exploring and testing different settings. + +```graphql showLineNumbers +schema @server(showcase: true) { + query: Query +} +``` + +### batchRequests + +Batching in GraphQL combines requests into one, reducing server round trips. + +```graphql showLineNumbers +schema @server( + port: 8000 + batchRequests: true +) +``` + +:::tip +Batching can improve performance but may introduce latency if one request in the batch takes longer. It also makes network traffic debugging harder. +::: + +### dedupe + +A boolean flag, if set to `true`, will enable deduplication of IO operations to enhance performance. This flag prevents duplicate IO requests from being executed concurrently, reducing resource load. If not specified, this feature defaults to `false`. + +```graphql showLineNumbers +schema @server( + port: 8000 + dedupe: true +) +``` + +### routes + +This optional field allows you to customize the server's endpoint paths, enabling you to override the default values for the GraphQL and status endpoints. If not specified, the following default paths will be used: + +- graphQL: `/graphql` +- status: `/status` + +```graphql showLineNumbers +schema @server(routes: {graphQL: "/tailcall-gql", status: "/health"}) +``` + +In this example, the GraphQL endpoint is changed to `/tailcall-gql` and the status endpoint to `/health`. diff --git a/docs/directives/telemetry.md b/docs/directives/telemetry.md new file mode 100644 index 0000000000..feb65740b8 --- /dev/null +++ b/docs/directives/telemetry.md @@ -0,0 +1,143 @@ +--- +title: Enhancing Observability with @telemetry +description: Enhance the observability of your GraphQL services with the @telemetry directive, enabling seamless integration with OpenTelemetry. +slug: telemetry-directive +sidebar_label: Telemetry Configuration +--- + +The `@telemetry` directive facilitates seamless integration with [OpenTelemetry](https://open-telemetry.io), enhancing the observability of your GraphQL services powered by Tailcall. By leveraging this directive, developers gain access to valuable insights into the performance and behavior of their applications. + +### Traces + +Here are the traces that are captured by the `@telemetry` directive: + +| Trace Name | Description | +| --------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| request | Captures the span for processing the HTTP request on the server side, providing foundational observability. | +| graphQL | _Only for GraphQL ingress_. Span for processing GraphQL call | +| `REST ` | _Only for REST ingress_. Span for processing REST API call | +| `` | Denotes spans for fields with defined resolvers, offering insights into field names and execution times for resolver logic. | +| `` | Nested within the `` spans, these granulated spans detail the execution of expressions in resolving a field, highlighting the hierarchical execution pattern of nested expressions. | +| upstream_request | Request that were made from tailcall service to upstream | + +### Metrics + +The `@telemetry` directive also captures the following metrics: + +| Metric | Description | +| ------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------- | +| cache.hit_rate | Reflects the cache hit rate for the cache powered by the [`@cache`](#cache-directive) directive | +| http.server.request.count | Counts the number of incoming requests made to specific route. Optionally enriched with selected headers by [`requestHeaders`](#requestheaders) | +| http.client.request.count | Counts the number of outgoing requests to specific upstream | + +### export + +The `export` field defines how the open-telemetry data should be exported and in which format. The following are the supported formats: + +### otlp + +Utilizes the OTLP format to export telemetry data to backend systems, supported by most modern tracing and analytics platforms. Here is an example using [honeycomb.io]: + +[honecomb.io]: https://www.honeycomb.io/ + +```graphql +schema + @telemetry( + export: { + otlp: { + url: "https://api.honeycomb.io:443" + headers: [ + { + key: "x-honeycomb-team" + value: "{{.env.HONEYCOMB_API_KEY}}" + } + {key: "x-honeycomb-dataset", value: "tailcall"} + ] + } + } + ) { + query: Query +} +``` + +You can configure the OTLP exporter with the following options: + +| Field | Description | +| ------: | ----------------------------------------------------------- | +| url | Defines the URL for the OTLP Collector. | +| headers | Sets additional headers for requests to the OTLP Collector. | + +### prometheus + +Facilitates metrics export in a Prometheus compatible format, providing a dedicated endpoint for metrics. + +```graphql +schema + @telemetry(export: {prometheus: {path: "/metrics"}}) { + query: Query +} +``` + +You can configure the Prometheus exporter with the following options: + +| Field | Description | +| -----: | ---------------------------------------------------------------------------------- | +| path | Designates the endpoint path for Prometheus metrics, defaulting to `/metrics`. | +| format | Controls the format viz. **text** or **protobuf**, for sending data to Prometheus. | + +### stdout + +Outputs all telemetry data to stdout, ideal for testing or local development environments. + +```graphql +schema @telemetry(export: {stdout: {pretty: true}}) { + query: Query +} +``` + +You can configure the stdout exporter with the following options: + +| Field | Description | +| -----: | -------------------------------------------------------------------- | +| pretty | Enables formatted output of telemetry data for enhanced readability. | + +### requestHeaders + +Specifies list of headers of ingress request the value of which will be sent to the telemetry as attributes. + +```graphql +schema @telemetry(requestHeaders: ["X-User-Id"]) { + query: Query +} +``` + +### apollo + +Facilitates seamless integration with [Apollo Studio](https://studio.apollographql.com/), enhancing the observability of GraphQL services. By leveraging this field, developers gain access to valuable insights into the performance and behavior of their GraphQL APIs. + +```graphql +schema + @telemetry( + export: { + otlp: { + api_key: "{{.env.APOLLO_API_KEY}}" + graph_ref: "graph-id@current" + platform: "website.com" + version: "1.0.0" + } + } + ) { + query: Query +} +``` + +You can configure the apollo exporter with the following options: + +| Field | Description | +| --------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| api_key | The API Key generated from Apollo Studio. | +| graph_ref | The Graph Ref, which is the `graph_id` and the `variant` concatenated using `@`(i.e. \@\) | +| platform | An arbitrary value which can contain the name of your website or some other value to identify your deployment uniqely, in case you have multiple deployments. | +| version | Version of Apollo which is being used. | + +By integrating the `@telemetry` directive into your GraphQL schema, you empower your development teams with critical insights into application performance, enabling proactive optimization and maintenance. diff --git a/docs/directives/upstream.md b/docs/directives/upstream.md new file mode 100644 index 0000000000..271975537c --- /dev/null +++ b/docs/directives/upstream.md @@ -0,0 +1,257 @@ +--- +title: Configuring Upstream Connections +description: Control upstream server connections, manage settings like timeouts and proxies, and enhance API efficiency with the upstream directive. +slug: upstream-directive +sidebar_label: Configuring Upstream +--- + +The `upstream` directive enables control over specific aspects of the upstream server connection, including settings such as connection timeouts, keep-alive intervals, and more. The system applies default values if you do not specify them. + +```graphql showLineNumbers +schema @upstream(...[UpstreamSetting]...){ + query: Query + mutation: Mutation +} +``` + +The document below details the options for `UpstreamSetting`. + +### poolIdleTimeout + +The connection pool waits for this duration in seconds before closing idle connections. + +```graphql showLineNumbers +schema + @upstream( + poolIdleTimeout: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### poolMaxIdlePerHost + +The max number of idle connections each host will maintain. + +```graphql showLineNumbers +schema + @upstream( + poolMaxIdlePerHost: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### keepAliveInterval + +The time in seconds between each keep-alive message sent to maintain the connection. + +```graphql showLineNumbers +schema + @upstream( + keepAliveInterval: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### keepAliveTimeout + +The time in seconds that the connection will wait for a keep-alive message before closing. + +```graphql showLineNumbers +schema + @upstream( + keepAliveTimeout: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### keepAliveWhileIdle + +A boolean value that determines whether to send keep-alive messages while the connection is idle. + +```graphql showLineNumbers +schema + @upstream( + keepAliveWhileIdle: false + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### proxy + +The `proxy` setting defines an intermediary server that routes upstream requests before they reach their intended endpoint. By specifying a proxy URL, you introduce a layer, enabling custom routing and security policies. + +```graphql showLineNumbers +schema + @upstream( + proxy: {url: "http://localhost:3000"} + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +In the provided example, we've set the proxy's `url` to "http://localhost:3000". This configuration ensures that all requests aimed at the designated `baseURL` first go through this proxy. To illustrate, if the `baseURL` is "http://jsonplaceholder.typicode.com", any request targeting it initially goes to "http://localhost:3000" before the proxy redirects it to its final destination. + +### connectTimeout + +The time in seconds that the connection will wait for a response before timing out. + +```graphql showLineNumbers +schema + @upstream( + connectTimeout: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### timeout + +The max time in seconds that the connection will wait for a response. + +```graphql showLineNumbers +schema + @upstream( + timeout: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### tcpKeepAlive + +The time in seconds between each TCP keep-alive message sent to maintain the connection. + +```graphql showLineNumbers +schema + @upstream( + tcpKeepAlive: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### userAgent + +The User-Agent header value for HTTP requests. + +```graphql showLineNumbers +schema + @upstream( + userAgent: "Tailcall/1.0" + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### allowedHeaders + +The `allowedHeaders` configuration defines a set of whitelisted HTTP headers that can be forwarded to upstream services during requests. +Without specifying `allowedHeaders`, the system will not forward any incoming headers to upstream services, offering an extra security layer but potentially limiting necessary data flow. Tailcall compares the provided whitelisted headers in a case-insensitive format. + +```graphql showLineNumbers +schema + @upstream( + allowedHeaders: ["Authorization", "X-Api-Key"] + ) { + query: Query + mutation: Mutation +} +``` + +In the example above, the configuration for `allowedHeaders` permits `Authorization` and `X-Api-Key` headers. Thus, requests with these headers will forward them to upstream services; the system ignores all others. This configuration ensures communication of the expected headers to dependent services, emphasizing security and consistency. + +### baseURL + +This refers to the default base URL for your APIs. If it's not explicitly mentioned in the `@upstream` directive, then each [`@http`](#http-directive) directive must specify its own `baseURL`. If neither `@upstream` nor [`@http`](#http-directive) provides a `baseURL`, it results in a compilation error. + +```graphql showLineNumbers +schema + @upstream( + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +In this representation, `http://jsonplaceholder.typicode.com` serves as the `baseURL`. Thus, all API calls made by `@http` prepend this URL to their respective paths. + +:::tip +Ensure that your base URL remains free from specific path segments. + +- **GOOD:** `@upstream(baseURL: http://jsonplaceholder.typicode.com)` +- **BAD:** `@upstream(baseURL: http://jsonplaceholder.typicode.com/api)` + +::: + +### httpCache + +When httpCache passed with value greater than 0 it directs Tailcall to use HTTP caching mechanisms, following the [HTTP Caching RFC](https://tools.ietf.org/html/rfc7234) to enhance performance by minimizing unnecessary data fetches. If left unspecified, this feature defaults to `0` disabling the caching mechanism. + +```graphql showLineNumbers +schema @upstream(httpCache: 42) { + query: Query + mutation: Mutation +} +``` + +### Tips + +- Use batching when other optimization techniques fail to resolve performance issues. +- Apply batching and thoroughly assess its impact. +- Understand that batching may make debugging more challenging. + +### batch + +An object that specifies the batch settings, including `maxSize` (the max size of the batch), `delay` (the delay in milliseconds between each batch), and `headers` (an array of HTTP headers that the batch will include). + +```graphql showLineNumbers +schema + @upstream( + batch: { + maxSize: 1000 + delay: 10 + headers: ["X-Server", "Authorization"] + } + ) { + query: Query + mutation: Mutation +} +``` + +### onRequest + +Similar to the [@http](#http-directive) property, this accepts a string value representing a middleware function defined in a JavaScript file. It intercepts all outgoing HTTP requests from the server. This interceptor, written in JavaScript, can be used to modify outgoing requests and also generate artificial responses to customize the behavior of the GraphQL server. + +```graphql showLineNumbers +schema @upstream(onRequest: 'someFunctionName') +@link(type: Script, src: "path_to/worker.js") { + query: Query + mutation: Mutation +} +``` diff --git a/sidebars.ts b/sidebars.ts index 362a8178de..14419679a3 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -45,6 +45,22 @@ const sidebars: SidebarsConfig = { "environment-variables", "configuration", "config-generation", + "directives/add-field", + "directives/cache", + "directives/call", + "directives/expr", + "directives/graphql", + "directives/grpc", + "directives/http", + "directives/js", + "directives/link", + "directives/modify", + "directives/omit", + "directives/protected", + "directives/rest", + "directives/server", + "directives/telemetry", + "directives/upstream", ], }, {type: "category", label: "Integrations", items: ["apollo-studio", "data-dog", "new-relic", "honey-comb", "llm"]}, From b3fb6844915a0096805d9f0f54c62f75b71854e6 Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Wed, 25 Sep 2024 07:52:27 +0000 Subject: [PATCH 03/12] lint fix --- docs/directives.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/directives.md b/docs/directives.md index cb6b0ef943..b9e66af878 100644 --- a/docs/directives.md +++ b/docs/directives.md @@ -12,8 +12,8 @@ Here is a list of all the custom directives supported by Tailcall: -| Operator | Description | -| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | +| Operator | Description | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | [`@addField`](/docs/directives/add-field-directive/) | Simplifies data structures and queries by adding, inlining, or flattening fields or nodes within the schema. | | [`@cache`](/docs/directives/cache-directive/) | Enables caching for the query, field or type applied to. | | [`@call`](/docs/directives/call-directive/) | Invokes a query or mutation from another query or mutation field. | From 0dc01961740c2684bd00fab2977e4aa4cbb33559 Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Wed, 25 Sep 2024 07:55:35 +0000 Subject: [PATCH 04/12] revert snapshot --- publish-externals/snapshot.json | 315 +------------------------------- 1 file changed, 1 insertion(+), 314 deletions(-) diff --git a/publish-externals/snapshot.json b/publish-externals/snapshot.json index 48c77ea3c6..2ffbaf0659 100644 --- a/publish-externals/snapshot.json +++ b/publish-externals/snapshot.json @@ -1,316 +1,3 @@ { - "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 - } - } - } - } + "blogs": {} } \ No newline at end of file From 0960e5d08956e3c3f8942379af0d5bfa56ab491c Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Wed, 25 Sep 2024 07:56:36 +0000 Subject: [PATCH 05/12] lint --- publish-externals/snapshot.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/publish-externals/snapshot.json b/publish-externals/snapshot.json index 2ffbaf0659..770fdd3117 100644 --- a/publish-externals/snapshot.json +++ b/publish-externals/snapshot.json @@ -1,3 +1,3 @@ { "blogs": {} -} \ No newline at end of file +} From d6d6131d77bc5fcea86f2b13218c859573eba79f Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Wed, 25 Sep 2024 07:59:26 +0000 Subject: [PATCH 06/12] fixes --- docs/directives/grpc.md | 2 +- docs/directives/http.md | 2 +- docs/directives/link.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/directives/grpc.md b/docs/directives/grpc.md index 464a029107..a2ead524b5 100644 --- a/docs/directives/grpc.md +++ b/docs/directives/grpc.md @@ -137,7 +137,7 @@ type Query { ``` :::info -Read about [n + 1](./N+1.md) to learn how to use the `batchKey` setting. +Read about [n + 1](../N+1.md) to learn how to use the `batchKey` setting. ::: ### onResponseBody diff --git a/docs/directives/http.md b/docs/directives/http.md index 8fbde28f15..6d33db6405 100644 --- a/docs/directives/http.md +++ b/docs/directives/http.md @@ -144,7 +144,7 @@ In this scenario, the `User-Name` header's value will dynamically adjust accordi ### batchKey -Groups data requests into a single call, enhancing efficiency. Refer to our [n + 1 guide](./N+1.md) for more details. +Groups data requests into a single call, enhancing efficiency. Refer to our [n + 1 guide](../N+1.md) for more details. :::important When `batchKey` is present, Tailcall considers the first `query` parameter to be the batch query key, so remember to adjust the order of the items accordingly. Whereas, the last item from `batchKey` is used to instruct which field is the ID of an object. In case that the returned result is a nested property `batchKey` can be used as a path to extract and group the items for the returned result. diff --git a/docs/directives/link.md b/docs/directives/link.md index 2fc5e5a93e..f1d6025faf 100644 --- a/docs/directives/link.md +++ b/docs/directives/link.md @@ -116,8 +116,8 @@ Each type serves a specific purpose, enabling the flexible integration of extern ### Htpasswd -The `Htpasswd` link type allows the importation of an [`htpasswd`](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) file. This file is utilized to set up [Basic authentication](./auth.md#basic-authentication). +The `Htpasswd` link type allows the importation of an [`htpasswd`](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) file. This file is utilized to set up [Basic authentication](../auth.md#basic-authentication). ### Jwks -The `Jwks` link type enables the importation of a [`JWKS`](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-key-sets) file. This file facilitates the provision of detailed access control through [JWT authentication](./auth.md#jwt-authentication). +The `Jwks` link type enables the importation of a [`JWKS`](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-key-sets) file. This file facilitates the provision of detailed access control through [JWT authentication](../auth.md#jwt-authentication). From a1e7432c2c750ec82703bd319dd8663b7cf89c9b Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Wed, 25 Sep 2024 11:06:31 +0000 Subject: [PATCH 07/12] fixed some links --- blog/tailcall-n+1-working-2024-08-04.md | 2 +- docs/apollo-studio.md | 2 +- docs/cli.md | 4 ++-- docs/client-tuning.md | 2 +- docs/config-generation.md | 8 ++++---- docs/directives.md | 2 +- docs/directives/call.md | 2 +- docs/directives/graphql.md | 2 +- docs/directives/http.md | 2 +- docs/directives/upstream.md | 4 ++-- docs/grpc.md | 2 +- docs/scripting.md | 2 +- docs/telemetry.md | 4 ++-- docusaurus.config.ts | 12 ++++++------ graphql/graphql.md | 2 +- 15 files changed, 26 insertions(+), 26 deletions(-) diff --git a/blog/tailcall-n+1-working-2024-08-04.md b/blog/tailcall-n+1-working-2024-08-04.md index 5bebb02151..9b20ae9fb4 100644 --- a/blog/tailcall-n+1-working-2024-08-04.md +++ b/blog/tailcall-n+1-working-2024-08-04.md @@ -116,7 +116,7 @@ Now, here's where it gets fascinating. We use a Depth-First Search (DFS) algorit 1. Initialize two variables to track the currently traversed `path` and `visited` fields so that we can avoid cycles. 2. Start at the root query and begin traversing the graph data structure. -3. For each field in the current node, check if it has a resolver and is not batched. We know if the node contains a resolver if that node has a [`@http`](/docs/tailcall-dsl-graphql-custom-directives#http-directive) or a [`@grpc`](/docs/tailcall-dsl-graphql-custom-directives#grpc-directive). Tailcall supports powerful batching primitives and if a field uses a Batch API, then that resolver is whitelisted and dropped from the list of potential N+1 candidates. +3. For each field in the current node, check if it has a resolver and is not batched. We know if the node contains a resolver if that node has a [`@http`](/docs/directives/http-directive) or a [`@grpc`](/docs/directives/grpc-directive). Tailcall supports powerful batching primitives and if a field uses a Batch API, then that resolver is whitelisted and dropped from the list of potential N+1 candidates. 4. If the field has a resolver and is not batched, and the current path contains a list, then the current path is added to the result. 5. Otherwise, we recursively traverse the graph data structure, updating the current path and visited fields as necessary. 6. If a cycle is detected, return the cached result instead of re-traversing the path. diff --git a/docs/apollo-studio.md b/docs/apollo-studio.md index e087eb2bc8..7d96bd18b0 100644 --- a/docs/apollo-studio.md +++ b/docs/apollo-studio.md @@ -34,7 +34,7 @@ This guide illustrates how to configure `tailcall` to send usage metrics to [Apo ![local-schema.png](../static/images/apollo-studio/local-schema.png) -You have now created a Monolith graph in Apollo Studio. The next step is to configure `tailcall` to use the `APOLLO_API_KEY` and `APOLLO_GRAPH_REF`. Follow detailed instructions [here](/docs/directives.md#telemetry-directive). +You have now created a Monolith graph in Apollo Studio. The next step is to configure `tailcall` to use the `APOLLO_API_KEY` and `APOLLO_GRAPH_REF`. Follow detailed instructions [here](/docs/directives/telemetry.md). ## Checking the metrics in Apollo Studio diff --git a/docs/cli.md b/docs/cli.md index cf01492b34..3c6fca26d6 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -430,9 +430,9 @@ preset: } ``` -2. **consolidateURL:** The setting identifies the most common base URL among multiple REST endpoints and uses this URL in the [upstream](directives.md#upstream-directive) directive. It takes a threshold value between 0.0 and 1.0 to determine the most common endpoint. The default is `0.5`. +2. **consolidateURL:** The setting identifies the most common base URL among multiple REST endpoints and uses this URL in the [upstream](/docs/directives/upstream.md) directive. It takes a threshold value between 0.0 and 1.0 to determine the most common endpoint. The default is `0.5`. - For example, if the `Query` type has three base URLs, using the `consolidateURL` setting with a `0.5` threshold will pick the base URL that is used in more than 50% of the [http](directives.md#http-directive) directives, `http://jsonplaceholder.typicode.com`, and add it to the upstream, cleaning the base URLs from the `Query` type. + For example, if the `Query` type has three base URLs, using the `consolidateURL` setting with a `0.5` threshold will pick the base URL that is used in more than 50% of the [http](/docs/directives/http.md) directives, `http://jsonplaceholder.typicode.com`, and add it to the upstream, cleaning the base URLs from the `Query` type. ```graphql showLineNumbers schema diff --git a/docs/client-tuning.md b/docs/client-tuning.md index 2b768cc8c6..1d305904ae 100644 --- a/docs/client-tuning.md +++ b/docs/client-tuning.md @@ -50,7 +50,7 @@ Connection pooling mitigates these issues by reusing existing connections for re ## Tuning HTTP Client -Tailcall uses connection pooling by default and sets up with default tuning suitable for most use cases. You might need to further tune the HTTP client to improve your application's performance. Tailcall DSL provides a directive named [`@upstream`](/docs/directives.md#upstream-directive) for this purpose. +Tailcall uses connection pooling by default and sets up with default tuning suitable for most use cases. You might need to further tune the HTTP client to improve your application's performance. Tailcall DSL provides a directive named [`@upstream`](/docs/directives/upstream.md) for this purpose. :::note Connection pooling optimizes HTTP/1. Since HTTP/2 and HTTP/3 support multiplexing, pooling enabled does not noticeably affect performance. diff --git a/docs/config-generation.md b/docs/config-generation.md index 1904cb085a..1bf6637a8c 100644 --- a/docs/config-generation.md +++ b/docs/config-generation.md @@ -697,9 +697,9 @@ This setting merges types in the configuration that satisfy the threshold criter ### consolidateURL -The setting identifies the most common base URL among multiple REST endpoints and uses this URL in the [upstream](directives.md#upstream-directive) directive. It takes a threshold value between 0.0 and 1.0 to determine the most common endpoint. The default is `0.5`. +The setting identifies the most common base URL among multiple REST endpoints and uses this URL in the [upstream](/docs/directives/upstream.md) directive. It takes a threshold value between 0.0 and 1.0 to determine the most common endpoint. The default is `0.5`. -For example, if the `Query` type has three base URLs, using the `consolidateURL` setting with a `0.5` threshold will pick the base URL that is used in more than 50% of the [http](directives.md#http-directive) directives, `http://jsonplaceholder.typicode.com`, and add it to the upstream, cleaning the base URLs from the `Query` type. +For example, if the `Query` type has three base URLs, using the `consolidateURL` setting with a `0.5` threshold will pick the base URL that is used in more than 50% of the [http](/docs/directives/http.md) directives, `http://jsonplaceholder.typicode.com`, and add it to the upstream, cleaning the base URLs from the `Query` type. ```graphql showLineNumbers schema @@ -950,7 +950,7 @@ When setting up your configuration file for GraphQL generation with Tailcall, co -2. **[Consolidate URL](config-generation.md#consolidateurl)**: Identifies the most common base URL among multiple REST endpoints and uses it in the [@upstream](directives.md#upstream-directive) directive. Set a threshold (0.0 to 1.0) to determine when to consolidate URLs. Recommended threshold is anything above `0.5`. +2. **[Consolidate URL](config-generation.md#consolidateurl)**: Identifies the most common base URL among multiple REST endpoints and uses it in the [@upstream](/docs/directives/upstream.md) directive. Set a threshold (0.0 to 1.0) to determine when to consolidate URLs. Recommended threshold is anything above `0.5`. ```json showLineNumbers @@ -1043,7 +1043,7 @@ curl: **Q. What if I have multiple REST endpoints with different base URLs?** -**Answer:** Use the [consolidateURL](config-generation.md#consolidateurl) parameter to identify the most common base URL among multiple REST endpoints and it will automatically select the most common base url and add it to the [@upstream](directives.md#upstream-directive) directive. Here is an example: +**Answer:** Use the [consolidateURL](config-generation.md#consolidateurl) parameter to identify the most common base URL among multiple REST endpoints and it will automatically select the most common base url and add it to the [@upstream](/docs/directives/upstream.md) directive. Here is an example: diff --git a/docs/directives.md b/docs/directives.md index b9e66af878..b95db3a34b 100644 --- a/docs/directives.md +++ b/docs/directives.md @@ -18,7 +18,7 @@ Here is a list of all the custom directives supported by Tailcall: | [`@cache`](/docs/directives/cache-directive/) | Enables caching for the query, field or type applied to. | | [`@call`](/docs/directives/call-directive/) | Invokes a query or mutation from another query or mutation field. | | [`@expr`](/docs/directives/expr-directive/) | Allows embedding of a constant response within the schema. | -| [`@graphQL`](/docs/directives/graphql-directive/) | Resolves a field or node by a GraphQL API. | +| [`@graphQL`](/docs/directives/graphQL-directive/) | Resolves a field or node by a GraphQL API. | | [`@grpc`](/docs/directives/grpc-directive/) | Resolves a field or node by a gRPC API. | | [`@http`](/docs/directives/http-directive/) | Resolves a field or node by a REST API. | | [`@link`](/docs/directives/link-directive/) | Imports external resources such as config files, certs, protobufs, etc in the schema. | diff --git a/docs/directives/call.md b/docs/directives/call.md index 4486cf2311..73819db481 100644 --- a/docs/directives/call.md +++ b/docs/directives/call.md @@ -177,7 +177,7 @@ Here is how the response would look like: } ``` -As you can see the [`@expr`](#expr-directive) directive plucks the inner value and returns the result. How about we implement an `abc` operation that could leverage the existing operations and unwrap the following input value: +As you can see the [`@expr`](/docs/directives/expr.md) directive plucks the inner value and returns the result. How about we implement an `abc` operation that could leverage the existing operations and unwrap the following input value: ```json {"a": {"b": {"c": {"d": 1000}}}} diff --git a/docs/directives/graphql.md b/docs/directives/graphql.md index df8a38151a..c32022e040 100644 --- a/docs/directives/graphql.md +++ b/docs/directives/graphql.md @@ -39,7 +39,7 @@ query { ### baseURL -This refers to the base URL of the API. If not specified, the default base URL is the one specified in the [`@upstream`](#upstream-directive) directive. +This refers to the base URL of the API. If not specified, the default base URL is the one specified in the [`@upstream`](/docs/directives/upstream.md) directive. ```graphql showLineNumbers type Query { diff --git a/docs/directives/http.md b/docs/directives/http.md index 6d33db6405..a0f57e99ec 100644 --- a/docs/directives/http.md +++ b/docs/directives/http.md @@ -17,7 +17,7 @@ In this example, adding the `@http` directive to the `users` field of the `Query ### baseURL -Specifies the API's base URL. If unspecified, it defaults to the URL in the [`@upstream`](#upstream-directive) directive. +Specifies the API's base URL. If unspecified, it defaults to the URL in the [`@upstream`](/docs/directives/upstream.md) directive. ```graphql showLineNumbers type Query { diff --git a/docs/directives/upstream.md b/docs/directives/upstream.md index 271975537c..7564db75ec 100644 --- a/docs/directives/upstream.md +++ b/docs/directives/upstream.md @@ -187,7 +187,7 @@ In the example above, the configuration for `allowedHeaders` permits `Authorizat ### baseURL -This refers to the default base URL for your APIs. If it's not explicitly mentioned in the `@upstream` directive, then each [`@http`](#http-directive) directive must specify its own `baseURL`. If neither `@upstream` nor [`@http`](#http-directive) provides a `baseURL`, it results in a compilation error. +This refers to the default base URL for your APIs. If it's not explicitly mentioned in the `@upstream` directive, then each [`@http`](/docs/directives/http.md) directive must specify its own `baseURL`. If neither `@upstream` nor [`@http`](/docs/directives/http.md) provides a `baseURL`, it results in a compilation error. ```graphql showLineNumbers schema @@ -246,7 +246,7 @@ schema ### onRequest -Similar to the [@http](#http-directive) property, this accepts a string value representing a middleware function defined in a JavaScript file. It intercepts all outgoing HTTP requests from the server. This interceptor, written in JavaScript, can be used to modify outgoing requests and also generate artificial responses to customize the behavior of the GraphQL server. +Similar to the [@http](/docs/directives/http.md) property, this accepts a string value representing a middleware function defined in a JavaScript file. It intercepts all outgoing HTTP requests from the server. This interceptor, written in JavaScript, can be used to modify outgoing requests and also generate artificial responses to customize the behavior of the GraphQL server. ```graphql showLineNumbers schema @upstream(onRequest: 'someFunctionName') diff --git a/docs/grpc.md b/docs/grpc.md index d6c4d0ca45..64b0b43267 100644 --- a/docs/grpc.md +++ b/docs/grpc.md @@ -123,7 +123,7 @@ type Query { } ``` -Also, let's specify options for Tailcall's ingress and egress at the beginning of the config using [`@server`](/docs/directives.md#server-directive) and [`@upstream`](/docs/directives.md#upstream-directive) directives. +Also, let's specify options for Tailcall's ingress and egress at the beginning of the config using [`@server`](/docs/directives.md#server-directive) and [`@upstream`](/docs/directives/upstream.md) directives. ```graphql schema diff --git a/docs/scripting.md b/docs/scripting.md index 260ef7e39b..82e0311403 100644 --- a/docs/scripting.md +++ b/docs/scripting.md @@ -123,7 +123,7 @@ type Request = { } ``` -By default the headers field will be empty in most cases, unless headers are whitelisted via the [allowedHeaders](/docs/directives.md#allowedheaders) setting in [`@upstream`](/docs/directives.md#upstream-directive). +By default the headers field will be empty in most cases, unless headers are whitelisted via the [allowedHeaders](/docs/directives.md#allowedheaders) setting in [`@upstream`](/docs/directives/upstream.md). The http filter doesn't have access to the request's body, hence you can't directly modify the body of an outgoing request. This is more of a design choice than a limitation we have made to ensure that developers don't misuse this API to write all kind of business logic in Tailcall. diff --git a/docs/telemetry.md b/docs/telemetry.md index 2c4cd0fc90..315fdd06bf 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -75,7 +75,7 @@ We will update that config with telemetry integration in following sections. By default, telemetry data is not generated by Tailcall since it requires some setup to know where to send this data and also that affects performance of server that could be undesirable in some cases. -Telemetry configuration is provided by [`@telemetry`](/docs/directives.md#telemetry-directive) directive to setup how and where the telemetry data is send. +Telemetry configuration is provided by [`@telemetry`](/docs/directives/telemetry.md) directive to setup how and where the telemetry data is send. To enable it we can update our config with something like config below: @@ -112,7 +112,7 @@ Prometheus integration works by adding a special route for the GraphQL server's ## Data generated -You can find a reference of type of info generated by Tailcall in the [`@telemetry` reference](/docs/directives.md#telemetry-directive) or consult examples in the next section, in order to gain some understanding. +You can find a reference of type of info generated by Tailcall in the [`@telemetry` reference](/docs/directives/telemetry.md) or consult examples in the next section, in order to gain some understanding. ### Relation with other services diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 7286b917b7..34934e64b9 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -185,27 +185,27 @@ export default { }, { from: "/docs/operators/add-field/", - to: "/docs/tailcall-dsl-graphql-custom-directives/#addfield-directive", + to: "/docs/directives/add-field-directive", }, { from: "/docs/operators/graphql/", - to: "/docs/tailcall-dsl-graphql-custom-directives/#graphql-directive", + to: "/docs/directives/graphQL-directive", }, { from: "/docs/operators/http/", - to: "/docs/tailcall-dsl-graphql-custom-directives/#http-directive", + to: "/docs/directives/http-directive", }, { from: "/docs/operators/server/", - to: "/docs/tailcall-dsl-graphql-custom-directives/#server-directive", + to: "/docs/directives/server-directive", }, { from: "/docs/operators/telemetry/", - to: "/docs/tailcall-dsl-graphql-custom-directives/#telemetry-directive", + to: "/docs/directives/telemetry-directive", }, { from: "/docs/operators/upstream/", - to: "/docs/tailcall-dsl-graphql-custom-directives/#upstream-directive", + to: "/docs/directives/upstream-directive", }, { from: "/docs/n+1/introduction/", diff --git a/graphql/graphql.md b/graphql/graphql.md index 40b553d52f..988a116dc7 100644 --- a/graphql/graphql.md +++ b/graphql/graphql.md @@ -230,7 +230,7 @@ When a client sends a GraphQL query, the server resolves each field in the query By setting up a GraphQL server, defining a schema, and attaching resolvers, you can begin building powerful and flexible APIs that provide clients with the precise data they need. This foundational knowledge forms the basis for more advanced GraphQL development, including integrating GraphQL with existing APIs, handling complex data relationships, and optimizing API performance. Lets attach resolvers to the schema we defined in the previous section using Tailcall's GraphQL Configuration: -We will add the resolvers with [`@http`](/docs/tailcall-dsl-graphql-custom-directives/#http-directive) directive: +We will add the resolvers with [`@http`](/docs/directives/http-directive) directive: ```graphql schema From b5e97c5fa56b1432b135ea1b627ab51de1bdc7d8 Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Wed, 25 Sep 2024 11:42:12 +0000 Subject: [PATCH 08/12] links fix --- blog/graphql-and-ms-match-2024-08-18.md | 2 +- docs/N+1.md | 4 ++-- docs/auth.md | 14 +++++++------- docs/directives/grpc.md | 2 +- docs/directives/http.md | 4 ++-- docs/directives/modify.md | 2 +- docs/directives/omit.md | 2 +- docs/directives/protected.md | 2 +- docs/directives/telemetry.md | 2 +- docs/grpc.md | 6 +++--- docs/http-cache.md | 2 +- docs/http2.md | 2 +- docs/rest.md | 4 ++-- docs/scripting.md | 10 +++++----- docs/telemetry.md | 2 +- 15 files changed, 30 insertions(+), 30 deletions(-) diff --git a/blog/graphql-and-ms-match-2024-08-18.md b/blog/graphql-and-ms-match-2024-08-18.md index e9fab7ebc0..7fef50f593 100644 --- a/blog/graphql-and-ms-match-2024-08-18.md +++ b/blog/graphql-and-ms-match-2024-08-18.md @@ -125,7 +125,7 @@ Designing scalable APIs that leverage both **GraphQL** and **microservices** req 3. **Caching:** Utilize effective caching strategies at both the GraphQL and microservice levels to enhance performance. Caching reduces the need for repeated data retrieval, lowering latency and improving the user experience. Consider using in-memory caches for frequently accessed data and implement cache invalidation strategies to ensure data consistency. 4. **Monitoring and Logging:** Comprehensive monitoring and logging are vital for tracking API performance, identifying potential bottlenecks, and quickly resolving issues. Implementing detailed logging with tools like OpenTelemetry provides valuable insights into the behavior of your API, helping you maintain high availability and optimize performance over time. -5. **Security:** With **Tailcall's** [built-in auth](https://tailcall.run/docs/field-level-access-control-graphql-authentication/) and [@protected](https://tailcall.run/docs/tailcall-dsl-graphql-custom-directives/#protected-directive) directive, you can add auth functionality and make fields protected with just a few lines of code - which is intelligent enough to protect any query that indirectly resolves to that field: +5. **Security:** With **Tailcall's** [built-in auth](https://tailcall.run/docs/field-level-access-control-graphql-authentication/) and [@protected](https://tailcall.run/docs/protected-directive) directive, you can add auth functionality and make fields protected with just a few lines of code - which is intelligent enough to protect any query that indirectly resolves to that field: ```graphql type Reaction @protected { diff --git a/docs/N+1.md b/docs/N+1.md index ea26bd09aa..58c84dd19f 100644 --- a/docs/N+1.md +++ b/docs/N+1.md @@ -175,7 +175,7 @@ If you run the query, at first you will observe a lot of duplicate requests are ![Duplicate Upstream Calls](../static/images/docs/n+1-duplicate.png) -This happens because of the 100 posts, a lot them are authored by the same user and by default Tailcall will make a request for every user when requested. You can fix this by setting [dedupe](/docs/directives.md#dedupe) to `true` in [server](/docs/directives.md#server-directive). +This happens because of the 100 posts, a lot them are authored by the same user and by default Tailcall will make a request for every user when requested. You can fix this by setting [dedupe](/docs/directives/server.md#dedupe) to `true` in [server](/docs/directives/server.md). ```graphql {3} schema @@ -260,7 +260,7 @@ Incredible, isn't it? Tailcall has discovered that querying for posts followed b ![Batched API](../static/images/docs/n+1-batch.png) -An effective technique to mitigate the N+1 problem is deduplicating similar requests, significantly reducing the number of server calls. We achieved it previously using the [dedupe](/docs/directives.md#dedupe) setting. With Tailcall we can go one step further by giving hints about "batch APIs". +An effective technique to mitigate the N+1 problem is deduplicating similar requests, significantly reducing the number of server calls. We achieved it previously using the [dedupe](/docs/directives/server.md#dedupe) setting. With Tailcall we can go one step further by giving hints about "batch APIs". **Batch APIs:** Are special APIs that allow us to query multiple things at once. In our case we can pass multiple user Ids as query params, to the `/users` API to resolve many users at once: diff --git a/docs/auth.md b/docs/auth.md index 3a292668c7..8ed5f4cdbb 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -46,8 +46,8 @@ to know more about how to use it, read the following articles: Enabling support for authentication in Tailcall could be done in two steps: -1. With the help of [`@link` directive](/docs/directives.md#link-directive) connect multiple authentication files as you need for different provides. To connect it use either [`Htpasswd`](/docs/directives.md#htpasswd) or [`Jwks`](/docs/directives.md#jwks) link type -2. Mark that some type of field requires authentication to be fetched with the help of [`@protected` directive](/docs/directives.md#protected-directive) +1. With the help of [`@link` directive](/docs/directives/link.md) connect multiple authentication files as you need for different provides. To connect it use either [`Htpasswd`](/docs/directives/link.md#htpasswd) or [`Jwks`](/docs/directives/link.md#jwks) link type +2. Mark that some type of field requires authentication to be fetched with the help of [`@protected` directive](/docs/directives/protected.md) Your config could look like this now: @@ -229,7 +229,7 @@ In case you linked multiple authentication files all of them will be used to exe ### Authentication headers -To validate authentication for user request the specific headers are used (like `Authorization` header). In case auth is enabled for tailcall those headers will be also added to the [`allowedHeaders` list](/docs/directives.md#allowedheaders) and therefore they will be forwarded to the upstream requests implicitly. +To validate authentication for user request the specific headers are used (like `Authorization` header). In case auth is enabled for tailcall those headers will be also added to the [`allowedHeaders` list](/docs/directives/upstream.md#allowedheaders) and therefore they will be forwarded to the upstream requests implicitly. ## Basic Authentication @@ -249,7 +249,7 @@ Since this file stores secure information make sure to hash the password you use ### Basic Auth GraphQL Configuration -To use Basic Auth you should first include htpasswd file generated from [Prerequisites](#prerequisites) with the help of [`@link` directive](/docs/directives.md#htpasswd). +To use Basic Auth you should first include htpasswd file generated from [Prerequisites](#prerequisites) with the help of [`@link` directive](/docs/directives/link.md#htpasswd). We can use that file as an example for it that has data for `testuser:mypassword` credentials in encrypted format: @@ -257,7 +257,7 @@ We can use that file as an example for it that has data for `testuser:mypassword testuser:$2y$10$wJ/mZDURcAOBIrswCAKFsO0Nk7BpHmWl/XuhF7lNm3gBAFH3ofsuu ``` -After adding `@link` you can use the [`@protected` directive](/docs/directives.md#protected-directive) to mark the fields that requiring success authentication to be requested. +After adding `@link` you can use the [`@protected` directive](/docs/directives/protected.md) to mark the fields that requiring success authentication to be requested. The whole example could look like this: @@ -330,7 +330,7 @@ To create this file you can use available web-tools like [JWK creator](https://r ### JWT Auth GraphQL Configuration -To use JWT you should first include JWKS file generated from [Prerequisites](#prerequisites) with the help of [`@link` directive](/docs/directives.md#jwks). +To use JWT you should first include JWKS file generated from [Prerequisites](#prerequisites) with the help of [`@link` directive](/docs/directives/link.md#jwks). We can use that file as an example for it: @@ -349,7 +349,7 @@ We can use that file as an example for it: } ``` -After adding `@link` you can use the [`@protected` directive](/docs/directives.md#protected-directive) to mark the fields that requiring success authentication to be requested. +After adding `@link` you can use the [`@protected` directive](/docs/directives/protected.md) to mark the fields that requiring success authentication to be requested. The whole example could look like this: diff --git a/docs/directives/grpc.md b/docs/directives/grpc.md index a2ead524b5..74234d3800 100644 --- a/docs/directives/grpc.md +++ b/docs/directives/grpc.md @@ -142,7 +142,7 @@ Read about [n + 1](../N+1.md) to learn how to use the `batchKey` setting. ### onResponseBody -This hook allows you to intercept and modify the response body from upstream services before it's processed by Tailcall. Like [onRequest](/docs/directives.md#onrequest), it accepts a string value representing a middleware function defined in a JavaScript file. This function can be used to transform or validate the response data. +This hook allows you to intercept and modify the response body from upstream services before it's processed by Tailcall. Like [onRequest](/docs/directives/http.md#onrequest), it accepts a string value representing a middleware function defined in a JavaScript file. This function can be used to transform or validate the response data. ```graphql showLineNumbers type Query { diff --git a/docs/directives/http.md b/docs/directives/http.md index a0f57e99ec..6154472f1f 100644 --- a/docs/directives/http.md +++ b/docs/directives/http.md @@ -170,7 +170,7 @@ type Post { The `onRequest` property accepts a string value representing the remote function to be called every time an HTTP request is initiated. Typically the remote function is defined in a linked JavaScript worker file. :::note -For defining a request middleware globally for all requests, refer to the [upstream directive documentation](/docs/directives.md#onrequest-1). +For defining a request middleware globally for all requests, refer to the [upstream directive documentation](/docs/directives/upstream.md#onrequest). ::: ```graphql showLineNumbers @@ -186,7 +186,7 @@ type Query { ### onResponseBody -This hook allows you to intercept and modify the response body from upstream services before it's processed by Tailcall. Like [onRequest](/docs/directives.md#onrequest), it accepts a string value representing a middleware function defined in a JavaScript file. This function can be used to transform or validate the response data. +This hook allows you to intercept and modify the response body from upstream services before it's processed by Tailcall. Like [onRequest](#onrequest), it accepts a string value representing a middleware function defined in a JavaScript file. This function can be used to transform or validate the response data. ```graphql showLineNumbers type Query { diff --git a/docs/directives/modify.md b/docs/directives/modify.md index 31a9bad926..3b0f722eb2 100644 --- a/docs/directives/modify.md +++ b/docs/directives/modify.md @@ -32,5 +32,5 @@ type User { `@modify(omit: true)` instructs GraphQL to exclude the `id` field from the schema, making it inaccessible to the client. :::tip -`@omit` is a standalone directive and is an alias/shorthand for `modify(omit: true)` checkout [documentation](/docs/directives.md#omit-directive) +`@omit` is a standalone directive and is an alias/shorthand for `modify(omit: true)` checkout [documentation](/docs/directives/omit.md) ::: diff --git a/docs/directives/omit.md b/docs/directives/omit.md index 94eda1ca3c..720bab70a2 100644 --- a/docs/directives/omit.md +++ b/docs/directives/omit.md @@ -35,4 +35,4 @@ The `@omit` directive and `@modify(omit: true)` essentially serve the same purpo - `@omit` offers a concise way to directly exclude a field or node without additional arguments. -- `@modify(omit: true)`, as part of the broader [`@modify`](/docs/directives.md#omit-directive) directive, provides more options, such as field renaming through the `name` argument. This makes it a more flexible choice when you need more than field exclusion. +- `@modify(omit: true)`, as part of the broader [`@modify`](/docs/directives/modify.md#omit) directive, provides more options, such as field renaming through the `name` argument. This makes it a more flexible choice when you need more than field exclusion. diff --git a/docs/directives/protected.md b/docs/directives/protected.md index 2fe5ce9525..2a82a363fa 100644 --- a/docs/directives/protected.md +++ b/docs/directives/protected.md @@ -20,7 +20,7 @@ type ProtectedType @protected { ``` :::important -To utilize the `@protected` directive, you must link at least one authentication provider in the configuration using the [`@link`](#link-directive) directive (`Htpasswd` or `Jwks`). +To utilize the `@protected` directive, you must link at least one authentication provider in the configuration using the [`@link`](/docs/directives/link.md) directive (`Htpasswd` or `Jwks`). ::: ### How It Works diff --git a/docs/directives/telemetry.md b/docs/directives/telemetry.md index feb65740b8..6818e7147c 100644 --- a/docs/directives/telemetry.md +++ b/docs/directives/telemetry.md @@ -26,7 +26,7 @@ The `@telemetry` directive also captures the following metrics: | Metric | Description | | ------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------- | -| cache.hit_rate | Reflects the cache hit rate for the cache powered by the [`@cache`](#cache-directive) directive | +| cache.hit_rate | Reflects the cache hit rate for the cache powered by the [`@cache`](/docs/directives/cache.md) directive | | http.server.request.count | Counts the number of incoming requests made to specific route. Optionally enriched with selected headers by [`requestHeaders`](#requestheaders) | | http.client.request.count | Counts the number of outgoing requests to specific upstream | diff --git a/docs/grpc.md b/docs/grpc.md index 64b0b43267..4757eb6f25 100644 --- a/docs/grpc.md +++ b/docs/grpc.md @@ -123,7 +123,7 @@ type Query { } ``` -Also, let's specify options for Tailcall's ingress and egress at the beginning of the config using [`@server`](/docs/directives.md#server-directive) and [`@upstream`](/docs/directives/upstream.md) directives. +Also, let's specify options for Tailcall's ingress and egress at the beginning of the config using [`@server`](/docs/directives/server.md) and [`@upstream`](/docs/directives/upstream.md) directives. ```graphql schema @@ -142,7 +142,7 @@ To specify the protobuf file to read types from, use the `@link` directive with schema @link(id: "news", src: "./news.proto", type: Protobuf) ``` -Now you can connect GraphQL types to gRPC types. To do this you may want to explore more about [`@grpc` directive](/docs/directives.md#grpc-directive). Its usage is pretty straightforward and requires you to specify the path to a method that should be used to make a call. The method name will start with the package name, followed by the service name and the method name, all separated by the `.` symbol. +Now you can connect GraphQL types to gRPC types. To do this you may want to explore more about [`@grpc` directive](/docs/directives/grpc.md). Its usage is pretty straightforward and requires you to specify the path to a method that should be used to make a call. The method name will start with the package name, followed by the service name and the method name, all separated by the `.` symbol. If you need to provide any input to the gRPC method call you can specify it with the `body` option that allows you to specify a Mustache template and therefore it could use any input data like `args` and `value` to construct the body request. The body value is specified in the JSON format if you need to create the input manually and cannot use `args` input. @@ -235,7 +235,7 @@ Or Another important feature of the `@grpc` directive is that it allows you to implement request batching for remote data almost effortlessly as soon as you have gRPC methods that resolve multiple responses for multiple inputs in a single request. -In our protobuf example file, we have a method called `GetMultipleNews` that we can use. To enable batching we need to enable [`@upstream.batch` option](/docs/directives.md#batch) first and specify `batchKey` option for the `@grpc` directive. +In our protobuf example file, we have a method called `GetMultipleNews` that we can use. To enable batching we need to enable [`@upstream.batch` option](/docs/directives/upstream.md#batch) first and specify `batchKey` option for the `@grpc` directive. ```graphql schema diff --git a/docs/http-cache.md b/docs/http-cache.md index 860cbdc99c..fdd0dfc2c2 100644 --- a/docs/http-cache.md +++ b/docs/http-cache.md @@ -53,4 +53,4 @@ The combination of `httpCache` and `cacheControl` provides a comprehensive cachi These caching primitives are beneficial for REST APIs that are latency-sensitive, have a high rate of request repetition, or come with explicit caching headers indicating cacheable responses. Together, they tackle the common challenges of optimizing REST API performance by minimizing unnecessary network traffic and server load while ensuring response accuracy. -To further enhance the performance of any API with Tailcall, integrating the [`@cache`](/docs/directives.md#cache-directive) directive offers protocol agnostic control over caching at the field level within a GraphQL schema. +To further enhance the performance of any API with Tailcall, integrating the [`@cache`](/docs/directives/cache.md) directive offers protocol agnostic control over caching at the field level within a GraphQL schema. diff --git a/docs/http2.md b/docs/http2.md index 7369ead2ca..307910d12c 100644 --- a/docs/http2.md +++ b/docs/http2.md @@ -57,7 +57,7 @@ Use self-signed certificates for HTTP/2 configurations in development environmen ## Configuration -Once the certificate and key are generated we can link them with our main configuration using the [@link](/docs/directives.md#link-directive) directive, to enable HTTPS. +Once the certificate and key are generated we can link them with our main configuration using the [@link](/docs/directives/link.md) directive, to enable HTTPS. ```graphql showLineNumbers schema diff --git a/docs/rest.md b/docs/rest.md index a315736dab..c23bf49e3e 100644 --- a/docs/rest.md +++ b/docs/rest.md @@ -74,7 +74,7 @@ query ($id: Int!) @rest(method: GET, path: "/post/$id") { } ``` -to know more about the `@rest` directive, please refer to the [Tailcall GraphQL Directives](/docs/directives.md#rest-directive). +to know more about the `@rest` directive, please refer to the [Tailcall GraphQL Directives](/docs/directives/rest.md). ### Step 3: Link the operation to the main config file @@ -90,7 +90,7 @@ schema } ``` -To know more about the `@link` directive, please refer to the [Tailcall GraphQL Directives](/docs/directives.md#link-directive). +To know more about the `@link` directive, please refer to the [Tailcall GraphQL Directives](/docs/directives/link.md). #### Response diff --git a/docs/scripting.md b/docs/scripting.md index 82e0311403..bc9c051cd3 100644 --- a/docs/scripting.md +++ b/docs/scripting.md @@ -10,10 +10,10 @@ The runtime is not a full-fledged Node.js environment and has no access to the f ## Getting Started -To leverage this customization, JavaScript functions must be created in a JavaScript file and linked with the main configuration file using the [@link](./directives.md#link-directive) directive. There are two primary ways to achieve this: +To leverage this customization, JavaScript functions must be created in a JavaScript file and linked with the main configuration file using the [@link](/docs/directives/link.md) directive. There are two primary ways to achieve this: -1. Define an `onRequest` property with the JS function name in the [http](./directives.md#onrequest) directive. -2. Define it in the [upstream](./directives.md#onrequest-1) directive, which acts as a global middleware for all requests. +1. Define an `onRequest` property with the JS function name in the [http](/docs/directives/http.md#onrequest) directive. +2. Define it in the [upstream](/docs/directives/upstream.md#onrequest) directive, which acts as a global middleware for all requests. :::tip If you specify a `onRequest` handler for both `http` and `upstream` the `http` one will always take precedence over the global `onRequest` handler. @@ -29,7 +29,7 @@ function foo({request}) { } ``` -Once you have a worker file ready, link that file to the GraphQL configuration using the [`@link`](./directives.md#link-directive) directive and define the [onRequest](./directives.md#onrequest-1) property. +Once you have a worker file ready, link that file to the GraphQL configuration using the [`@link`](/docs/directives/link.md) directive and define the [onRequest](/docs/directives/upstream.md#onrequest) property. ```graphql schema @@ -123,7 +123,7 @@ type Request = { } ``` -By default the headers field will be empty in most cases, unless headers are whitelisted via the [allowedHeaders](/docs/directives.md#allowedheaders) setting in [`@upstream`](/docs/directives/upstream.md). +By default the headers field will be empty in most cases, unless headers are whitelisted via the [allowedHeaders](/docs/directives/upstream.md#allowedheaders) setting in [`@upstream`](/docs/directives/upstream.md). The http filter doesn't have access to the request's body, hence you can't directly modify the body of an outgoing request. This is more of a design choice than a limitation we have made to ensure that developers don't misuse this API to write all kind of business logic in Tailcall. diff --git a/docs/telemetry.md b/docs/telemetry.md index 315fdd06bf..34b3aa3c2c 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -126,7 +126,7 @@ Where Tailcall is a part of whole distributed trace ### Customize generated data -In some cases you may want to customize the data that were added to telemetry payload to have more control over analyzing process. Tailcall supports that customization for specific use cases described below. For eg. the metric [`http.server.request.count`](/docs/directives.md#metrics) can be customized with the [`requestHeaders`](/docs/directives.md#requestheaders) property to allow splitting the overall count by specific headers. +In some cases you may want to customize the data that were added to telemetry payload to have more control over analyzing process. Tailcall supports that customization for specific use cases described below. For eg. the metric [`http.server.request.count`](/docs/directives/telemetry.md#metrics) can be customized with the [`requestHeaders`](/docs/directives/telemetry.md#requestheaders) property to allow splitting the overall count by specific headers. :::important The value of specified headers will be sent to telemetry backend as is, so use it with care to prevent of leaking any sensitive data to third-party services you don't have control over. From 4544f566d7289aabbeb7d818c718d7424135a1e1 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Wed, 25 Sep 2024 18:50:53 +0530 Subject: [PATCH 09/12] update titles of some pages --- docs/directives/add-field.md | 2 +- docs/directives/cache.md | 4 ++-- docs/directives/call.md | 6 +++--- docs/directives/expr.md | 2 +- docs/directives/graphql.md | 4 ++-- docs/directives/grpc.md | 4 ++-- docs/directives/http.md | 2 +- docs/directives/js.md | 2 +- docs/directives/link.md | 2 +- docs/directives/modify.md | 2 +- docs/directives/omit.md | 2 +- docs/directives/protected.md | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/directives/add-field.md b/docs/directives/add-field.md index 83beec14c9..61f031d97e 100644 --- a/docs/directives/add-field.md +++ b/docs/directives/add-field.md @@ -1,5 +1,5 @@ --- -title: Adding Custom Fields with the @addField Directive +title: Adding Custom Fields description: Simplify your GraphQL schema using the @addField directive to flatten nested fields and streamline data queries for better performance. slug: add-field-directive sidebar_label: Adding Custom Fields diff --git a/docs/directives/cache.md b/docs/directives/cache.md index 9dfc7c1d99..25b0e1f401 100644 --- a/docs/directives/cache.md +++ b/docs/directives/cache.md @@ -1,8 +1,8 @@ --- -title: Optimizing GraphQL with the @cache Directive +title: Caching on GraphQL description: Enhance GraphQL performance by caching field results using the @cache directive, specifying caching durations and optimizing data fetching. slug: cache-directive -sidebar_label: Caching Fields +sidebar_label: GraphQL Caching --- The `@cache` directive provides a protocol agnostic mechanism for caching the results of fields within a GraphQL schema. Like any other cache implementation, this feature is useful for optimizing performance by reducing the need to fetch data that doesn't change frequently. diff --git a/docs/directives/call.md b/docs/directives/call.md index 73819db481..62c6c376e2 100644 --- a/docs/directives/call.md +++ b/docs/directives/call.md @@ -1,8 +1,8 @@ --- -title: Efficient Schema Refactoring with the @call Directive in GraphQL -description: Leverage the @call directive to reduce redundancy in GraphQL schemas by composing queries and mutations, streamlining operations and enhancing code maintainability. +title: Composing GraphQL operations +description: Leverage the @call directive to reduce duplication in GraphQL schemas by composing queries and mutations, streamlining operations and enhancing code maintainability. slug: call-directive -sidebar_label: Function Invocations +sidebar_label: Operation Composition --- The `@call` directive in GraphQL signifies a shift towards more efficient configuration management by introducing a methodology akin to function invocations in conventional programming. This directive is pivotal for developers navigating the intricacies of elaborate GraphQL schemas, where minimizing redundancy and adhering to the DRY (Don't Repeat Yourself) principle are paramount. Consider the following schema example: diff --git a/docs/directives/expr.md b/docs/directives/expr.md index 5785583584..e66eb2df31 100644 --- a/docs/directives/expr.md +++ b/docs/directives/expr.md @@ -1,5 +1,5 @@ --- -title: Enhancing GraphQL Responses with the @expr Directive +title: Embedding values in GraphQL Responses description: Learn how to use the @expr directive in GraphQL for embedding data into your schema, providing flexibility and efficiency for custom responses. slug: expr-directive sidebar_label: Embedding Data diff --git a/docs/directives/graphql.md b/docs/directives/graphql.md index c32022e040..50355f799d 100644 --- a/docs/directives/graphql.md +++ b/docs/directives/graphql.md @@ -1,8 +1,8 @@ --- -title: Efficient Data Fetching with GraphQL Directive +title: Integrating with other GraphQL APIs description: Streamline data retrieval from upstream GraphQL APIs using the @graphQL directive for enhanced schema efficiency. slug: graphQL-directive -sidebar_label: GraphQL API Fetching +sidebar_label: GraphQL Upstream --- The `@graphQL` directive allows to specify GraphQL API server request to fetch data from. diff --git a/docs/directives/grpc.md b/docs/directives/grpc.md index 74234d3800..8118c49f73 100644 --- a/docs/directives/grpc.md +++ b/docs/directives/grpc.md @@ -1,8 +1,8 @@ --- -title: Integrating gRPC Services +title: Integrating with gRPC APIs description: Explore how the @grpc directive facilitates the resolution of GraphQL fields using gRPC services, enabling efficient data fetching and seamless integration of gRPC in your GraphQL schema. slug: grpc-directive -sidebar_label: gRPC Integration +sidebar_label: gRPC Upstream --- The `@grpc` directive enables the resolution of GraphQL fields via gRPC services. Below is an illustrative example of how to apply this directive within a GraphQL schema: diff --git a/docs/directives/http.md b/docs/directives/http.md index 6154472f1f..b0786bf795 100644 --- a/docs/directives/http.md +++ b/docs/directives/http.md @@ -1,5 +1,5 @@ --- -title: Using @http Directive +title: Integrating with REST APIs description: Learn how to leverage the @http directive in GraphQL to interact with REST APIs, including customizable request methods, dynamic paths, and query parameters for efficient data fetching. slug: http-directive sidebar_label: HTTP Directive diff --git a/docs/directives/js.md b/docs/directives/js.md index 0c440b108d..a2756fec31 100644 --- a/docs/directives/js.md +++ b/docs/directives/js.md @@ -1,5 +1,5 @@ --- -title: Custom Field Resolvers with @js Directive +title: Writing custom resolvers using Javascript description: Discover how to utilize the @js directive in GraphQL to incorporate JavaScript functions for custom data transformations and complex field resolutions, enhancing the flexibility of your API. slug: js-directive sidebar_label: JavaScript Field Resolution diff --git a/docs/directives/link.md b/docs/directives/link.md index f1d6025faf..eca1815210 100644 --- a/docs/directives/link.md +++ b/docs/directives/link.md @@ -1,5 +1,5 @@ --- -title: Integrating External Resources with Link Directive +title: Integrating External Resources description: Explore how to use the @link directive in GraphQL to integrate external resources like configurations, Protobuf files, and scripts into your schema, enhancing flexibility and modularity. slug: link-directive sidebar_label: Linking Resources diff --git a/docs/directives/modify.md b/docs/directives/modify.md index 3b0f722eb2..3f35b882ff 100644 --- a/docs/directives/modify.md +++ b/docs/directives/modify.md @@ -1,5 +1,5 @@ --- -title: Altering Schema Attributes with Modify Directive +title: Altering fields in the GraphQL Schema description: Learn how to use the @modify directive in GraphQL to alter the fields in your schema, allowing for better alignment between your data source and GraphQL API design. slug: modify-directive sidebar_label: Modifying Fields diff --git a/docs/directives/omit.md b/docs/directives/omit.md index 720bab70a2..aa9b615e39 100644 --- a/docs/directives/omit.md +++ b/docs/directives/omit.md @@ -1,5 +1,5 @@ --- -title: Excluding Fields with Omit Directive +title: Excluding Fields description: Utilize the @omit directive in GraphQL to exclude specific fields from your schema, enhancing API security and simplifying client interactions by hiding unnecessary data. slug: omit-directive sidebar_label: Omitting Fields diff --git a/docs/directives/protected.md b/docs/directives/protected.md index 2a82a363fa..19b523f925 100644 --- a/docs/directives/protected.md +++ b/docs/directives/protected.md @@ -1,5 +1,5 @@ --- -title: Enforcing Authentication with Protected Directive +title: Enforcing Authentication description: The @protected directive restricts access to specific fields or types in your GraphQL schema, ensuring that only authenticated users can retrieve sensitive data. slug: protected-directive sidebar_label: Protected Fields From 36643d7d5d0509ecc836841b60699ab4eab9235a Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Wed, 25 Sep 2024 15:20:31 +0000 Subject: [PATCH 10/12] resolved comments --- docs/apollo-studio.md | 2 +- docs/auth.md | 33 ++++- docs/cli.md | 2 +- docs/client-tuning.md | 255 +++++++++++++++++++++++++++++++++- docs/config-generation.md | 6 +- docs/directives.md | 32 ++--- docs/directives/graphql.md | 2 +- docs/directives/http.md | 4 +- docs/directives/protected.md | 29 ---- docs/directives/rest.md | 54 -------- docs/directives/telemetry.md | 143 ------------------- docs/directives/upstream.md | 257 ----------------------------------- docs/grpc.md | 4 +- docs/rest.md | 52 ++++++- docs/scripting.md | 6 +- docs/telemetry.md | 145 +++++++++++++++++++- docusaurus.config.ts | 4 +- sidebars.ts | 4 - 18 files changed, 507 insertions(+), 527 deletions(-) delete mode 100644 docs/directives/protected.md delete mode 100644 docs/directives/rest.md delete mode 100644 docs/directives/telemetry.md delete mode 100644 docs/directives/upstream.md diff --git a/docs/apollo-studio.md b/docs/apollo-studio.md index 7d96bd18b0..46a3e88dff 100644 --- a/docs/apollo-studio.md +++ b/docs/apollo-studio.md @@ -34,7 +34,7 @@ This guide illustrates how to configure `tailcall` to send usage metrics to [Apo ![local-schema.png](../static/images/apollo-studio/local-schema.png) -You have now created a Monolith graph in Apollo Studio. The next step is to configure `tailcall` to use the `APOLLO_API_KEY` and `APOLLO_GRAPH_REF`. Follow detailed instructions [here](/docs/directives/telemetry.md). +You have now created a Monolith graph in Apollo Studio. The next step is to configure `tailcall` to use the `APOLLO_API_KEY` and `APOLLO_GRAPH_REF`. Follow detailed instructions [here](/docs/telemetry.md). ## Checking the metrics in Apollo Studio diff --git a/docs/auth.md b/docs/auth.md index 8ed5f4cdbb..18069b6fef 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -42,12 +42,37 @@ to know more about how to use it, read the following articles: 1. [Basic Auth](#basic-authentication) 2. [JWT](#jwt-authentication) +## @protected Directive + +The `@protected` annotation designates a type or field as protected, meaning that a user must be authenticated to access that data. + +```graphql +type Query { + protected: String! @protected + protectedType: ProtectedType +} + +type ProtectedType @protected { + name: String! + nested: String! +} +``` + +:::important +To utilize the `@protected` directive, you must link at least one authentication provider in the configuration using the [`@link`](/docs/directives/link.md) directive (`Htpasswd` or `Jwks`). +::: + +### How It Works + +- When a field is annotated with `@protected`, an authentication check is performed upon receiving the request. Depending on the authentication result, either the requested data is provided in the response, or an authentication error is returned. +- If a type is annotated with `@protected`, all fields within that type inherit the protection, requiring user authentication for any field that's queried. + ## GraphQL Configuration Enabling support for authentication in Tailcall could be done in two steps: 1. With the help of [`@link` directive](/docs/directives/link.md) connect multiple authentication files as you need for different provides. To connect it use either [`Htpasswd`](/docs/directives/link.md#htpasswd) or [`Jwks`](/docs/directives/link.md#jwks) link type -2. Mark that some type of field requires authentication to be fetched with the help of [`@protected` directive](/docs/directives/protected.md) +2. Mark that some type of field requires authentication to be fetched with the help of [`@protected` directive](/docs/auth.md#protected-directive) Your config could look like this now: @@ -229,7 +254,7 @@ In case you linked multiple authentication files all of them will be used to exe ### Authentication headers -To validate authentication for user request the specific headers are used (like `Authorization` header). In case auth is enabled for tailcall those headers will be also added to the [`allowedHeaders` list](/docs/directives/upstream.md#allowedheaders) and therefore they will be forwarded to the upstream requests implicitly. +To validate authentication for user request the specific headers are used (like `Authorization` header). In case auth is enabled for tailcall those headers will be also added to the [`allowedHeaders` list](/docs/client-tuning.md#allowedheaders) and therefore they will be forwarded to the upstream requests implicitly. ## Basic Authentication @@ -257,7 +282,7 @@ We can use that file as an example for it that has data for `testuser:mypassword testuser:$2y$10$wJ/mZDURcAOBIrswCAKFsO0Nk7BpHmWl/XuhF7lNm3gBAFH3ofsuu ``` -After adding `@link` you can use the [`@protected` directive](/docs/directives/protected.md) to mark the fields that requiring success authentication to be requested. +After adding `@link` you can use the [`@protected` directive](/docs/auth.md#protected-directive) to mark the fields that requiring success authentication to be requested. The whole example could look like this: @@ -349,7 +374,7 @@ We can use that file as an example for it: } ``` -After adding `@link` you can use the [`@protected` directive](/docs/directives/protected.md) to mark the fields that requiring success authentication to be requested. +After adding `@link` you can use the [`@protected` directive](/docs/auth.md#protected-directive) to mark the fields that requiring success authentication to be requested. The whole example could look like this: diff --git a/docs/cli.md b/docs/cli.md index 3c6fca26d6..7f3bae9cca 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -430,7 +430,7 @@ preset: } ``` -2. **consolidateURL:** The setting identifies the most common base URL among multiple REST endpoints and uses this URL in the [upstream](/docs/directives/upstream.md) directive. It takes a threshold value between 0.0 and 1.0 to determine the most common endpoint. The default is `0.5`. +2. **consolidateURL:** The setting identifies the most common base URL among multiple REST endpoints and uses this URL in the [upstream](/docs/client-tuning.md#upstream-directive) directive. It takes a threshold value between 0.0 and 1.0 to determine the most common endpoint. The default is `0.5`. For example, if the `Query` type has three base URLs, using the `consolidateURL` setting with a `0.5` threshold will pick the base URL that is used in more than 50% of the [http](/docs/directives/http.md) directives, `http://jsonplaceholder.typicode.com`, and add it to the upstream, cleaning the base URLs from the `Query` type. diff --git a/docs/client-tuning.md b/docs/client-tuning.md index 1d305904ae..f868b6da7f 100644 --- a/docs/client-tuning.md +++ b/docs/client-tuning.md @@ -48,9 +48,262 @@ Learn more about QUIC in detail [here](https://blog.cloudflare.com/the-road-to-q Connection pooling mitigates these issues by reusing existing connections for requests, reducing connection establishment frequency (and thus handshake overhead) and conserving client-side ports. This approach enhances application performance by minimizing the resources and time spent on managing connections. +## @upstream Directive + +The `upstream` directive enables control over specific aspects of the upstream server connection, including settings such as connection timeouts, keep-alive intervals, and more. The system applies default values if you do not specify them. + +```graphql showLineNumbers +schema @upstream(...[UpstreamSetting]...){ + query: Query + mutation: Mutation +} +``` + +The document below details the options for `UpstreamSetting`. + +### poolIdleTimeout + +The connection pool waits for this duration in seconds before closing idle connections. + +```graphql showLineNumbers +schema + @upstream( + poolIdleTimeout: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### poolMaxIdlePerHost + +The max number of idle connections each host will maintain. + +```graphql showLineNumbers +schema + @upstream( + poolMaxIdlePerHost: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### keepAliveInterval + +The time in seconds between each keep-alive message sent to maintain the connection. + +```graphql showLineNumbers +schema + @upstream( + keepAliveInterval: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### keepAliveTimeout + +The time in seconds that the connection will wait for a keep-alive message before closing. + +```graphql showLineNumbers +schema + @upstream( + keepAliveTimeout: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### keepAliveWhileIdle + +A boolean value that determines whether to send keep-alive messages while the connection is idle. + +```graphql showLineNumbers +schema + @upstream( + keepAliveWhileIdle: false + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### proxy + +The `proxy` setting defines an intermediary server that routes upstream requests before they reach their intended endpoint. By specifying a proxy URL, you introduce a layer, enabling custom routing and security policies. + +```graphql showLineNumbers +schema + @upstream( + proxy: {url: "http://localhost:3000"} + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +In the provided example, we've set the proxy's `url` to "http://localhost:3000". This configuration ensures that all requests aimed at the designated `baseURL` first go through this proxy. To illustrate, if the `baseURL` is "http://jsonplaceholder.typicode.com", any request targeting it initially goes to "http://localhost:3000" before the proxy redirects it to its final destination. + +### connectTimeout + +The time in seconds that the connection will wait for a response before timing out. + +```graphql showLineNumbers +schema + @upstream( + connectTimeout: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### timeout + +The max time in seconds that the connection will wait for a response. + +```graphql showLineNumbers +schema + @upstream( + timeout: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### tcpKeepAlive + +The time in seconds between each TCP keep-alive message sent to maintain the connection. + +```graphql showLineNumbers +schema + @upstream( + tcpKeepAlive: 60 + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### userAgent + +The User-Agent header value for HTTP requests. + +```graphql showLineNumbers +schema + @upstream( + userAgent: "Tailcall/1.0" + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +### allowedHeaders + +The `allowedHeaders` configuration defines a set of whitelisted HTTP headers that can be forwarded to upstream services during requests. +Without specifying `allowedHeaders`, the system will not forward any incoming headers to upstream services, offering an extra security layer but potentially limiting necessary data flow. Tailcall compares the provided whitelisted headers in a case-insensitive format. + +```graphql showLineNumbers +schema + @upstream( + allowedHeaders: ["Authorization", "X-Api-Key"] + ) { + query: Query + mutation: Mutation +} +``` + +In the example above, the configuration for `allowedHeaders` permits `Authorization` and `X-Api-Key` headers. Thus, requests with these headers will forward them to upstream services; the system ignores all others. This configuration ensures communication of the expected headers to dependent services, emphasizing security and consistency. + +### baseURL + +This refers to the default base URL for your APIs. If it's not explicitly mentioned in the `@upstream` directive, then each [`@http`](/docs/directives/http.md) directive must specify its own `baseURL`. If neither `@upstream` nor [`@http`](/docs/directives/http.md) provides a `baseURL`, it results in a compilation error. + +```graphql showLineNumbers +schema + @upstream( + baseURL: "http://jsonplaceholder.typicode.com" + ) { + query: Query + mutation: Mutation +} +``` + +In this representation, `http://jsonplaceholder.typicode.com` serves as the `baseURL`. Thus, all API calls made by `@http` prepend this URL to their respective paths. + +:::tip +Ensure that your base URL remains free from specific path segments. + +- **GOOD:** `@upstream(baseURL: http://jsonplaceholder.typicode.com)` +- **BAD:** `@upstream(baseURL: http://jsonplaceholder.typicode.com/api)` + +::: + +### httpCache + +When httpCache passed with value greater than 0 it directs Tailcall to use HTTP caching mechanisms, following the [HTTP Caching RFC](https://tools.ietf.org/html/rfc7234) to enhance performance by minimizing unnecessary data fetches. If left unspecified, this feature defaults to `0` disabling the caching mechanism. + +```graphql showLineNumbers +schema @upstream(httpCache: 42) { + query: Query + mutation: Mutation +} +``` + +### Tips + +- Use batching when other optimization techniques fail to resolve performance issues. +- Apply batching and thoroughly assess its impact. +- Understand that batching may make debugging more challenging. + +### batch + +An object that specifies the batch settings, including `maxSize` (the max size of the batch), `delay` (the delay in milliseconds between each batch), and `headers` (an array of HTTP headers that the batch will include). + +```graphql showLineNumbers +schema + @upstream( + batch: { + maxSize: 1000 + delay: 10 + headers: ["X-Server", "Authorization"] + } + ) { + query: Query + mutation: Mutation +} +``` + +### onRequest + +Similar to the [@http](/docs/directives/http.md) property, this accepts a string value representing a middleware function defined in a JavaScript file. It intercepts all outgoing HTTP requests from the server. This interceptor, written in JavaScript, can be used to modify outgoing requests and also generate artificial responses to customize the behavior of the GraphQL server. + +```graphql showLineNumbers +schema @upstream(onRequest: 'someFunctionName') +@link(type: Script, src: "path_to/worker.js") { + query: Query + mutation: Mutation +} +``` + ## Tuning HTTP Client -Tailcall uses connection pooling by default and sets up with default tuning suitable for most use cases. You might need to further tune the HTTP client to improve your application's performance. Tailcall DSL provides a directive named [`@upstream`](/docs/directives/upstream.md) for this purpose. +Tailcall uses connection pooling by default and sets up with default tuning suitable for most use cases. You might need to further tune the HTTP client to improve your application's performance. Tailcall DSL provides a directive named [`@upstream`](/docs/client-tuning.md#upstream-directive) for this purpose. :::note Connection pooling optimizes HTTP/1. Since HTTP/2 and HTTP/3 support multiplexing, pooling enabled does not noticeably affect performance. diff --git a/docs/config-generation.md b/docs/config-generation.md index 1bf6637a8c..1affc8f7bd 100644 --- a/docs/config-generation.md +++ b/docs/config-generation.md @@ -697,7 +697,7 @@ This setting merges types in the configuration that satisfy the threshold criter ### consolidateURL -The setting identifies the most common base URL among multiple REST endpoints and uses this URL in the [upstream](/docs/directives/upstream.md) directive. It takes a threshold value between 0.0 and 1.0 to determine the most common endpoint. The default is `0.5`. +The setting identifies the most common base URL among multiple REST endpoints and uses this URL in the [upstream](/docs/client-tuning.md#upstream-directive) directive. It takes a threshold value between 0.0 and 1.0 to determine the most common endpoint. The default is `0.5`. For example, if the `Query` type has three base URLs, using the `consolidateURL` setting with a `0.5` threshold will pick the base URL that is used in more than 50% of the [http](/docs/directives/http.md) directives, `http://jsonplaceholder.typicode.com`, and add it to the upstream, cleaning the base URLs from the `Query` type. @@ -950,7 +950,7 @@ When setting up your configuration file for GraphQL generation with Tailcall, co -2. **[Consolidate URL](config-generation.md#consolidateurl)**: Identifies the most common base URL among multiple REST endpoints and uses it in the [@upstream](/docs/directives/upstream.md) directive. Set a threshold (0.0 to 1.0) to determine when to consolidate URLs. Recommended threshold is anything above `0.5`. +2. **[Consolidate URL](config-generation.md#consolidateurl)**: Identifies the most common base URL among multiple REST endpoints and uses it in the [@upstream](/docs/client-tuning.md#upstream-directive) directive. Set a threshold (0.0 to 1.0) to determine when to consolidate URLs. Recommended threshold is anything above `0.5`. ```json showLineNumbers @@ -1043,7 +1043,7 @@ curl: **Q. What if I have multiple REST endpoints with different base URLs?** -**Answer:** Use the [consolidateURL](config-generation.md#consolidateurl) parameter to identify the most common base URL among multiple REST endpoints and it will automatically select the most common base url and add it to the [@upstream](/docs/directives/upstream.md) directive. Here is an example: +**Answer:** Use the [consolidateURL](config-generation.md#consolidateurl) parameter to identify the most common base URL among multiple REST endpoints and it will automatically select the most common base url and add it to the [@upstream](/docs/client-tuning.md#upstream-directive) directive. Here is an example: diff --git a/docs/directives.md b/docs/directives.md index b95db3a34b..edd1ad80e5 100644 --- a/docs/directives.md +++ b/docs/directives.md @@ -12,19 +12,19 @@ Here is a list of all the custom directives supported by Tailcall: -| Operator | Description | -| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | -| [`@addField`](/docs/directives/add-field-directive/) | Simplifies data structures and queries by adding, inlining, or flattening fields or nodes within the schema. | -| [`@cache`](/docs/directives/cache-directive/) | Enables caching for the query, field or type applied to. | -| [`@call`](/docs/directives/call-directive/) | Invokes a query or mutation from another query or mutation field. | -| [`@expr`](/docs/directives/expr-directive/) | Allows embedding of a constant response within the schema. | -| [`@graphQL`](/docs/directives/graphQL-directive/) | Resolves a field or node by a GraphQL API. | -| [`@grpc`](/docs/directives/grpc-directive/) | Resolves a field or node by a gRPC API. | -| [`@http`](/docs/directives/http-directive/) | Resolves a field or node by a REST API. | -| [`@link`](/docs/directives/link-directive/) | Imports external resources such as config files, certs, protobufs, etc in the schema. | -| [`@modify`](/docs/directives/modify-directive/) | Enables changes to attributes of fields or nodes in the schema. | -| [`@omit`](/docs/directives/omit-directive/) | Excludes fields or nodes from the generated schema, making them inaccessible through the GraphQL API. | -| [`@rest`](/docs/directives/rest-directive/) | Allows exposing REST endpoints on top of GraphQL. | -| [`@server`](/docs/directives/server-directive/) | Provides server configurations for behavior tuning and tailcall optimization in specific use-cases. | -| [`@telemetry`](/docs/directives/telemetry-directive/) | Integrates with open-telemetry to provide observability of the running tailcall service. | -| [`@upstream`](/docs/directives/upstream-directive/) | Controls aspects of the upstream server connection, including timeouts and keep-alive settings. | +| Operator | Description | +| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| [`@addField`](/docs/directives/add-field-directive/) | Simplifies data structures and queries by adding, inlining, or flattening fields or nodes within the schema. | +| [`@cache`](/docs/directives/cache-directive/) | Enables caching for the query, field or type applied to. | +| [`@call`](/docs/directives/call-directive/) | Invokes a query or mutation from another query or mutation field. | +| [`@expr`](/docs/directives/expr-directive/) | Allows embedding of a constant response within the schema. | +| [`@graphQL`](/docs/directives/graphQL-directive/) | Resolves a field or node by a GraphQL API. | +| [`@grpc`](/docs/directives/grpc-directive/) | Resolves a field or node by a gRPC API. | +| [`@http`](/docs/directives/http-directive/) | Resolves a field or node by a REST API. | +| [`@link`](/docs/directives/link-directive/) | Imports external resources such as config files, certs, protobufs, etc in the schema. | +| [`@modify`](/docs/directives/modify-directive/) | Enables changes to attributes of fields or nodes in the schema. | +| [`@omit`](/docs/directives/omit-directive/) | Excludes fields or nodes from the generated schema, making them inaccessible through the GraphQL API. | +| [`@rest`](/docs/rest.md#rest-directive) | Allows exposing REST endpoints on top of GraphQL. | +| [`@server`](/docs/directives/server-directive/) | Provides server configurations for behavior tuning and tailcall optimization in specific use-cases. | +| [`@telemetry`](/docs/telemetry.md#telemetry-directive) | Integrates with open-telemetry to provide observability of the running tailcall service. | +| [`@upstream`](/docs/client-tuning.md#upstream-directive) | Controls aspects of the upstream server connection, including timeouts and keep-alive settings. | diff --git a/docs/directives/graphql.md b/docs/directives/graphql.md index 50355f799d..4511fafb8d 100644 --- a/docs/directives/graphql.md +++ b/docs/directives/graphql.md @@ -39,7 +39,7 @@ query { ### baseURL -This refers to the base URL of the API. If not specified, the default base URL is the one specified in the [`@upstream`](/docs/directives/upstream.md) directive. +This refers to the base URL of the API. If not specified, the default base URL is the one specified in the [`@upstream`](/docs/client-tuning.md#upstream-directive) directive. ```graphql showLineNumbers type Query { diff --git a/docs/directives/http.md b/docs/directives/http.md index b0786bf795..70bf16524e 100644 --- a/docs/directives/http.md +++ b/docs/directives/http.md @@ -17,7 +17,7 @@ In this example, adding the `@http` directive to the `users` field of the `Query ### baseURL -Specifies the API's base URL. If unspecified, it defaults to the URL in the [`@upstream`](/docs/directives/upstream.md) directive. +Specifies the API's base URL. If unspecified, it defaults to the URL in the [`@upstream`](/docs/client-tuning.md#upstream-directive) directive. ```graphql showLineNumbers type Query { @@ -170,7 +170,7 @@ type Post { The `onRequest` property accepts a string value representing the remote function to be called every time an HTTP request is initiated. Typically the remote function is defined in a linked JavaScript worker file. :::note -For defining a request middleware globally for all requests, refer to the [upstream directive documentation](/docs/directives/upstream.md#onrequest). +For defining a request middleware globally for all requests, refer to the [upstream directive documentation](/docs/client-tuning.md#onrequest). ::: ```graphql showLineNumbers diff --git a/docs/directives/protected.md b/docs/directives/protected.md deleted file mode 100644 index 19b523f925..0000000000 --- a/docs/directives/protected.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Enforcing Authentication -description: The @protected directive restricts access to specific fields or types in your GraphQL schema, ensuring that only authenticated users can retrieve sensitive data. -slug: protected-directive -sidebar_label: Protected Fields ---- - -The `@protected` annotation designates a type or field as protected, meaning that a user must be authenticated to access that data. - -```graphql -type Query { - protected: String! @protected - protectedType: ProtectedType -} - -type ProtectedType @protected { - name: String! - nested: String! -} -``` - -:::important -To utilize the `@protected` directive, you must link at least one authentication provider in the configuration using the [`@link`](/docs/directives/link.md) directive (`Htpasswd` or `Jwks`). -::: - -### How It Works - -- When a field is annotated with `@protected`, an authentication check is performed upon receiving the request. Depending on the authentication result, either the requested data is provided in the response, or an authentication error is returned. -- If a type is annotated with `@protected`, all fields within that type inherit the protection, requiring user authentication for any field that's queried. diff --git a/docs/directives/rest.md b/docs/directives/rest.md deleted file mode 100644 index 26b71e7a70..0000000000 --- a/docs/directives/rest.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Integrating REST APIs -description: The @rest directive enables GraphQL to orchestrate RESTful services by mapping queries to REST endpoints, facilitating seamless integration with existing API infrastructures. -slug: rest-directive -sidebar_label: REST Integration ---- - -API orchestration is essential, yet not all can adopt GraphQL despite its benefits. The Tailcall DSL feature leverages GraphQL at compile time to generate REST endpoints, aligning with traditional API infrastructure like CDNs and Gateways. - -### Usage - -- **method**: Specifies the HTTP method (GET, POST, etc.). -- **path**: Sets the endpoint URL, with support for dynamic values from query arguments. -- **query**: Defines the query parameters as key-value pairs. - -### Example - -Define GraphQL types and queries, using the `@rest` directive to map fields to REST API endpoints. - -`schema.graphql` - -```graphql -schema - @upstream(baseURL: "https://jsonplaceholder.typicode.com") - @link(type: Operation, src: "user-operation.graphql") { - query: Query -} - -type Query { - user(id: Int!): User - @rest(method: "GET", path: "/users/{{.args.id}}") -} - -type User { - id: Int! - name: String! - email: String! -} -``` - -`user-operation.graphql` - -```graphql -query ($id: Int!) @rest(method: GET, path: "/user/$id") { - user(id: $id) { - id - name - } -} -``` - -![REST Demo](/images/docs/rest-user.png) - -This example demonstrates how to define a simple query to fetch user data from a REST endpoint using the `@rest` directive. By leveraging `@rest`, GraphQL can serve as a layer over RESTful services, combining REST's simplicity with GraphQL's flexibility. diff --git a/docs/directives/telemetry.md b/docs/directives/telemetry.md deleted file mode 100644 index 6818e7147c..0000000000 --- a/docs/directives/telemetry.md +++ /dev/null @@ -1,143 +0,0 @@ ---- -title: Enhancing Observability with @telemetry -description: Enhance the observability of your GraphQL services with the @telemetry directive, enabling seamless integration with OpenTelemetry. -slug: telemetry-directive -sidebar_label: Telemetry Configuration ---- - -The `@telemetry` directive facilitates seamless integration with [OpenTelemetry](https://open-telemetry.io), enhancing the observability of your GraphQL services powered by Tailcall. By leveraging this directive, developers gain access to valuable insights into the performance and behavior of their applications. - -### Traces - -Here are the traces that are captured by the `@telemetry` directive: - -| Trace Name | Description | -| --------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| request | Captures the span for processing the HTTP request on the server side, providing foundational observability. | -| graphQL | _Only for GraphQL ingress_. Span for processing GraphQL call | -| `REST ` | _Only for REST ingress_. Span for processing REST API call | -| `` | Denotes spans for fields with defined resolvers, offering insights into field names and execution times for resolver logic. | -| `` | Nested within the `` spans, these granulated spans detail the execution of expressions in resolving a field, highlighting the hierarchical execution pattern of nested expressions. | -| upstream_request | Request that were made from tailcall service to upstream | - -### Metrics - -The `@telemetry` directive also captures the following metrics: - -| Metric | Description | -| ------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------- | -| cache.hit_rate | Reflects the cache hit rate for the cache powered by the [`@cache`](/docs/directives/cache.md) directive | -| http.server.request.count | Counts the number of incoming requests made to specific route. Optionally enriched with selected headers by [`requestHeaders`](#requestheaders) | -| http.client.request.count | Counts the number of outgoing requests to specific upstream | - -### export - -The `export` field defines how the open-telemetry data should be exported and in which format. The following are the supported formats: - -### otlp - -Utilizes the OTLP format to export telemetry data to backend systems, supported by most modern tracing and analytics platforms. Here is an example using [honeycomb.io]: - -[honecomb.io]: https://www.honeycomb.io/ - -```graphql -schema - @telemetry( - export: { - otlp: { - url: "https://api.honeycomb.io:443" - headers: [ - { - key: "x-honeycomb-team" - value: "{{.env.HONEYCOMB_API_KEY}}" - } - {key: "x-honeycomb-dataset", value: "tailcall"} - ] - } - } - ) { - query: Query -} -``` - -You can configure the OTLP exporter with the following options: - -| Field | Description | -| ------: | ----------------------------------------------------------- | -| url | Defines the URL for the OTLP Collector. | -| headers | Sets additional headers for requests to the OTLP Collector. | - -### prometheus - -Facilitates metrics export in a Prometheus compatible format, providing a dedicated endpoint for metrics. - -```graphql -schema - @telemetry(export: {prometheus: {path: "/metrics"}}) { - query: Query -} -``` - -You can configure the Prometheus exporter with the following options: - -| Field | Description | -| -----: | ---------------------------------------------------------------------------------- | -| path | Designates the endpoint path for Prometheus metrics, defaulting to `/metrics`. | -| format | Controls the format viz. **text** or **protobuf**, for sending data to Prometheus. | - -### stdout - -Outputs all telemetry data to stdout, ideal for testing or local development environments. - -```graphql -schema @telemetry(export: {stdout: {pretty: true}}) { - query: Query -} -``` - -You can configure the stdout exporter with the following options: - -| Field | Description | -| -----: | -------------------------------------------------------------------- | -| pretty | Enables formatted output of telemetry data for enhanced readability. | - -### requestHeaders - -Specifies list of headers of ingress request the value of which will be sent to the telemetry as attributes. - -```graphql -schema @telemetry(requestHeaders: ["X-User-Id"]) { - query: Query -} -``` - -### apollo - -Facilitates seamless integration with [Apollo Studio](https://studio.apollographql.com/), enhancing the observability of GraphQL services. By leveraging this field, developers gain access to valuable insights into the performance and behavior of their GraphQL APIs. - -```graphql -schema - @telemetry( - export: { - otlp: { - api_key: "{{.env.APOLLO_API_KEY}}" - graph_ref: "graph-id@current" - platform: "website.com" - version: "1.0.0" - } - } - ) { - query: Query -} -``` - -You can configure the apollo exporter with the following options: - -| Field | Description | -| --------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| api_key | The API Key generated from Apollo Studio. | -| graph_ref | The Graph Ref, which is the `graph_id` and the `variant` concatenated using `@`(i.e. \@\) | -| platform | An arbitrary value which can contain the name of your website or some other value to identify your deployment uniqely, in case you have multiple deployments. | -| version | Version of Apollo which is being used. | - -By integrating the `@telemetry` directive into your GraphQL schema, you empower your development teams with critical insights into application performance, enabling proactive optimization and maintenance. diff --git a/docs/directives/upstream.md b/docs/directives/upstream.md deleted file mode 100644 index 7564db75ec..0000000000 --- a/docs/directives/upstream.md +++ /dev/null @@ -1,257 +0,0 @@ ---- -title: Configuring Upstream Connections -description: Control upstream server connections, manage settings like timeouts and proxies, and enhance API efficiency with the upstream directive. -slug: upstream-directive -sidebar_label: Configuring Upstream ---- - -The `upstream` directive enables control over specific aspects of the upstream server connection, including settings such as connection timeouts, keep-alive intervals, and more. The system applies default values if you do not specify them. - -```graphql showLineNumbers -schema @upstream(...[UpstreamSetting]...){ - query: Query - mutation: Mutation -} -``` - -The document below details the options for `UpstreamSetting`. - -### poolIdleTimeout - -The connection pool waits for this duration in seconds before closing idle connections. - -```graphql showLineNumbers -schema - @upstream( - poolIdleTimeout: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### poolMaxIdlePerHost - -The max number of idle connections each host will maintain. - -```graphql showLineNumbers -schema - @upstream( - poolMaxIdlePerHost: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### keepAliveInterval - -The time in seconds between each keep-alive message sent to maintain the connection. - -```graphql showLineNumbers -schema - @upstream( - keepAliveInterval: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### keepAliveTimeout - -The time in seconds that the connection will wait for a keep-alive message before closing. - -```graphql showLineNumbers -schema - @upstream( - keepAliveTimeout: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### keepAliveWhileIdle - -A boolean value that determines whether to send keep-alive messages while the connection is idle. - -```graphql showLineNumbers -schema - @upstream( - keepAliveWhileIdle: false - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### proxy - -The `proxy` setting defines an intermediary server that routes upstream requests before they reach their intended endpoint. By specifying a proxy URL, you introduce a layer, enabling custom routing and security policies. - -```graphql showLineNumbers -schema - @upstream( - proxy: {url: "http://localhost:3000"} - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -In the provided example, we've set the proxy's `url` to "http://localhost:3000". This configuration ensures that all requests aimed at the designated `baseURL` first go through this proxy. To illustrate, if the `baseURL` is "http://jsonplaceholder.typicode.com", any request targeting it initially goes to "http://localhost:3000" before the proxy redirects it to its final destination. - -### connectTimeout - -The time in seconds that the connection will wait for a response before timing out. - -```graphql showLineNumbers -schema - @upstream( - connectTimeout: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### timeout - -The max time in seconds that the connection will wait for a response. - -```graphql showLineNumbers -schema - @upstream( - timeout: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### tcpKeepAlive - -The time in seconds between each TCP keep-alive message sent to maintain the connection. - -```graphql showLineNumbers -schema - @upstream( - tcpKeepAlive: 60 - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### userAgent - -The User-Agent header value for HTTP requests. - -```graphql showLineNumbers -schema - @upstream( - userAgent: "Tailcall/1.0" - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -### allowedHeaders - -The `allowedHeaders` configuration defines a set of whitelisted HTTP headers that can be forwarded to upstream services during requests. -Without specifying `allowedHeaders`, the system will not forward any incoming headers to upstream services, offering an extra security layer but potentially limiting necessary data flow. Tailcall compares the provided whitelisted headers in a case-insensitive format. - -```graphql showLineNumbers -schema - @upstream( - allowedHeaders: ["Authorization", "X-Api-Key"] - ) { - query: Query - mutation: Mutation -} -``` - -In the example above, the configuration for `allowedHeaders` permits `Authorization` and `X-Api-Key` headers. Thus, requests with these headers will forward them to upstream services; the system ignores all others. This configuration ensures communication of the expected headers to dependent services, emphasizing security and consistency. - -### baseURL - -This refers to the default base URL for your APIs. If it's not explicitly mentioned in the `@upstream` directive, then each [`@http`](/docs/directives/http.md) directive must specify its own `baseURL`. If neither `@upstream` nor [`@http`](/docs/directives/http.md) provides a `baseURL`, it results in a compilation error. - -```graphql showLineNumbers -schema - @upstream( - baseURL: "http://jsonplaceholder.typicode.com" - ) { - query: Query - mutation: Mutation -} -``` - -In this representation, `http://jsonplaceholder.typicode.com` serves as the `baseURL`. Thus, all API calls made by `@http` prepend this URL to their respective paths. - -:::tip -Ensure that your base URL remains free from specific path segments. - -- **GOOD:** `@upstream(baseURL: http://jsonplaceholder.typicode.com)` -- **BAD:** `@upstream(baseURL: http://jsonplaceholder.typicode.com/api)` - -::: - -### httpCache - -When httpCache passed with value greater than 0 it directs Tailcall to use HTTP caching mechanisms, following the [HTTP Caching RFC](https://tools.ietf.org/html/rfc7234) to enhance performance by minimizing unnecessary data fetches. If left unspecified, this feature defaults to `0` disabling the caching mechanism. - -```graphql showLineNumbers -schema @upstream(httpCache: 42) { - query: Query - mutation: Mutation -} -``` - -### Tips - -- Use batching when other optimization techniques fail to resolve performance issues. -- Apply batching and thoroughly assess its impact. -- Understand that batching may make debugging more challenging. - -### batch - -An object that specifies the batch settings, including `maxSize` (the max size of the batch), `delay` (the delay in milliseconds between each batch), and `headers` (an array of HTTP headers that the batch will include). - -```graphql showLineNumbers -schema - @upstream( - batch: { - maxSize: 1000 - delay: 10 - headers: ["X-Server", "Authorization"] - } - ) { - query: Query - mutation: Mutation -} -``` - -### onRequest - -Similar to the [@http](/docs/directives/http.md) property, this accepts a string value representing a middleware function defined in a JavaScript file. It intercepts all outgoing HTTP requests from the server. This interceptor, written in JavaScript, can be used to modify outgoing requests and also generate artificial responses to customize the behavior of the GraphQL server. - -```graphql showLineNumbers -schema @upstream(onRequest: 'someFunctionName') -@link(type: Script, src: "path_to/worker.js") { - query: Query - mutation: Mutation -} -``` diff --git a/docs/grpc.md b/docs/grpc.md index 4757eb6f25..153a6e11b5 100644 --- a/docs/grpc.md +++ b/docs/grpc.md @@ -123,7 +123,7 @@ type Query { } ``` -Also, let's specify options for Tailcall's ingress and egress at the beginning of the config using [`@server`](/docs/directives/server.md) and [`@upstream`](/docs/directives/upstream.md) directives. +Also, let's specify options for Tailcall's ingress and egress at the beginning of the config using [`@server`](/docs/directives/server.md) and [`@upstream`](/docs/client-tuning.md#upstream-directive) directives. ```graphql schema @@ -235,7 +235,7 @@ Or Another important feature of the `@grpc` directive is that it allows you to implement request batching for remote data almost effortlessly as soon as you have gRPC methods that resolve multiple responses for multiple inputs in a single request. -In our protobuf example file, we have a method called `GetMultipleNews` that we can use. To enable batching we need to enable [`@upstream.batch` option](/docs/directives/upstream.md#batch) first and specify `batchKey` option for the `@grpc` directive. +In our protobuf example file, we have a method called `GetMultipleNews` that we can use. To enable batching we need to enable [`@upstream.batch` option](/docs/client-tuning.md#batch) first and specify `batchKey` option for the `@grpc` directive. ```graphql schema diff --git a/docs/rest.md b/docs/rest.md index c23bf49e3e..ee41cba760 100644 --- a/docs/rest.md +++ b/docs/rest.md @@ -9,6 +9,56 @@ In order to handle complicated business problems, modern systems frequently need That's why Tailcall has developed a directive called `@rest` to streamline this transition and take advantage of GraphQL's power without requiring a complete overhaul. With the help of this directive, Tailcall GraphQL queries and mutations may be made available as REST endpoints. +## @rest Directive + +API orchestration is essential, yet not all can adopt GraphQL despite its benefits. The Tailcall DSL feature leverages GraphQL at compile time to generate REST endpoints, aligning with traditional API infrastructure like CDNs and Gateways. + +### Usage + +- **method**: Specifies the HTTP method (GET, POST, etc.). +- **path**: Sets the endpoint URL, with support for dynamic values from query arguments. +- **query**: Defines the query parameters as key-value pairs. + + + ## How it works This guide show you how to expose REST endpoints for your GraphQL operations by using the @rest directive like follows: @@ -74,7 +124,7 @@ query ($id: Int!) @rest(method: GET, path: "/post/$id") { } ``` -to know more about the `@rest` directive, please refer to the [Tailcall GraphQL Directives](/docs/directives/rest.md). +to know more about the `@rest` directive, please refer to the [Tailcall GraphQL Directives](/docs/rest.md#rest-directive). ### Step 3: Link the operation to the main config file diff --git a/docs/scripting.md b/docs/scripting.md index bc9c051cd3..affd740b18 100644 --- a/docs/scripting.md +++ b/docs/scripting.md @@ -13,7 +13,7 @@ The runtime is not a full-fledged Node.js environment and has no access to the f To leverage this customization, JavaScript functions must be created in a JavaScript file and linked with the main configuration file using the [@link](/docs/directives/link.md) directive. There are two primary ways to achieve this: 1. Define an `onRequest` property with the JS function name in the [http](/docs/directives/http.md#onrequest) directive. -2. Define it in the [upstream](/docs/directives/upstream.md#onrequest) directive, which acts as a global middleware for all requests. +2. Define it in the [upstream](/docs/client-tuning.md#onrequest) directive, which acts as a global middleware for all requests. :::tip If you specify a `onRequest` handler for both `http` and `upstream` the `http` one will always take precedence over the global `onRequest` handler. @@ -29,7 +29,7 @@ function foo({request}) { } ``` -Once you have a worker file ready, link that file to the GraphQL configuration using the [`@link`](/docs/directives/link.md) directive and define the [onRequest](/docs/directives/upstream.md#onrequest) property. +Once you have a worker file ready, link that file to the GraphQL configuration using the [`@link`](/docs/directives/link.md) directive and define the [onRequest](/docs/client-tuning.md#onrequest) property. ```graphql schema @@ -123,7 +123,7 @@ type Request = { } ``` -By default the headers field will be empty in most cases, unless headers are whitelisted via the [allowedHeaders](/docs/directives/upstream.md#allowedheaders) setting in [`@upstream`](/docs/directives/upstream.md). +By default the headers field will be empty in most cases, unless headers are whitelisted via the [allowedHeaders](/docs/client-tuning.md#allowedheaders) setting in [`@upstream`](/docs/client-tuning.md#upstream-directive). The http filter doesn't have access to the request's body, hence you can't directly modify the body of an outgoing request. This is more of a design choice than a limitation we have made to ensure that developers don't misuse this API to write all kind of business logic in Tailcall. diff --git a/docs/telemetry.md b/docs/telemetry.md index 34b3aa3c2c..c4f2793f09 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -33,6 +33,145 @@ While [Apollo studio](./apollo-studio.md) telemetry also provides analytics tool - OpenTelemetry is vendor-agnostic and therefore you could actually use different observability platforms depending on your needs and don't rely on single tool like Apollo Studio - OpenTelemetry integration in Tailcall can provide more analytical data that is out of scope of graphQL analytics provided by Apollo Studio +## @telemetry Directive + +The `@telemetry` directive facilitates seamless integration with [OpenTelemetry](https://open-telemetry.io), enhancing the observability of your GraphQL services powered by Tailcall. By leveraging this directive, developers gain access to valuable insights into the performance and behavior of their applications. + +### Traces + +Here are the traces that are captured by the `@telemetry` directive: + +| Trace Name | Description | +| --------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| request | Captures the span for processing the HTTP request on the server side, providing foundational observability. | +| graphQL | _Only for GraphQL ingress_. Span for processing GraphQL call | +| `REST ` | _Only for REST ingress_. Span for processing REST API call | +| `` | Denotes spans for fields with defined resolvers, offering insights into field names and execution times for resolver logic. | +| `` | Nested within the `` spans, these granulated spans detail the execution of expressions in resolving a field, highlighting the hierarchical execution pattern of nested expressions. | +| upstream_request | Request that were made from tailcall service to upstream | + +### Metrics + +The `@telemetry` directive also captures the following metrics: + +| Metric | Description | +| ------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------- | +| cache.hit_rate | Reflects the cache hit rate for the cache powered by the [`@cache`](/docs/directives/cache.md) directive | +| http.server.request.count | Counts the number of incoming requests made to specific route. Optionally enriched with selected headers by [`requestHeaders`](#requestheaders) | +| http.client.request.count | Counts the number of outgoing requests to specific upstream | + +### export + +The `export` field defines how the open-telemetry data should be exported and in which format. The following are the supported formats: + +### otlp + +Utilizes the OTLP format to export telemetry data to backend systems, supported by most modern tracing and analytics platforms. Here is an example using [honeycomb.io]: + +[honecomb.io]: https://www.honeycomb.io/ + +```graphql +schema + @telemetry( + export: { + otlp: { + url: "https://api.honeycomb.io:443" + headers: [ + { + key: "x-honeycomb-team" + value: "{{.env.HONEYCOMB_API_KEY}}" + } + {key: "x-honeycomb-dataset", value: "tailcall"} + ] + } + } + ) { + query: Query +} +``` + +You can configure the OTLP exporter with the following options: + +| Field | Description | +| ------: | ----------------------------------------------------------- | +| url | Defines the URL for the OTLP Collector. | +| headers | Sets additional headers for requests to the OTLP Collector. | + +### prometheus + +Facilitates metrics export in a Prometheus compatible format, providing a dedicated endpoint for metrics. + +```graphql +schema + @telemetry(export: {prometheus: {path: "/metrics"}}) { + query: Query +} +``` + +You can configure the Prometheus exporter with the following options: + +| Field | Description | +| -----: | ---------------------------------------------------------------------------------- | +| path | Designates the endpoint path for Prometheus metrics, defaulting to `/metrics`. | +| format | Controls the format viz. **text** or **protobuf**, for sending data to Prometheus. | + +### stdout + +Outputs all telemetry data to stdout, ideal for testing or local development environments. + +```graphql +schema @telemetry(export: {stdout: {pretty: true}}) { + query: Query +} +``` + +You can configure the stdout exporter with the following options: + +| Field | Description | +| -----: | -------------------------------------------------------------------- | +| pretty | Enables formatted output of telemetry data for enhanced readability. | + +### requestHeaders + +Specifies list of headers of ingress request the value of which will be sent to the telemetry as attributes. + +```graphql +schema @telemetry(requestHeaders: ["X-User-Id"]) { + query: Query +} +``` + +### apollo + +Facilitates seamless integration with [Apollo Studio](https://studio.apollographql.com/), enhancing the observability of GraphQL services. By leveraging this field, developers gain access to valuable insights into the performance and behavior of their GraphQL APIs. + +```graphql +schema + @telemetry( + export: { + otlp: { + api_key: "{{.env.APOLLO_API_KEY}}" + graph_ref: "graph-id@current" + platform: "website.com" + version: "1.0.0" + } + } + ) { + query: Query +} +``` + +You can configure the apollo exporter with the following options: + +| Field | Description | +| --------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| api_key | The API Key generated from Apollo Studio. | +| graph_ref | The Graph Ref, which is the `graph_id` and the `variant` concatenated using `@`(i.e. \@\) | +| platform | An arbitrary value which can contain the name of your website or some other value to identify your deployment uniqely, in case you have multiple deployments. | +| version | Version of Apollo which is being used. | + +By integrating the `@telemetry` directive into your GraphQL schema, you empower your development teams with critical insights into application performance, enabling proactive optimization and maintenance. + ## Prerequisites Consider we have the following GraphQL configuration that connects with jsonplaceholder.com to fetch the data about user and posts @@ -75,7 +214,7 @@ We will update that config with telemetry integration in following sections. By default, telemetry data is not generated by Tailcall since it requires some setup to know where to send this data and also that affects performance of server that could be undesirable in some cases. -Telemetry configuration is provided by [`@telemetry`](/docs/directives/telemetry.md) directive to setup how and where the telemetry data is send. +Telemetry configuration is provided by [`@telemetry`](/docs/telemetry.md#telemetry-directive) directive to setup how and where the telemetry data is send. To enable it we can update our config with something like config below: @@ -112,7 +251,7 @@ Prometheus integration works by adding a special route for the GraphQL server's ## Data generated -You can find a reference of type of info generated by Tailcall in the [`@telemetry` reference](/docs/directives/telemetry.md) or consult examples in the next section, in order to gain some understanding. +You can find a reference of type of info generated by Tailcall in the [`@telemetry` reference](/docs/telemetry.md#telemetry-directive) or consult examples in the next section, in order to gain some understanding. ### Relation with other services @@ -126,7 +265,7 @@ Where Tailcall is a part of whole distributed trace ### Customize generated data -In some cases you may want to customize the data that were added to telemetry payload to have more control over analyzing process. Tailcall supports that customization for specific use cases described below. For eg. the metric [`http.server.request.count`](/docs/directives/telemetry.md#metrics) can be customized with the [`requestHeaders`](/docs/directives/telemetry.md#requestheaders) property to allow splitting the overall count by specific headers. +In some cases you may want to customize the data that were added to telemetry payload to have more control over analyzing process. Tailcall supports that customization for specific use cases described below. For eg. the metric [`http.server.request.count`](/docs/telemetry.md#metrics) can be customized with the [`requestHeaders`](/docs/telemetry.md#requestheaders) property to allow splitting the overall count by specific headers. :::important The value of specified headers will be sent to telemetry backend as is, so use it with care to prevent of leaking any sensitive data to third-party services you don't have control over. diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 34934e64b9..003a912197 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -201,11 +201,11 @@ export default { }, { from: "/docs/operators/telemetry/", - to: "/docs/directives/telemetry-directive", + to: "/docs/graphql-telemetry-guide#telemetry-directive", }, { from: "/docs/operators/upstream/", - to: "/docs/directives/upstream-directive", + to: "/docs/graphql-client-performance-tuning#upstream-directive", }, { from: "/docs/n+1/introduction/", diff --git a/sidebars.ts b/sidebars.ts index 14419679a3..dfc9334018 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -56,11 +56,7 @@ const sidebars: SidebarsConfig = { "directives/link", "directives/modify", "directives/omit", - "directives/protected", - "directives/rest", "directives/server", - "directives/telemetry", - "directives/upstream", ], }, {type: "category", label: "Integrations", items: ["apollo-studio", "data-dog", "new-relic", "honey-comb", "llm"]}, From 47a21e9235873b6372e38a92dc4e5436a0e71e9a Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Sat, 28 Sep 2024 12:25:20 +0000 Subject: [PATCH 11/12] refactor: tuning http doc. --- docs/client-tuning.md | 78 +++++++++++-------------------------------- 1 file changed, 19 insertions(+), 59 deletions(-) diff --git a/docs/client-tuning.md b/docs/client-tuning.md index f868b6da7f..4021f080db 100644 --- a/docs/client-tuning.md +++ b/docs/client-tuning.md @@ -48,6 +48,10 @@ Learn more about QUIC in detail [here](https://blog.cloudflare.com/the-road-to-q Connection pooling mitigates these issues by reusing existing connections for requests, reducing connection establishment frequency (and thus handshake overhead) and conserving client-side ports. This approach enhances application performance by minimizing the resources and time spent on managing connections. +## Tuning HTTP Client + +Tailcall uses connection pooling by default and sets up with default tuning suitable for most use cases. You might need to further tune the HTTP client to improve your application's performance. Tailcall DSL provides a directive named [`@upstream`](/docs/client-tuning.md#upstream-directive) for this purpose. + ## @upstream Directive The `upstream` directive enables control over specific aspects of the upstream server connection, including settings such as connection timeouts, keep-alive intervals, and more. The system applies default values if you do not specify them. @@ -78,7 +82,7 @@ schema ### poolMaxIdlePerHost -The max number of idle connections each host will maintain. +The max number of idle connections each host will maintain, defaulting to `60`. ```graphql showLineNumbers schema @@ -91,6 +95,10 @@ schema } ``` +Too idle connections can unnecessarily consume memory and ports, while too few might cause delays as new connections need frequent establishment. `poolMaxIdlePerHost` ensures judicious use of network and memory resources, avoiding wastage on seldom-used connections. + +For applications connecting to hosts, set this value lower to keep connections available for other hosts. Conversely, if you have hosts and all requests must resolve through them, maintain a higher value for this setting. + ### keepAliveInterval The time in seconds between each keep-alive message sent to maintain the connection. @@ -155,7 +163,7 @@ In the provided example, we've set the proxy's `url` to "http://localhost:3000". ### connectTimeout -The time in seconds that the connection will wait for a response before timing out. +The time in seconds that the connection will wait for a response before timing out, defaulting to 60 seconds. ```graphql showLineNumbers schema @@ -168,6 +176,8 @@ schema } ``` +`connectTimeout` specifically applies to the phase where your client attempts to establish a connection with the server. When making a connection request, the client tries to resolve the DNS, complete the SSL handshake, and establish a TCP connection. In environments where pods are frequently created and destroyed, maintaining a low `connectTimeout` is crucial to avoid unnecessary delays. In systems using connection pooling, the system aborts the attempt if it cannot establish a connection within the `connectTimeout` period. This approach prevents indefinite waiting for a connection to establish, which could cause delays and timeouts. + ### timeout The max time in seconds that the connection will wait for a response. @@ -185,7 +195,7 @@ schema ### tcpKeepAlive -The time in seconds between each TCP keep-alive message sent to maintain the connection. +The time in seconds between each TCP keep-alive message sent to maintain the connection, defaults to 5 seconds. ```graphql showLineNumbers schema @@ -198,6 +208,8 @@ schema } ``` +`tcpKeepAlive` keeps TCP connections alive for a duration, during inactivity, by periodically sending packets to the server to check if the connection remains open. In connection pooling, `tcpKeepAlive` maintains reusable connections in a ready-to-use state. This setting is useful for long-lived connections, preventing -lived connections, preventing the client from using a connection the server has closed due to inactivity. Without `tcpKeepAlive`, connections in the pool might get dropped by the server or intermediate network devices (like firewalls or load balancers). When your client tries to use such a dropped connection, it would fail, causing delays and errors. Keeping connections alive and monitored means you can efficiently reuse them, reducing the overhead of establishing new connections frequently. + ### userAgent The User-Agent header value for HTTP requests. @@ -301,68 +313,16 @@ schema @upstream(onRequest: 'someFunctionName') } ``` -## Tuning HTTP Client - -Tailcall uses connection pooling by default and sets up with default tuning suitable for most use cases. You might need to further tune the HTTP client to improve your application's performance. Tailcall DSL provides a directive named [`@upstream`](/docs/client-tuning.md#upstream-directive) for this purpose. - :::note Connection pooling optimizes HTTP/1. Since HTTP/2 and HTTP/3 support multiplexing, pooling enabled does not noticeably affect performance. ::: When using HTTP/1.x, tune the connection pool with the following parameters: -### poolMaxIdlePerHost - -`poolMaxIdlePerHost` specifies the allowed number of idle connections per host, defaulting to `60`. Example: - -```graphql showLineNumbers -schema - @upstream( - # highlight-start - poolMaxIdlePerHost: 60 - # highlight-end - ) { - query: Query -} -``` +- [poolMaxIdlePerHost](#poolmaxidleperhost) +- [tcpKeepAlive](#tcpkeepalive) +- [connectTimeout](#connecttimeout) -Too idle connections can unnecessarily consume memory and ports, while too few might cause delays as new connections need frequent establishment. `poolMaxIdlePerHost` ensures judicious use of network and memory resources, avoiding wastage on seldom-used connections. - -For applications connecting to hosts, set this value lower to keep connections available for other hosts. Conversely, if you have hosts and all requests must resolve through them, maintain a higher value for this setting. - -### tcpKeepAlive - -`tcpKeepAlive` keeps TCP connections alive for a duration, during inactivity, by periodically sending packets to the server to check if the connection remains open. In connection pooling, `tcpKeepAlive` maintains reusable connections in a ready-to-use state. This setting is useful for long-lived connections, preventing -lived connections, preventing the client from using a connection the server has closed due to inactivity. Without `tcpKeepAlive`, connections in the pool might get dropped by the server or intermediate network devices (like firewalls or load balancers). When your client tries to use such a dropped connection, it would fail, causing delays and errors. Keeping connections alive and monitored means you can efficiently reuse them, reducing the overhead of establishing new connections frequently. - -Tailcall provides a parameter named `tcpKeepAlive` for the upstream which defaults to 5 seconds. Example: -schema - -```graphql -@upstream ( -# highlight-start - tcpKeepAlive: 300 -# highlight-end -) { -query: Query -} - -``` - -### connectTimeout - -`connectTimeout` specifically applies to the phase where your client attempts to establish a connection with the server. When making a connection request, the client tries to resolve the DNS, complete the SSL handshake, and establish a TCP connection. In environments where pods are frequently created and destroyed, maintaining a low `connectTimeout` is crucial to avoid unnecessary delays. In systems using connection pooling, the system aborts the attempt if it cannot establish a connection within the `connectTimeout` period. This approach prevents indefinite waiting for a connection to establish, which could cause delays and timeouts. - -Tailcall offers a `connectTimeout` parameter to set the connection timeout in seconds for the HTTP client, defaulting to 60 seconds. Example: - -```graphql showLineNumbers -schema - @upstream( - # highlight-start - connectTimeout: 10 - # highlight-end - ) { - query: Query -} -``` +These parameters allow you to control the number of idle connections, maintain active connections, and set appropriate timeouts to ensure efficient communication between the client and server. In summary, maximizing HTTP client performance requires understanding the underlying protocols and configuring client settings through testing. This ensures efficient, robust, and high-performing client-server communication, crucial for the smooth operation of modern web applications. From 3cfc839f7e58e0378d1e0c5e82b7b5fb9d03c16d Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Mon, 30 Sep 2024 13:12:49 +0000 Subject: [PATCH 12/12] resolved conflicts --- docs/apollo-federation-subgraph.md | 2 +- docs/directives/link.md | 3 +++ docs/directives/server.md | 8 ++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/apollo-federation-subgraph.md b/docs/apollo-federation-subgraph.md index 6eadc991a4..1fafeb2e92 100644 --- a/docs/apollo-federation-subgraph.md +++ b/docs/apollo-federation-subgraph.md @@ -40,7 +40,7 @@ Please, note that you don't need to specify the `@key` directive manually when d ## Enable federation in the Tailcall config -Federation is controlled by the flag [`enableFederation`](./directives.md#enablefederation). In case you've added the entity resolvers on the previous step then federation compatibility will be enabled even without the flag. +Federation is controlled by the flag [`enableFederation`](./directives/server.md#enablefederation). In case you've added the entity resolvers on the previous step then federation compatibility will be enabled even without the flag. ## Register your subgraph diff --git a/docs/directives/link.md b/docs/directives/link.md index eca1815210..3d0170aea5 100644 --- a/docs/directives/link.md +++ b/docs/directives/link.md @@ -17,6 +17,8 @@ The `@link` directive requires specifying a source `src`, the resource's type `t - `id`: This is an optional field that assigns a unique identifier to the link. It's helpful for referring to the link within the schema. +- `headers`: This is an optional field that assigns custom headers to the gRPC reflection server requests. Specifying a key-value map of header names and their values achieves this. + ### Example The following example illustrates how to utilize the `@link` directive to incorporate a Protocol Buffers (.proto) file for a gRPC service into your GraphQL schema. @@ -33,6 +35,7 @@ schema id: "news" src: "./src/grpc/news.proto" type: Protobuf + headers: [{key: "authorization", value: "Bearer 123"}] ) { query: Query } diff --git a/docs/directives/server.md b/docs/directives/server.md index 7c06b02f57..85a1982d9f 100644 --- a/docs/directives/server.md +++ b/docs/directives/server.md @@ -348,3 +348,11 @@ schema @server(routes: {graphQL: "/tailcall-gql", status: "/health"}) ``` In this example, the GraphQL endpoint is changed to `/tailcall-gql` and the status endpoint to `/health`. + +### enableFederation + +A boolean flag, if set to `true` the Tailcall server will additionally act as federation subgraph. If set to `false` federation compatibility will be forcefully disabled. If flag is not set the federation compatibility is enabled automatically if any entity resolver is defined and disabled otherwise. + +```graphql showLineNumbers +schema @server(enableFederation: true) +```