---
description: Enforce parameterized SQL in API code that contains hardcoded queries
globs: src/api/**
alwaysApply: false
---

# Parameterize Hardcoded SQL

Whenever an API file builds SQL directly (e.g., a literal string, `format!`, or interpolation/concatenation), the query should use parameter binding instead of injecting values into the string. Hardcoded SQL often appears in handlers that read path/query parameters and then inline them into `SELECT`/`INSERT`/`UPDATE` statements.

- **Goal**: Keep SQL templates static and pass user input via the driver's parameter binding API (such as positional `?`/`$1` placeholders or a query builder). That prevents SQL injection, keeps query plans cache-friendly, and matches best practices for database drivers.
- **Action**: Replace interpolated strings with placeholders and feed the runtime values as `session.query(sql, (value,))`, `query_builder.bind(value)`, etc. If the query really must be dynamic, build the clauses via helper functions that still pass parameters instead of concatenating user input directly.

```rust
// ❌ BAD
let query = format!("SELECT * FROM events WHERE id = {}", id);
session.query(query, &[]).await?;

// ✅ GOOD
let query = "SELECT * FROM events WHERE id = ?";
session.query(query, (id,)).await?;
```

When you spot hardcoded SQL, document any remaining interpolation (e.g., table names coming from trusted config) and keep the rest parameterized. Use this rule to flag the pattern and remind reviewers to convert it before merging.
