Chaining queries and mutations

Using the Pipeline service you can run several queries and mutations sequentially through a single request.

Tutorial steps

To craft a pipeline, you'll need to:

  1. Activate Pipeline service
  2. Add a new field categoryID to Product type
  3. Create a skeleton of Pipeline file
  4. Define resolver
  5. Apply changes
  6. Confirm the creation of the pipeline

1. Activate Pipeline service

Before we get into crafting pipelines, we first need to enable the Pipeline service. To activate the Pipeline service, uncomment the Pipeline entry in the app.cue file as the following.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> (</span></span>
<span><span style="color: var(--shiki-color-text)">  ...</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">tailor.build/template/services/pipeline</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">)</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">application.#Spec </span><span style="color: var(--shiki-token-keyword)">&amp;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  ...</span></span>
<span><span style="color: var(--shiki-color-text)">  subgraphs: [</span></span>
<span><span style="color: var(--shiki-color-text)">    ...</span></span>
<span><span style="color: var(--shiki-color-text)">    {type: common.#Pipeline</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> name:  pipeline.namespace}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  ]</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

Now, the Pipeline service is enabled. For more information, please refer to the Activating Services guide.

2. Add a new field categoryID to Product type

Create Category type by referring to Adding new data model tutorial. Next, Add categoryID to Product type

<span><span style="color: var(--shiki-token-keyword)">package</span><span style="color: var(--shiki-color-text)"> master</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> (</span></span>
<span><span style="color: var(--shiki-color-text)">	</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">github.com/tailor-platform/tailorctl/schema/v2/tailordb</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">	</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">tailor.build/template/services/tailordb</span><span style="color: var(--shiki-color-text)">:permissions</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">)</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">Product: tailordb.#Type </span><span style="color: var(--shiki-token-keyword)">&amp;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">	Name:        </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">Product</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">	Description: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">Product model</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">	Fields: {</span></span>
<span><span style="color: var(--shiki-color-text)">		...</span></span>
<span><span style="color: var(--shiki-color-text)">		categoryID: {</span></span>
<span><span style="color: var(--shiki-color-text)">			Type:        tailordb.#TypeUUID</span></span>
<span><span style="color: var(--shiki-color-text)">			Description: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">category ID</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">		}</span></span>
<span><span style="color: var(--shiki-color-text)">	}</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">	TypePermission: permissions.editorAccess</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
<span></span>

3. Create a skeleton of Pipeline file

Let's create a skeleton of the services/pipeline.cue file:

<span></span>
<span><span style="color: var(--shiki-token-keyword)">package</span><span style="color: var(--shiki-color-text)"> pipeline</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> (</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">github.com/tailor-platform/tailorctl/schema/v2/pipeline</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">tailor.build/template/services/pipeline/resolvers</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">tailor.build/template/environment</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">)</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">pipeline.#Spec </span><span style="color: var(--shiki-token-keyword)">&amp;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  Namespace: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">my-pipeline</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  Resolvers: [</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// this is where we will implement the queries and mutations chains.</span></span>
<span><span style="color: var(--shiki-color-text)">    resolvers.createCategoryForProduct</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  ]</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

4. Define createCategoryForProduct resolver

The pipeline we're going to craft will provide a mutation that creates a new Category and Product record using the input. Locate resolvers folder in the services/pipeline folder and create createCategoryForProduct.cue file.

<span><span style="color: var(--shiki-token-keyword)">package</span><span style="color: var(--shiki-color-text)"> resolvers</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> (</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">github.com/tailor-platform/tailorctl/schema/v2/pipeline</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">tailor.build/template/services/pipeline</span><span style="color: var(--shiki-color-text)">:settings</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">)</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">createCategoryForProduct: pipeline.#Resolver </span><span style="color: var(--shiki-token-keyword)">&amp;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">	Authorization: pipeline.#AuthInsecure</span></span>
<span><span style="color: var(--shiki-color-text)">	Name:        </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">createCategoryForProduct</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">	Description: </span><span style="color: var(--shiki-color-text)">&quot;&quot;&quot;</span></span>
<span><span style="color: var(--shiki-token-string-expression)">	This API is an alternative to createCategory. It creates a Category record and then creates Product record. Use this API if you want to create a Category record and Product record in one API call.</span></span>
<span><span style="color: var(--shiki-token-string-expression)">	</span><span style="color: var(--shiki-color-text)">&quot;&quot;&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">	Inputs: [</span></span>
<span><span style="color: var(--shiki-color-text)">    { Name: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">input</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">      Required: </span><span style="color: var(--shiki-token-constant)">true</span></span>
<span><span style="color: var(--shiki-color-text)">      Type: {</span></span>
<span><span style="color: var(--shiki-color-text)">        Name: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">createCategoryForProductInput</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">        Fields: [</span></span>
<span><span style="color: var(--shiki-color-text)">          {Name: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">categoryName</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Type: pipeline.String</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Required:  </span><span style="color: var(--shiki-token-constant)">true</span><span style="color: var(--shiki-color-text)">}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">          {Name: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">productTitle</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Type: pipeline.String</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Required:  </span><span style="color: var(--shiki-token-constant)">true</span><span style="color: var(--shiki-color-text)">}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">          {Name: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">productDescription</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Type: pipeline.String</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Required:  </span><span style="color: var(--shiki-token-constant)">true</span><span style="color: var(--shiki-color-text)">}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">        ]</span></span>
<span><span style="color: var(--shiki-color-text)">      }</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  ]</span></span>
<span><span style="color: var(--shiki-color-text)">  Response: {	Type: pipeline.ID }</span></span>
<span><span style="color: var(--shiki-color-text)">  PostScript: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">context.pipeline.createProduct.id</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  Pipelines: []</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

Here you can define the details of your pipeline resolver.
The Inputs is an array of the input types and Response is the output type of the pipeline resolver. In this example, the createCategoryForProduct pipeline resolver accepts an input of type createCategoryForProductInput.

Caveats

  • Subscription operations are not supported, only Mutation and Query may be used. For more details of the properties in pipeline.#Resolver, please refer to the Pipeline Reference.

Next, we're going to add the steps this specific pipeline resolver will go through.

Step 1: Create category record based on the categoryName.

In the Pipelines array, let's add the first step: creating a category.

<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)">  Name:        </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">createCategory</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  Description: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">Create a category.</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  Invoker:     settings.adminInvoker</span></span>
<span><span style="color: var(--shiki-color-text)">  PreScript: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">context.args.input</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  Operation: pipeline.#GraphqlOperation </span><span style="color: var(--shiki-token-keyword)">&amp;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    Query: </span><span style="color: var(--shiki-color-text)">&quot;&quot;&quot;</span></span>
<span><span style="color: var(--shiki-token-string-expression)">      mutation createCategory ($categoryName: String!) {</span></span>
<span><span style="color: var(--shiki-token-string-expression)">        createCategory (input: { name: $categoryName }) {</span></span>
<span><span style="color: var(--shiki-token-string-expression)">          id</span></span>
<span><span style="color: var(--shiki-token-string-expression)">        }</span></span>
<span><span style="color: var(--shiki-token-string-expression)">		 }</span><span style="color: var(--shiki-color-text)">&quot;&quot;&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">	  }</span></span>
<span><span style="color: var(--shiki-color-text)">	PostScript: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">args.createCategory</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span></span>

In PreScript we can transform the arguments that are going to be passed to the GraphQL query. context.args is an object containing the arguments passed to the pipeline resolver. As we defined in the Inputs property, the input property is going to be an object of type createCategoryForProductInput.

The value of the Query property is the GraphQL query that is going to be executed.

In PostScript we can transform the result of the query, which is going to be assigned to args. Here we do not need to change the shape or contents of the result so we pass the result of the createCategory query to the next step. Specifically, the next step would receive an object called args with the following properties:

<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">&quot;id&quot;</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&quot;&lt;- uuid of the category -&gt;&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

There are 2 ways to access the result value of an already executed pipeline step: args and context.pipeline.{PIPELINE_NAME}.{PROPERTY_NAME}.

In most cases, we recommend using context.pipeline.{PIPELINE_NAME}.{PROPERTY_NAME} because it is accessible to all pipeline steps that occur after the execution of the {PIPELINE_NAME} step.

However, args is only accessible in the pipeline step immediately following the previous step. args holds the result of the PostScript from the previous step.

Here is an example that demonstrates the content of args after each step in the pipeline:

  • step A
  • step B (args contains step A post script data)
  • step C (args contains step B post script data)

Step 2: Use the data in the createCategory as inputs for creating a new Product record.

In the second step, we will create a Product record for the category created in step 1.

<span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  Name:        </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">createProduct</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  Description: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">Create a new Product record with the Category id created in the previous step.</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  Invoker:     settings.adminInvoker</span></span>
<span><span style="color: var(--shiki-color-text)">  PreScript: </span><span style="color: var(--shiki-color-text)">&quot;&quot;&quot;</span></span>
<span><span style="color: var(--shiki-token-string-expression)">    {</span></span>
<span><span style="color: var(--shiki-token-string-expression)">      &quot;categoryID&quot;: context.pipeline.createCategory.id,</span></span>
<span><span style="color: var(--shiki-token-string-expression)">      &quot;title&quot;:  context.args.input.productTitle,</span></span>
<span><span style="color: var(--shiki-token-string-expression)">      &quot;description&quot;:  context.args.input.productDescription,</span></span>
<span><span style="color: var(--shiki-token-string-expression)">    }</span><span style="color: var(--shiki-color-text)">&quot;&quot;&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  Operation: pipeline.#GraphqlOperation </span><span style="color: var(--shiki-token-keyword)">&amp;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    Query: </span><span style="color: var(--shiki-color-text)">&quot;&quot;&quot;</span></span>
<span><span style="color: var(--shiki-token-string-expression)">      mutation createProduct ($categoryID: ID!, $title: String!, $description: String!) {</span></span>
<span><span style="color: var(--shiki-token-string-expression)">        createProduct (input: {categoryID: $categoryID, title: $title, description: $description}) {</span></span>
<span><span style="color: var(--shiki-token-string-expression)">          id</span></span>
<span><span style="color: var(--shiki-token-string-expression)">        }</span></span>
<span><span style="color: var(--shiki-token-string-expression)">      }</span><span style="color: var(--shiki-color-text)">&quot;&quot;&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">	PostScript: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">args.createProduct</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span></span>

We dynamically crafted the input of the GraphQL query in PreScript, setting the categoryID property with the created category.

Finally, in postScript we return the result of the createProduct mutation.

5. Apply changes

Now that our pipeline is ready, we can deploy it using:

<span><span style="color: var(--shiki-token-function)">tailorctl</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">workspace</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">apply</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">-m</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">./workspace.cue</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">-a</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">pipeline</span></span>
<span></span>

6. Confirm the creation of the pipeline

You can view the created pipeline in the Console.

Tailor Console

In the console, select GraphQL Playground to call the pipeline using a mutation such as:

<span><span style="color: var(--shiki-token-keyword)">mutation</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  createCategoryForProduct(input: </span></span>
<span><span style="color: var(--shiki-color-text)">    {</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-string)">categoryName</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-string-expression)">&quot;Jackets&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-string)">productTitle</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-string-expression)">&quot;Recycled Polyester Jacket&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-string)">productDescription</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-string-expression)">&quot;Warm, lightweight jacket made from recycled polyester&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">    })</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

After calling the pipeline, you can view the created Product record in the console.

Tailor Console

You can now view the resolver's execution logs in the console. Select the Execution Logs tab to see the list of logs.

Tailor Console - Pipeline execution logs

Click on View Details to see the input data for each pipeline step.

Tailor Console - Pipeline execution log details

Further Information

In this tutorial we only scratched the surface of what's possible to do using the Pipeline service. For a complete reference, please see the Pipeline Reference.