Skip to main content

Build a Simple Application

Applications are powerful, reusable groups of derived Components bundled into a single visual unit within your Flow Graph. The Simple Application is a built-in Ascend solution that generates Components from templates using Jinja, enabling flexible and maintainable data pipelines.

When to use Simple Applications

Choose Simple Applications when you need:

  • Template-based Component generation with Jinja
  • SQL, Python, or YAML templates with parameter substitution
  • Multiple related Components that share configuration
  • Reusable patterns across different datasets

For complex logic requiring full programmatic control, see Custom Applications.

Quick start

1. Create template directory

my_project/
├── templates/
│ └── customer_pipeline/
│ ├── read_source.yaml
│ ├── clean_data.sql
│ └── aggregate.py

2. Configure the Application

flows/analytics/customer_analytics.yaml
component:
application:
application_id: simple
config:
template_path: templates/customer_pipeline
parameters:
source_table: raw_customers
min_age: 18
include_inactive: false

Template types

Simple Application processes three types of template files:

ExtensionTypeUse case
.yaml, .ymlYAMLRead Components, Write Components, declarative configurations
.sql, .sql.jinjaSQLTransform Components with SQL logic
.pyPythonTransform Components with Python logic

Context variables

Simple Application provides these variables in all templates:

VariableDescription
_ascend_flow_nameName of the containing Flow
_ascend_component_nameComponent name (without parent prefix)
_ascend_application_component_nameParent Application Component name
_ascend_fully_qualified_component_nameFull name with parent prefix

SQL templates

SQL templates create Transform Components with Jinja templating.

Basic SQL template

templates/filter/filter_data.sql
-- Parameters are directly accessible
SELECT *
FROM {{ ref(input_table) }}
WHERE {{ filter_column }} > {{ threshold }}
{% if include_inactive %}
OR status = 'inactive'
{% endif %}

Reference sibling Components

Use application_subcomponent_ref() to reference other Components in the same Application:

templates/pipeline/transform.sql
-- Reference a sibling component in the same Application
SELECT
s.*,
CURRENT_TIMESTAMP() as processed_at
FROM {{ application_subcomponent_ref('read_source') }} s
WHERE s.value > {{ min_value }}

Reference external Components

Use standard ref() for Components outside the Application:

templates/pipeline/enrich.sql
SELECT
t.*,
l.region_name
FROM {{ application_subcomponent_ref('transform') }} t
LEFT JOIN {{ ref('lookup_regions') }} l
ON t.region_id = l.id
-- Or reference from another Flow
LEFT JOIN {{ ref('dimension_table', flow='dimensions') }} d
ON t.dim_id = d.id

Python templates

Python templates create Transform Components with full Python capabilities.

Basic Python template

templates/pipeline/aggregate.py
from ascend.resources import transform, application_subcomponent_name, application_subcomponent_ref

@transform(name=application_subcomponent_name('aggregate'))
def aggregate(
clean_data=application_subcomponent_ref('clean_data', alias='clean_data'),
context=None
):
# Access parameters from context
group_column = context.parameters.get('group_by', 'category')

return clean_data.group_by(group_column).agg({
'amount': 'sum',
'count': 'count'
})

Python template helpers

FunctionDescription
application_subcomponent_name(name)Returns fully qualified Component name
application_subcomponent_ref(name, alias)Creates reference to sibling Component

Access Application configuration

In Python templates, access Application configuration through context:

templates/pipeline/process.py
from ascend.resources import transform, application_subcomponent_name, ref

@transform(name=application_subcomponent_name('process'))
def process(input_data=ref('source_table'), context=None):
# Access flow parameters
threshold = context.parameters.get('threshold', 100)

# Access application config through application_component_context
app_config = context.application_component_context.config.get('parameters', {})
enable_filter = app_config.get('enable_filter', False)

if enable_filter:
return input_data.filter(input_data['value'] > threshold)
return input_data

YAML templates

YAML templates define declarative Components, typically Read or Write Components.

Read Component template

templates/pipeline/read_source.yaml
component:
read:
connection: {{ connection_name }}
{{ data_platform }}:
table: {{ source_table }}
{% if schema_name is defined %}
schema: {{ schema_name }}
{% endif %}

Write Component template

templates/pipeline/write_output.yaml
component:
write:
input:
name: {{ application_subcomponent_name('transform') }}
flow: {{ _ascend_flow_name }}
connection: {{ output_connection }}
{{ output_platform }}:
table: {{ output_table }}

Complete example

Here's a full Simple Application that builds a customer analytics pipeline.

Directory structure

templates/
└── customer_analytics/
├── read_customers.yaml
├── clean_data.sql
├── calculate_metrics.sql
└── aggregate.py

Template files

templates/customer_analytics/read_customers.yaml
component:
read:
connection: {{ connection_name }}
snowflake:
table: {{ source_table }}
schema: {{ schema_name | default('PUBLIC') }}
templates/customer_analytics/clean_data.sql
-- Clean and filter customer data
SELECT
customer_id,
email,
created_at,
total_purchases
FROM {{ application_subcomponent_ref('read_customers') }}
WHERE created_at >= '{{ start_date }}'
{% if min_purchases is defined %}
AND total_purchases >= {{ min_purchases }}
{% endif %}
templates/customer_analytics/calculate_metrics.sql
-- Calculate customer metrics
SELECT
customer_id,
email,
created_at,
total_purchases,
DATEDIFF('day', created_at, CURRENT_DATE()) as days_since_signup,
total_purchases / NULLIF(DATEDIFF('day', created_at, CURRENT_DATE()), 0) as purchases_per_day
FROM {{ application_subcomponent_ref('clean_data') }}
templates/customer_analytics/aggregate.py
from ascend.resources import transform, application_subcomponent_name, application_subcomponent_ref

@transform(name=application_subcomponent_name('aggregate'))
def aggregate(
metrics=application_subcomponent_ref('calculate_metrics', alias='metrics'),
context=None
):
# Get aggregation level from parameters
agg_column = context.parameters.get('aggregate_by', 'customer_id')

return metrics.group_by(agg_column).agg({
'total_purchases': 'sum',
'purchases_per_day': 'mean',
'customer_id': 'count'
}).rename({'customer_id': 'customer_count'})

Application configuration

flows/analytics/customer_analytics.yaml
component:
application:
application_id: simple
config:
template_path: templates/customer_analytics
parameters:
connection_name: warehouse
source_table: raw_customers
schema_name: SALES
start_date: "2024-01-01"
min_purchases: 5
aggregate_by: region

Reference Application sub-components from outside

From other Components in your Flow, reference Application sub-components using the parent__child naming pattern:

-- In another Component outside the Application
SELECT *
FROM {{ ref('customer_analytics__aggregate') }}
WHERE customer_count > 100

Best practices

  1. Organize templates logically: Group related templates in directories
  2. Use descriptive parameter names: Make configuration self-documenting
  3. Provide defaults: Use {{ param | default('value') }} for optional parameters
  4. Document parameters: Add comments explaining expected parameter values
  5. Test templates: Validate templates work with various parameter combinations
  6. Keep templates focused: Each template should do one thing well

Troubleshooting

IssueCauseSolution
Template not foundIncorrect template_pathVerify path is relative to project root
Parameter undefinedMissing parameter in configAdd parameter or provide default with `
Reference errorWrong component nameUse application_subcomponent_ref() for siblings
Empty outputTemplate file is emptyRemove empty files or add content

Next steps