Skip to content

Pipes

Pipes transform and validate input data before it reaches the route handler.

Built-in Pipes

Orbit provides several built-in pipes:

ParseIntPipe

typescript
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
  // id is guaranteed to be a number
  return { id };
}

ParseBoolPipe

typescript
@Get()
findAll(@Query('active', ParseBoolPipe) active: boolean) {
  return { active };
}

ParseFloatPipe

typescript
@Get()
findByPrice(@Query('price', ParseFloatPipe) price: number) {
  return { price };
}

ParseArrayPipe

typescript
@Get()
findByIds(@Query('ids', ParseArrayPipe) ids: string[]) {
  return { ids };
}

DefaultValuePipe

typescript
@Get()
findAll(
  @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
  @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number
) {
  return { page, limit };
}

TrimPipe

typescript
@Post()
create(@Body('name', TrimPipe) name: string) {
  // name is trimmed
  return { name };
}

Validation Pipe with Zod

typescript
import { z } from 'zod';
import { ZodValidationPipe } from '@galaxy-stack/orbit-validation';

const CreateUserSchema = z.object({
  name: z.string().min(2).max(100),
  email: z.string().email(),
  age: z.number().min(18).optional(),
});

type CreateUserDto = z.infer<typeof CreateUserSchema>;

@Controller('users')
export class UserController {
  @Post()
  @UsePipes(new ZodValidationPipe(CreateUserSchema))
  create(@Body() dto: CreateUserDto) {
    return dto;
  }
}

Custom Pipes

typescript
import { PipeTransform, Injectable, BadRequestException } from '@galaxy-stack/orbit-core';

@Injectable()
export class ParseUUIDPipe implements PipeTransform<string, string> {
  transform(value: string): string {
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
    
    if (!uuidRegex.test(value)) {
      throw new BadRequestException('Invalid UUID format');
    }
    
    return value;
  }
}

// Usage
@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string) {
  return { id };
}

Async Pipes

typescript
@Injectable()
export class UserExistsPipe implements PipeTransform {
  constructor(private userService: UserService) {}

  async transform(value: string): Promise<User> {
    const user = await this.userService.findOne(value);
    
    if (!user) {
      throw new NotFoundException(`User ${value} not found`);
    }
    
    return user;
  }
}

// Usage
@Get(':id')
findOne(@Param('id', UserExistsPipe) user: User) {
  return user;
}

Global Pipes

typescript
const app = await BunFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());

Pipe Context

Access metadata in custom pipes:

typescript
@Injectable()
export class ContextAwarePipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata): any {
    console.log('Type:', metadata.type); // 'body', 'query', 'param'
    console.log('Data:', metadata.data); // param name
    console.log('Metatype:', metadata.metatype); // expected type
    
    return value;
  }
}

Combining Pipes

Pipes execute in order:

typescript
@Get()
findAll(
  @Query('page', new DefaultValuePipe('1'), ParseIntPipe) page: number
) {
  // DefaultValuePipe sets '1' if missing
  // ParseIntPipe converts to number
  return { page };
}

Released under the MIT License.