Skip to content

Commit

Permalink
Merge branch 'develop' into docs/add-doc-for-http-post-request-batching
Browse files Browse the repository at this point in the history
  • Loading branch information
laststylebender14 committed Dec 3, 2024
2 parents d443798 + 4f3ca4a commit d578fed
Show file tree
Hide file tree
Showing 33 changed files with 8,746 additions and 5,891 deletions.
3 changes: 0 additions & 3 deletions babel.config.js

This file was deleted.

40 changes: 40 additions & 0 deletions docs/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,43 @@ Here is a list of all the custom directives supported by Tailcall:
| [`@server`](./directives/server.md) | Provides server configurations for behavior tuning and tailcall optimization in specific use-cases. |
| [`@telemetry`](./directives/telemetry.md) | Integrates with open-telemetry to provide observability of the running tailcall service. |
| [`@upstream`](./directives/upstream.md) | Controls aspects of the upstream server connection, including timeouts and keep-alive settings. |

### Resolvable Directives

Resolvable directives are used to fetch actual data from external sources. These include the following directives: `@call`, `@expr`, `@graphQL`, `@grpc`, and `@http`.

### Combining Resolvable Directives on Fields

When multiple resolvable directives (such as `@call`, `@expr`, `@graphQL`, `@grpc`, or `@http`) are applied to a field, the **order in which they are defined in the schema is important**. Each directive contributes a part of the final result, and the outputs are combined by performing a deep merge of all partial results.

#### Example: Combining Resolvable Directives

```graphql
type Query {
data: Data
# This request resolves the `{ "foo": "..." }` part of the response
@http(url: "http://api.com/foo")
# This request resolves the `{ "bar": "..." }` part of the response
# After executing both requests, the results are merged into a single `Data` object
@http(url: "http://api.com/bar")

dataList: [Data]
# This request resolves 3 entries of data: `[.., .., ..]`
@http(url: "http://api.com/list/foo")
# This request resolves 2 entries of data: `[.., ..]`
# After executing both requests, the results are merged into a single list
# containing 5 entries
@http(url: "http://api.com/list/bar")
}

type Data {
foo: String
bar: String
}
```

### Key Points

1. **Order Matters**: The schema's order of directives determines how partial results are combined.
2. **Deep Merge**: Partial outputs from each directive are deep-merged to produce the final result.
3. **Versatility**: Resolvable directives can fetch data from various sources, making them powerful tools for flexible schema design.
80 changes: 30 additions & 50 deletions docs/directives/call.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ description: The @call directive simplifies queries by composing together other
slug: ../call-directive
---

The `@call` directive simplifies GraphQL schema design by enabling resolver composition, allowing you to create chains of resolvers executed in sequence.

## `@call` Directive Definition

The `@call` directive is defined as follows:

```graphql showLineNumbers title="Directive Definition"
Expand Down Expand Up @@ -35,6 +39,8 @@ input CallStep {
}
```

