# Querystring & filter helpers
The router includes powerful helpers for working with querystrings and filters in a reactive, type-safe way.
## Querystring helpers
### Basic setup
Configure once in `main.js`:
```javascript
import { configureQuerystring } from '@keenmate/svelte-spa-router/helpers/querystring'
configureQuerystring({
arrayFormat: 'auto' // 'auto', 'repeat', or 'comma'
})
```
### Using shared reactive querystring
```svelte
handleSearch(e.target.value)}
/>
```
### Array format support
The router automatically detects array formats:
```javascript
// Auto-detect (default) - handles both formats
// ?tags=foo&tags=bar → { tags: ['foo', 'bar'] }
// ?tags=foo,bar,baz → { tags: ['foo', 'bar', 'baz'] }
// Explicit repeat format
configureQuerystring({ arrayFormat: 'repeat' })
// ?tags=foo&tags=bar
// Explicit comma format
configureQuerystring({ arrayFormat: 'comma' })
// ?tags=foo,bar,baz
```
### Manual parsing (without configuration)
```javascript
import { parseQuerystring, stringifyQuerystring } from '@keenmate/svelte-spa-router/helpers/querystring-helpers'
// Parse
const parsed = parseQuerystring('search=foo&tags=a,b,c', { arrayFormat: 'auto' })
// { search: 'foo', tags: ['a', 'b', 'c'] }
// Stringify
const qs = stringifyQuerystring({ search: 'foo', tags: ['a', 'b'] }, { arrayFormat: 'repeat' })
// 'search=foo&tags=a&tags=b'
```
## Filter helpers
The filter system supports both flat and structured filter modes for different API requirements.
### Flat mode (default)
Each filter is a separate query parameter:
```javascript
// main.js
import { configureFilters } from '@keenmate/svelte-spa-router/helpers/filters'
configureFilters({ mode: 'flat' })
```
```svelte
```
### Structured mode (OData-style)
Single filter parameter with custom syntax:
```javascript
// main.js
import { configureFilters } from '@keenmate/svelte-spa-router/helpers/filters'
configureFilters({
mode: 'structured',
paramName: '$filter',
parse: (filterString) => {
// Parse "displayName eq 'john' AND status eq 'active'"
const parts = filterString.split(' AND ')
const result = {}
parts.forEach(part => {
const [field, , value] = part.split(' ')
result[field] = value.replace(/'/g, '')
})
return result
},
stringify: (filters) => {
// Convert object to OData filter string
return Object.entries(filters)
.filter(([, v]) => v !== null && v !== undefined)
.map(([k, v]) => `${k} eq '${v}'`)
.join(' AND ')
}
})
```
```svelte
```
### null vs undefined in filters
```javascript
// undefined: Always removes the parameter
await updateFilters({ search: undefined })
// Result: parameter removed from URL
// null: Keeps parameter with empty value
await updateFilters({ search: null })
// Result: ?search=
// For querystring helpers, behavior is configurable:
await updateQuerystring({ search: null }, { dropNull: true }) // Removes it (default)
await updateQuerystring({ search: null }, { dropNull: false }) // Keeps as ?search=null
```