slogan3

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
disclaimerOnPageApi image_legal_disclaimer...

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.