Help me de Query Object Pattern te implementeren in mijn REST API voor flexibele querymogelijkheden.
description
Door het gebruik van de Query Object Pattern kan ik schonere en beter onderhoudbare code maken, waardoor complexe queries mogelijk zijn zonder mijn API-endpoints te overladen. Dit verbetert de algehele prestaties en bruikbaarheid van mijn API.
prompt
try_prompt
Ik wil de Query Object Pattern implementeren in mijn REST API om complexe query's mogelijk te maken zonder mijn endpoints te rommelen. Mijn API-doel is {{api_purpose}}. Beschrijf h ... more
generate_helper
...
tags
ai_answers
provider: openai
model: gpt-4.1-nano
De Query Object Pattern biedt een gestructureerde manier om complexe query's te modelleren en te verwerken zonder dat je endpoints onoverzichtelijk worden. Hieronder een beschrijving van hoe je dit kunt implementeren voor een API die gebruikersgegevens beheert:
**1. Structuur van het Query Object**
Maak een gestructureerd data-model (bijvoorbeeld JSON) dat alle mogelijke query-parameters omvat. Een voorbeeldstructuur kan er als volgt uitzien:
```json
{
"filters": {
"and": [
{"field": "age", "operator": "gte", "value": 18},
{"field": "status", "operator": "eq", "value": "active"}
]
},
"sort": [
{"field": "name", "direction": "asc"},
{"field": "createdAt", "direction": "desc"}
],
"pagination": {
"page": 1,
"size": 20
}
}
```
**Toelichting:**
- `filters`: Logische combinaties van voorwaarden (`and`, `or`, genesteld).
- `sort`: Sorteervolgorde.
- `pagination`: Pagina- en paginagrootte-informatie.
**2. Integratie in API-endpoints**
- **Acceptatie van query-object:** Laat je API endpoint het query-object via POST- of GET-parameter(s) ontvangen. Bijvoorbeeld via een POST-verzoek met JSON-body.
- **Parsing en validatie:** Valideer de query-structuur en zet deze om in een database-query.
- **Vertaling naar database:** Converteer de query-structuur naar SQL, ORM-query's, of andere data-access methoden.
**3. Effectief afhandelen van verschillende queryvoorwaarden**
- **Filters:** Bouw dynamische waar-voorwaarden op basis van de `filters`. Bijvoorbeeld:
```pseudo
if filters exist:
for each filter:
voeg toe aan query:
WHERE field operator value
```
- **Logisch combineren:** Ondersteun geneste `and`/`or`-structuren door een boom van voorwaarden te bouwen.
- **Operators:** Ondersteun operators zoals `eq`, `neq`, `lt`, `lte`, `gt`, `gte`, `like`, `in`, etc.
- **Voorbeeldquery's:**
- **Alle actieve gebruikers ouder dan 18:**
```json
{
"filters": {
"and": [
{"field": "status", "operator": "eq", "value": "active"},
{"field": "age", "operator": "gte", "value": 18}
]
}
}
```
- **Gebruikers met naam die begint met 'J' of in een specifieke lijst:**
```json
{
"filters": {
"or": [
{"field": "name", "operator": "like", "value": "J%"},
{"field": "id", "operator": "in", "value": [1, 2, 3]}
]
}
}
```
- **Sorting en paging:**
```json
{
"sort": [
{"field": "name", "direction": "asc"}
],
"pagination": {
"page": 2,
"size": 10
}
}
```
**Samenvatting:**
- Definieer een gestructureerd query-object dat filters, sortering en paginering bevat.
- Ontwikkel een parser die dit object omzet in database-specifieke query's.
- Ondersteun complexe logische voorwaarden via geneste `and`/`or`.
- Test verschillende combinaties om de flexibiliteit en robuustheid te waarborgen.
Met deze aanpak houd je je API endpoints overzichtelijk en maak je het mogelijk om zeer flexibele en complexe queries te ondersteunen.
provider: deepseek
model: deepseek-chat
Hier is een uitgebreide beschrijving voor het implementeren van het Query Object Pattern in je REST API voor user management:
## Query Object Structuur
### Basis Query Object
```typescript
class UserQuery {
filters: Map<string, any> = new Map();
sortBy: string = 'id';
sortOrder: 'asc' | 'desc' = 'asc';
page: number = 1;
limit: number = 50;
includes: string[] = [];
addFilter(field: string, operator: string, value: any): UserQuery {
const key = `${field}__${operator}`;
this.filters.set(key, value);
return this;
}
}
```
### Gespecialiseerde Query Objects
```typescript
class UserFilterQuery extends UserQuery {
static byEmail(email: string): UserFilterQuery {
return new UserFilterQuery().addFilter('email', 'eq', email);
}
static activeUsers(): UserFilterQuery {
return new UserFilterQuery()
.addFilter('status', 'eq', 'active')
.addFilter('last_login', 'gte', new Date(Date.now() - 30 * 24 * 60 * 60 * 1000));
}
static fromLocation(city: string): UserFilterQuery {
return new UserFilterQuery().addFilter('address.city', 'eq', city);
}
}
```
## API Endpoint Integratie
### Basis GET Endpoint
```typescript
// GET /api/users?filters[email__eq]=user@example.com&sortBy=name&page=2
app.get('/api/users', async (req: Request, res: Response) => {
try {
const query = UserQueryBuilder.fromRequest(req.query);
const result = await userService.findUsers(query);
res.json({
data: result.users,
pagination: {
page: query.page,
limit: query.limit,
total: result.total,
totalPages: Math.ceil(result.total / query.limit)
}
});
} catch (error) {
res.status(400).json({ error: error.message });
}
});
```
### Query Builder voor Request Parsing
```typescript
class UserQueryBuilder {
static fromRequest(queryParams: any): UserQuery {
const query = new UserQuery();
// Paginatie
if (queryParams.page) query.page = parseInt(queryParams.page);
if (queryParams.limit) query.limit = parseInt(queryParams.limit);
// Sortering
if (queryParams.sortBy) query.sortBy = queryParams.sortBy;
if (queryParams.sortOrder) query.sortOrder = queryParams.sortOrder;
// Filters parsen
if (queryParams.filters) {
Object.keys(queryParams.filters).forEach(key => {
const [field, operator] = key.split('__');
query.addFilter(field, operator, queryParams.filters[key]);
});
}
// Includes (eager loading)
if (queryParams.includes) {
query.includes = Array.isArray(queryParams.includes)
? queryParams.includes
: queryParams.includes.split(',');
}
return query;
}
}
```
## Query Voorwaarden Afhandeling
### 1. Basis Filtering
```typescript
// Gebruik in service layer
class UserService {
async findUsers(query: UserQuery): Promise<{ users: User[], total: number }> {
let qb = User.createQueryBuilder('user');
// Filters toepassen
query.filters.forEach((value, key) => {
const [field, operator] = key.split('__');
switch (operator) {
case 'eq':
qb = qb.andWhere(`user.${field} = :${field}`, { [field]: value });
break;
case 'neq':
qb = qb.andWhere(`user.${field} != :${field}`, { [field]: value });
break;
case 'contains':
qb = qb.andWhere(`user.${field} LIKE :${field}`, { [field]: `%${value}%` });
break;
case 'in':
qb = qb.andWhere(`user.${field} IN (:...${field})`, { [field]: value.split(',') });
break;
}
});
// Sortering
qb = qb.orderBy(`user.${query.sortBy}`, query.sortOrder.toUpperCase());
// Paginatie
const [users, total] = await qb
.skip((query.page - 1) * query.limit)
.take(query.limit)
.getManyAndCount();
return { users, total };
}
}
```
### 2. Geavanceerde Query Voorbeelden
**Complexe filtering:**
```typescript
// GET /api/users?filters[status__eq]=active&filters[age__gte]=18&filters[role__in]=admin,user
const query = UserQueryBuilder.fromRequest(req.query);
// Resultaat: actieve gebruikers van 18+ met admin of user rol
```
**Datum filtering:**
```typescript
// GET /api/users?filters[created_at__gte]=2024-01-01&filters[last_login__lte]=2024-12-31
const query = UserQueryBuilder.fromRequest(req.query);
// Resultaat: gebruikers aangemaakt in 2024 die ingelogd zijn in 2024
```
**Relatie filtering:**
```typescript
// GET /api/users?filters[profile.bio__contains]=developer&includes=profile
const query = UserQueryBuilder.fromRequest(req.query);
// Resultaat: gebruikers met 'developer' in hun bio, inclusief profiel data
```
### 3. Validatie en Beveiliging
```typescript
class UserQueryValidator {
private static allowedFilters = ['email', 'name', 'status', 'age', 'created_at'];
private static allowedSortFields = ['id', 'name', 'email', 'created_at'];
static validate(query: UserQuery): void {
// Filter velden valideren
query.filters.forEach((value, key) => {
const [field] = key.split('__');
if (!this.allowedFilters.includes(field)) {
throw new Error(`Filtering on field '${field}' is not allowed`);
}
});
// Sorteer velden valideren
if (!this.allowedSortFields.includes(query.sortBy)) {
throw new Error(`Sorting by '${query.sortBy}' is not allowed`);
}
// Paginatie limieten
if (query.limit > 100) {
throw new Error('Limit cannot exceed 100');
}
}
}
```
## Gebruik in Controllers
```typescript
// Specifieke query endpoints
app.get('/api/users/search', async (req: Request, res: Response) => {
const { q, field } = req.query;
let query: UserQuery;
if (field === 'email') {
query = UserFilterQuery.byEmail(q as string);
} else if (field === 'name') {
query = new UserQuery().addFilter('name', 'contains', q);
} else {
query = new UserQuery()
.addFilter('name', 'contains', q)
.addFilter('email', 'contains', q);
}
const result = await userService.findUsers(query);
res.json(result);
});
```
Deze structuur houdt je endpoints schoon en biedt maximale flexibiliteit voor complexe queries zonder dat je steeds nieuwe endpoints hoeft aan te maken.

