---
title: Typed processes
description: Use the typed process syntax in Nextflow to define statically typed inputs and outputs for processes.
---
# Typed processes
Typed processes use a new syntax for inputs and outputs that supports static typing.
```nextflow
nextflow.enable.types = true
process hello {
input:
message: String
output:
file('hello.txt')
script:
"""
echo '${message}' > hello.txt
"""
}
```
For an overview of static typing and how to enable it, see [Static typing][static-typing-page].
For the complete syntax reference, see [Process (typed)][syntax-process-typed].
## Inputs
The `input:` section declares process inputs. In typed processes, each input declaration consists of a name and type:
```nextflow
nextflow.enable.types = true
process fastqc {
input:
meta: Map
fastq: Path
extra_args: String
script:
"""
echo 'meta: ${meta}'
echo 'fastq: ${fastq}'
echo 'extra_args: ${extra_args}'
"""
}
```
All [standard types][stdlib-types] except for the dataflow types (`Channel` and `Value`) can be used as type annotations in processes.
### File inputs
Nextflow automatically stages `Path` inputs and `Path` collections (such as `Set`) into the task directory.
Path collections are rendered like any other collection when interpolated into a script (e.g. `[a.fastq, b.fastq]`). Use `join()` to render them as a space-separated list:
```nextflow
nextflow.enable.types = true
process cat {
input:
reads: List
script:
"""
cat ${reads.join(' ')}
"""
}
```
### Nullable inputs
By default, tasks fail if any input receives a `null` value. To allow `null` values, add `?` to the type annotation:
```nextflow
nextflow.enable.types = true
process cat_opt {
input:
input: Path?
stage:
stageAs input, 'input.txt'
output:
stdout()
script:
'''
[[ -f input.txt ]] && cat input.txt || echo 'empty input'
'''
}
```
### Record inputs
Record inputs can be declared using a record type:
```nextflow
nextflow.enable.types = true
process fastqc {
input:
sample: Sample
script:
"""
echo 'id: ${sample.id}'
echo 'fastq: ${sample.fastq}'
"""
}
record Sample {
id: String
fastq: Path
}
```
In this example, the record input is staged as `sample`, and `sample.fastq` is staged as an input file since it is declared with type `Path` in the `Sample` record type.
Each field in the record type is staged into the task the same way as an individual input.
When the process is invoked, the incoming record should contain the specified fields, or else the run will fail. If the incoming record has additional fields not declared by the process input, they are ignored.
Record collections (such as `List`) are staged the same way. Each `Path` field is staged as an input file for every record in the collection, and `null` values of nullable fields are skipped:
```nextflow
nextflow.enable.types = true
process cat_samples {
input:
samples: List
script:
"""
cat ${samples*.fastq.join(' ')}
"""
}
```
Record inputs can also be declared as a *destructured* input:
```nextflow
nextflow.enable.types = true
process fastqc {
input:
record(
id: String,
fastq: Path
)
script:
"""
echo 'id: ${id}'
echo 'fastq: ${fastq}'
"""
}
```
This pattern mirrors the standard `record()` function used to construct records. In this example, `fastq` is staged as an input file since the `fastq` field is declared with type `Path`.
:::tip
Record inputs are a useful way to select a subset of fields from a larger record. This way, the process stages only what it needs, keeping related data together in your workflow logic.
:::
Only the `Path` fields declared by the process input are staged. To avoid staging files that a process doesn't need, omit those fields from the input declaration. If the process needs the metadata of a record but none of its files, declare the input with the generic `Record` type:
```nextflow
nextflow.enable.types = true
process report {
input:
samples: List
script:
"""
echo 'ids: ${samples*.id.join(' ')}'
"""
}
```
In this example, the fields of each record can be accessed in the task, but no files are staged. Any `Path` fields still refer to the original file locations, so they shouldn't be used by the task script.
### Tuple inputs
Tuple inputs can be declared as a *destructured* input:
```nextflow
nextflow.enable.types = true
process fastqc {
input:
tuple(id: String, fastq: Path)
script:
"""
echo 'id: ${id}'
echo 'fastq: ${fastq}'
"""
}
```
This pattern mirrors the standard `tuple()` function used to construct tuples. Each tuple component is staged into the task the same way as an individual input.
## Stage directives
The `stage:` section defines custom staging behavior using *stage directives*. It should be specified after the `input:` section. These directives serve the same purpose as input qualifiers such as `env` and `stdin` in the legacy syntax.
### Environment variables
The `env` directive declares an environment variable in terms of task inputs:
```nextflow
nextflow.enable.types = true
process echo_env {
input:
hello: String
stage:
env 'HELLO', hello
script:
'''
echo "$HELLO world!"
'''
}
```
### Standard input (stdin)
The `stdin` directive defines the standard input of the task script:
```nextflow
nextflow.enable.types = true
process cat {
input:
message: String
stage:
stdin message
script:
"""
cat -
"""
}
```
### Custom file staging
The `stageAs` directive stages an input file (or files) under a custom file pattern:
```nextflow
nextflow.enable.types = true
process blast {
input:
fasta: Path
stage:
stageAs fasta, 'query.fa'
script:
"""
blastp -query query.fa -db nr
"""
}
```
The file pattern can also reference task inputs:
```nextflow
nextflow.enable.types = true
process grep {
input:
id: String
fasta: Path
stage:
stageAs fasta, "${id}.fa"
script:
"""
cat ${id}.fa | grep '>'
"""
}
```
Input files are staged under their original names by default, so two input files with the same name (e.g. from different records in a record collection) cause the task to fail with a file name collision error. Use a file pattern to stage each file in its own directory:
```nextflow
nextflow.enable.types = true
process cat_samples {
input:
samples: List
stage:
stageAs samples*.fastq, 'fastq?/*'
script:
"""
cat ${samples*.fastq.join(' ')}
"""
}
```
In this example, the files are staged as `fastq1/`, `fastq2/`, and so on.
:::note
The `stageAs` directive doesn't accept `null` values. To stage a nullable field, remove the `null` values first, e.g. `stageAs samples*.idx.findAll { idx -> idx != null }, 'idx?/*'`. Wildcards are numbered separately for each `stageAs` directive, after the `null` values are removed, so the files of a record might not have the same number in each directive.
:::
For more information about the available stage directives, see [Inputs and outputs (typed)][process-reference-typed].
## Outputs
The `output:` section declares the outputs of a typed process. Each output declaration consists of a name, an optional type, and an output value:
```nextflow
nextflow.enable.types = true
process echo {
input:
message: String
output:
out_env: String = env('MESSAGE')
out_file: Path = file('message.txt')
out_std: String = stdout()
script:
"""
export MESSAGE='${message}'
echo \$MESSAGE > message.txt
cat message.txt
"""
}
```
When there is only one output, the name can be omitted:
```nextflow
nextflow.enable.types = true
process echo {
input:
message: String
output:
stdout()
script:
"""
echo '${message}'
"""
}
```
For more information about the available output functions, see [Inputs and outputs (typed)][process-reference-typed].
### File outputs
You can use the `file()` and `files()` functions in the `output:` section to get a single file or collection of files from the task directory.
By default, the `file()` function fails if the specified file is not present in the task directory. You can specify `optional: true` to allow missing files. The `file()` function returns `null` for missing files. For example:
```nextflow
nextflow.enable.types = true
process foo {
output:
file('output.txt', optional: true)
script:
"""
exit 0
"""
}
```
Similarly, the `files()` function fails if no files match the given pattern. You can specify `optional: true` to return an empty set instead.
### Structured outputs
Whereas legacy process outputs could only be structured using specific qualifiers like `val` and `tuple`, typed process outputs are plain values.
The `record()` standard library function can be used to create a record:
```nextflow
nextflow.enable.types = true
process fastqc {
input:
record(
id: String,
fastq: Path
)
output:
record(
id: id,
fastqc: file('fastqc_logs')
)
script:
// ...
}
```
The `tuple()` standard library function can be used to create a tuple:
```nextflow
nextflow.enable.types = true
process fastqc {
input:
tuple(id: String, fastq: Path)
output:
tuple(id, file('fastqc_logs'))
script:
// ...
}
```
{/* (process-typed-topics) */}
## Topics
The `topic:` section emits values to [topic channels][channel-topic]. A topic emission consists of an output value and a topic name:
```nextflow
nextflow.enable.types = true
process cat {
input:
message: Path
output:
stdout()
topic:
tuple('bash', eval('bash --version')) >> 'versions'
tuple('cat', eval('cat --version')) >> 'versions'
script:
"""
cat ${message}
"""
}
```
Topic emissions can use the same [output functions][process-reference-typed] as the `output:` section.
See [Collecting values with topic channels][tutorial-topic-channels] for a practical example.
## Script
The `script:` and `exec:` sections behave the same way as [legacy processes][process-script].
## Stub
The `stub:` section behaves the same way as [legacy processes][process-stub].
## Directives
Directives behave the same way as [legacy processes][process-directives].
[channel-topic]: ./reference/stdlib-namespaces/channel#topic
[process-reference-typed]: ./reference/syntax#process-typed
[process-directives]: ./process#directives
[process-script]: ./process#script
[process-stub]: ./process#stub
[static-typing-page]: ./static-typing
[stdlib-types]: ./reference/stdlib-types
[syntax-process-typed]: ./reference/syntax#process-typed
[tutorial-topic-channels]: ./tutorials/topic-channels