## Example: Eliminating Redundancy

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
Expand Down Expand Up @@ -94,13 +100,13 @@ type Post {

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
## Composing Resolvers with `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.
The `steps` argument in the `@call` directive is used to define a chain of resolvers, executed sequentially.

## query
### Example: Invoking a Query

Specify the root **query** field to invoke, alongside the requisite arguments, using the `@call` directive for a concise and efficient query structure.
To invoke a query, specify the `query` field to call, along with any required arguments:

```graphql showLineNumbers
type Post {
Expand All @@ -114,9 +120,9 @@ type Post {
}
```

## mutation
### Example: Invoking a 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.
Similarly, you can invoke a mutation by specifying the `mutation` parameter:

```graphql showLineNumbers
type Mutation {
Expand All @@ -140,9 +146,9 @@ type Mutation {
}
```

## args
### Passing Arguments

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.
Arguments can be passed using the `args` parameter, where each key represents an argument name:

```graphql showLineNumbers
type Post {
Expand All @@ -157,14 +163,16 @@ type Post {
```

:::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.
The `@call` directive is most useful in larger schemas with complex configurations. If you're just starting with GraphQL, consider mastering the basics before diving into advanced directives like `@call`.
:::

## Composition
## Advanced Composition Example

The `@call` directive can be used to combine multiple resolvers in a sequence, passing the output of each step to the next. This allows for flexible composition of existing operations.

`@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.
### Example: Composing Multiple Operations

Let's explain this with an example:
Consider a scenario where we have three operations (`a`, `b`, `c`), each extracting a specific part of the input:

```graphql showLineNumbers
schema @server {
Expand All @@ -183,41 +191,7 @@ type Query {
}
```

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.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}}}}
```

Given the above input if we wish to extract the last inner number `1000` then we could define a new operation as follows
We can create a new `abc` operation that calls `a`, `b`, and `c` in sequence to extract deeply nested data:

```graphql showLineNumbers
schema @server {
Expand Down Expand Up @@ -247,15 +221,15 @@ type Query {
}
```

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:
### Running the Composed Query

```graphql
query {
abc(input: {a: {b: {c: 1000}}})
}
```

The server returns the response that we expected:
Response:

```json
{
Expand All @@ -270,5 +244,11 @@ The server returns the response that we expected:
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.
Using `JSON` scalar here is for simplicity. In production, proper input and output types should be used for type safety.
:::

## Combining Multiple Directives

The `@call` directive can be used in combination with other [resolvable directives](../directives.md#resolvable-directives), with results merged deeply. This allows for powerful and flexible resolver configurations.

For more details, see [Directives Documentation](../directives.md).
6 changes: 6 additions & 0 deletions docs/directives/expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,9 @@ type Emails {
```

In this example, the `@expr` directive dynamically generates an `Emails` object based on the provided template data. The placeholders within the template (`{{.value.workEmail}}` and `{{.value.personalEmail}}`) get replaced with the actual values specified in the `User` type, allowing for dynamic content generation while still adhering to the schema's structure.

## Combining Multiple Directives

The `@expr` directive can be used in combination with other [resolvable directives](../directives.md#resolvable-directives), with results merged deeply. This allows for powerful and flexible resolver configurations.

For more details, see [Directives Documentation](../directives.md).
81 changes: 41 additions & 40 deletions docs/directives/graphQL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ description: The @graphQL directive allows to specify a GraphQL API server to fe
slug: ../graphQL-directive
---

The `@graphQL` directive is defined as follows:
The `@graphQL` directive allows you to specify an external GraphQL API server to fetch data from, making it easier to integrate remote data sources.

## `@graphQL` Directive Definition

```graphql title="Directive Definition" showLineNumbers
directive @graphQL(
Expand All @@ -17,7 +19,9 @@ directive @graphQL(
) on FIELD_DEFINITION
```

The `@graphQL` directive allows to specify a GraphQL API server to fetch data from.
## Example: Fetching Users from External API

The following example shows how to use the `@graphQL` directive to fetch data from an external GraphQL API server:

```graphql showLineNumbers
type Query {
Expand All @@ -29,9 +33,11 @@ type Query {
}
```

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.
In the example above, the `@graphQL` directive specifies that the `users` field should be resolved by fetching data from an external GraphQL endpoint.

For the next request with the config above:
### Request Flow Example

Given a query for `users`:

```graphql showLineNumbers
query {
Expand All @@ -42,7 +48,7 @@ query {
}
```

Tailcall will request the next query for the upstream:
Tailcall will make the following query to the upstream server:

```graphql showLineNumbers
query {
Expand All @@ -53,9 +59,11 @@ query {
}
```

## url
## Directive Arguments

### url

This refers to the URL of the API.
The `url` parameter specifies the endpoint for the external GraphQL API:

```graphql showLineNumbers
type Query {
Expand All @@ -67,9 +75,9 @@ type Query {
}
```

## name
### name

The root field's name on the upstream to request data from. For example:
The `name` parameter specifies the name of the root field in the external API. For example:

```graphql showLineNumbers
type Query {
Expand All @@ -81,11 +89,11 @@ type Query {
}
```

When Tailcall receives a query for the `users` field, it will request a query for `userList` from the upstream.
In this case, `users` maps to `userList` in the upstream query.

## args
### args

Named arguments for the requested field. For example:
The `args` parameter allows you to pass arguments to the upstream GraphQL query:

```graphql showLineNumbers
type Query {
Expand All @@ -98,7 +106,7 @@ type Query {
}
```

Will request the next query from the upstream for the first user's name:
Example query to fetch a user's name by ID:

```graphql showLineNumbers
query {
Expand All @@ -108,11 +116,9 @@ query {
}
```

## headers
### 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:
The `headers` parameter customizes the HTTP headers sent in the GraphQL request:

```graphql showLineNumbers
type Mutation {
Expand All @@ -125,25 +131,13 @@ type Mutation {
}
```

In this example, a request to `/users` will include the HTTP header `X-Server` with the value `Tailcall`.
Here, the `X-Server` header is added to requests.

## batch
### 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:
If the upstream server supports batching, you can use the `batch` option to batch requests:

```graphql showLineNumbers
schema
@upstream(
batch: {
maxSize: 1000
delay: 10
headers: ["X-Server", "Authorization"]
}
) {
query: Query
mutation: Mutation
}

type Query {
users: [User]
@graphQL(
Expand All @@ -160,16 +154,23 @@ type Query {
}
```

## dedupe
### 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`.
The `dedupe` parameter is a boolean flag that, when set to `true`, prevents duplicate requests from being sent concurrently:

```graphql showLineNumbers
@graphQL(
url: "https://jsonplaceholder.typicode.com",
name: "users",
dedupe: true
)
type Query {
users: [User]
@graphQL(
url: "https://jsonplaceholder.typicode.com"
name: "users"
dedupe: true
)
}
```

Make sure you have also specified batch settings to the `@upstream` and to the `@graphQL` directive.
## Combining Multiple Directives

The `@graphQL` directive can be used in combination with other [resolvable directives](../directives.md#resolvable-directives), with results merged deeply. This allows for powerful and flexible resolver configurations.

For more details, see [Directives Documentation](../directives.md).
Loading

0 comments on commit d578fed

Please sign in to comment.