Advanced dynamic endpoints functions¶
Advanced
In the Template functions section of the Advanced templates part of our docs, we introduce some functions that help you create more advanced dynamic templates. In this guide, we’ll create some examples to illustrate how they can be used and show you some tricks that will improve the experience of creating dynamic endpoints with Tinybird.
Before going on, learn the basics about what we’ll talk about here by taking a look, if you haven’t already, at:
Our Quick Start (CLI) guide
The Query parameters section of our docs
This post on on Creating an API with dynamic parameters on stock market data.
The data¶
We’ll use the e-commerce events data enriched with products. The data looks like this:
Tips and tricks¶
When the complexity of Pipes and endpoints grows, developing them and knowing what’s going-on to debug problems can become challenging. Here are some tricks that we use when using our product for ourselves and for our clients that we think would be useful for you as well:
WHERE 1=1¶
When you filter by different criteria, given by dynamic parameters that can be omitted, you’ll need a WHERE
clause. But if none of the parameters are present, you’ll need to add a WHERE
statement with a dummy condition (like 1=1
) that’s always true, and then add the other filter statements dynamically if the parameters are defined, like we do in the defined example of this guide.
set¶
The set function present in the previous snippet lets you set the value of a parameter in a node, so that you can check the output of a query depending on the value of the parameters it takes. Otherwise, you’d have to to publish an endpoint and make requests to its endpoint with different parameters. Using set
, you don’t have to exit the Tinybird UI while creating an endpoint and the whole process is faster, without needing to go back and forth between your browser or IDE and Postman (or whatever you use to make requests).
Another example of its usage:
You can use more than one set
statement. Just put each one on a separate line at the beginning of a node.
set
is also a way to set defaults for parameters. If you used set
statements to test your endpoint while developing, remember to remove them before publishing your code.
The default argument¶
Another way to set default values for parameters is using the default
argument that most Tinybird template functions accept. The previous code could be rewritten as follows:
Keep in mind that defining the same parameter in more than one place in your code in different ways can lead to inconsistent behaviour. Here’s a solution to avoid that:
Using WITH statements to avoid duplicating code¶
If you are going to use the same dynamic parameters more than once in a node of a Pipe, it’s good to define them in one place only to avoid duplicating code. It also makes it clearer knowing which parameters are going to appear in the node. This can be done with one or more statements at the beginning of a node, using the WITH
clause.
The WITH clause in ClickHouse supports CTEs. They’re preprocessed before executing the query, and they can only return one row (this is different to other databases such as Postgres). This is better seen with a live example:
Documenting your endpoints¶
Tinybird creates auto-generated documentation for all your published endpoints, taking the information from the dynamic parameters found in the Pipe. It’s best practice to set default values and descriptions for every parameter in one place (also because some functions don’t accept a description, for example). We normally do that in the final node, with WITH
statements at the beginning. See how we’d do it in the last section of this guide.
Debugging any query¶
We have an experimental feature that lets you see how the actual SQL code that will be run on ClickHouse for any published endpoint looks, interpolating the query string parameters that you pass in the request URL. If you have a complex query and you’d like to know what is the SQL that will be run, let us know and we’ll give you access to this feature to debug a query.
Now let’s explore some of the Tinybird advanced template functions, what they allow you to do, and some tricks that will improve your experience creating dynamic endpoints on Tinybird.
Advanced functions¶
Most of these functions also appear in the Advanced templates section of our docs. Here we’ll provide practical examples of their usage so that it’s easier for you to understand how to use them.
defined¶
The defined
function lets you check if a query string parameter exists in the request URL or not.
Imagine you want to filter events with a price within a minimum or a maximum price, set by two dynamic parameters that could be omitted. A way to define the endpoint would be like this:
To see the effect of having a parameter not defined, use set
to set its value to None
like this:
You could also provide some smart defaults to avoid needing to use the defined
function at all:
Array(variable_name, ‘type’, [default])¶
Transforms a comma-separated list of values into a Tuple. You can provide a default value for it or not:
To filter events whose type belongs to the ones provided in a dynamic parameter, separated by commas, you’d define the endpoint like this:
And then the URL of the endpoint would be something like https://api.tinybird.co/v0/pipes/your_pipe_name.json?event_types=buy,view
sql_and¶
sql_and
lets you create a filter with AND
operators and several expressions dynamically, taking into account if the dynamic parameters in a template it are present in the request URL.
It’s not possible to use ClickHouse functions inside the {{ }}
brackets in templates. sql_and
can only be used with the``{column_name}__{operand}`` syntax. This function does the same as what you saw in the previous query: filtering a column by the values that are present in a tuple generated by Array(...)
if operand
is in
, are greater than (with the gt
operand), or less than (with the lt
operand). Let’s see an example to make it clearer:
You don’t have to provide default values. If you set the defined
argument of Array
to False
, when that parameter is not provided, no SQL expression will be generated. You can see this in the next code snippet:
split_to_array(name, [default])¶
This works similarly to Array
, but it returns an Array of Strings (instead of a tuple). You’ll have to cast the result to the type you want after. As you can see here too, they behave in a similar way:
One thing that you’ll want to keep in mind is that you can’t pass non-constant values (arrays, for example) to operations that require them. For example, this would fail:
If you find an error like this, you should use a Tuple instead (remember that {{Array(...)}}
returns a tuple). This will work:
split_to_array
is often used with enumerate_with_last.
column and columns¶
They let you select one or several columns from a Data Source or Pipe, given their name. You can also provide a default value.
enumerate_with_last¶
As the docs say, it creates an iterable array, returning a Boolean value that allows checking if the current element is the last element in the array. Its most common usage is to select several columns, or compute some function over them. We can see an example of columns
and enumerate_with_last
here:
If you use the defined
function around a parameter it doesn’t make sense to give it a default value because if it’s not provided, that line will never be run.
error and custom_error¶
They let you return customized error responses. With error
you can customize the error message:
And with custom_error
you can also customize the response code:
Note: error
and custom_error
have to be placed at the start of a node or they won’t work. The order should be:
set
lines, to give some parameter a default value (optional)Parameter validation functions:
error
andcustom_error
definitionsThe SQL query itself
Putting it all together¶
We’ve created a Pipe where we use most of these advanced techniques to filter e-commerce events. You can see its live documentation page here and play with it on Swagger here.
This is its code:
To replicate it in your account, copy the previous code to a new file called advanced_dynamic_endpoints.pipe
locally and run `tb push pipes/advanced_dynamic_endpoints.pipe` with our CLI to push it to your Tinybird account.