# Pipes
A pipe is a class annotated which extends the `Pipe` abstract class and override the `transform` method.
```dart
import 'package:serinus/serinus.dart';
class MyPipe extends Pipe {
@override
Future transform(ExecutionContext context) async {
// Transform the data here
}
}
```
Pipes have two typical use cases:
1. **Data Transformation**: Pipes can be used to transform the input data before it reaches the route handler.
2. **Data Validation**: Pipes can also be used to validate the incoming data by checking its structure, types, or values.
In both cases pipes operate on the `ExecutionContext` before it reaches the route handler, allowing for a centralized way to manage and manipulate request data.
Serinus comes with a number of built-in pipes that you can use out-of-the-box. You can also build your own custom pipes. In this chapter, we'll introduce the built-in pipes and show how to bind them to route handlers. We'll then examine several custom-built pipes to show how you can build one from scratch.
::: info
When a Pipe throws an exception it is handled by the exceptions layer. Given the above, it should be clear that when an exception is thrown in a Pipe, no other middleware, hooks or route handlers are subsequently executed.
:::
## Built-in Pipes
Serinus comes with a number of built-in pipes that you can use out-of-the-box. These include:
- DefaultValuePipe
- BodySchemaValidationPipe
- ParseDatePipe
- ParseDoublePipe
- ParseIntPipe
- ParseBoolPipe
## Binding pipes
Pipes can be bound to route handlers or controllers in order to process incoming requests.
### Routes
When binding pipes to routes, you can specify them directly in the route handler definition. For example:
```dart
class AppController extends Controller {
AppController(): super('/') {
on(
Route.get(
'/',
pipes: {
ParseIntPipe('id', bindingType: PipeBindingType.params)
}
),
(RequestContext context) async {
// Handle the request
}
);
}
}
```
In this example, the `ParseIntPipe` is applied to the `id` route parameter, ensuring that the parameter is parsed as an integer before the route handler is executed.
### Controllers
```dart
class AppController extends Controller {
@override
List get pipes => [
ParseIntPipe('id', bindingType: PipeBindingType.params)
]
AppController(): super('/') {
on(
Route.get('/'),
(context) async {
// Handle the request
}
);
}
}
```
In this example, the `ParseIntPipe` is applied to all the routes of the `AppController`, ensuring that the parameter is parsed as an integer before the route handler is executed.
## Global Pipes
Pipes, like hooks, can be applied globally to all routes and controllers. This is useful for applying common transformations or validations across your entire application.
To add a global pipe you need to use the same `use` method that you use for hooks in the application.
```dart
Future main() async {
final application = await serinus.createApplication(
entrypoint: AppModule(),
host: InternetAddress.anyIPv4.address,
logger: ConsoleLogger(prefix: 'Serinus New Logger'),
);
application.use(MyGlobalPipe());
application.serve();
}
```
Once a pipe is added as a global pipe, it will be applied to all incoming requests, regardless of the route or controller.