Filtri e Paginazione in GraphQL: Guida Completa
L’implementazione di filtri e paginazione nelle query GraphQL è essenziale per gestire grandi set di dati in modo efficiente, migliorando le prestazioni delle API e l’esperienza utente. Con filtri ben progettati, i client possono ottenere solo i dati rilevanti, mentre la paginazione consente di recuperare i dati in modo frammentato, riducendo il carico sul server e ottimizzando il traffico di rete. In questa guida, esploreremo come implementare filtri e paginazione in GraphQL, con esempi pratici e best practices.
1. Filtri in GraphQL
I filtri permettono ai client di richiedere solo i dati che soddisfano determinati criteri, rendendo le query più flessibili e riducendo il volume di dati trasferiti.
1.1. Definire Filtri nello Schema
I filtri possono essere implementati utilizzando input types, che permettono di passare vari criteri di ricerca.
Esempio di Definizione di Filtri
Supponiamo di avere un sistema di gestione di prodotti, e vogliamo filtrare i prodotti in base al nome, al prezzo o alla disponibilità .
input ProductFilter {
name: String
priceMin: Float
priceMax: Float
inStock: Boolean
}
type Query {
products(filter: ProductFilter): [Product!]!
}
type Product {
id: ID!
name: String!
price: Float!
inStock: Boolean!
}
1.2. Implementare i Filtri nei Resolvers
Nel resolver, puoi implementare la logica di filtraggio utilizzando i valori forniti nei filtri.
Esempio di Implementazione in JavaScript
const products = [
{ id: "1", name: "Laptop", price: 1000, inStock: true },
{ id: "2", name: "Phone", price: 500, inStock: false },
{ id: "3", name: "Tablet", price: 300, inStock: true },
];
const resolvers = {
Query: {
products: (parent, { filter }) => {
let filteredProducts = products;
if (filter) {
if (filter.name) {
filteredProducts = filteredProducts.filter((product) =>
product.name.toLowerCase().includes(filter.name.toLowerCase())
);
}
if (filter.priceMin !== undefined) {
filteredProducts = filteredProducts.filter(
(product) => product.price >= filter.priceMin
);
}
if (filter.priceMax !== undefined) {
filteredProducts = filteredProducts.filter(
(product) => product.price <= filter.priceMax
);
}
if (filter.inStock !== undefined) {
filteredProducts = filteredProducts.filter(
(product) => product.inStock === filter.inStock
);
}
}
return filteredProducts;
},
},
};
1.3. Esempio di Query con Filtri
I client possono ora inviare query con filtri specifici per ottenere i dati che desiderano.
query {
products(filter: { name: "lap", priceMin: 800, inStock: true }) {
id
name
price
}
}
2. Paginazione in GraphQL
La paginazione è una tecnica utilizzata per suddividere i dati in pagine più piccole, riducendo il carico sul server e migliorando l’efficienza delle query.
2.1. Tipi di Paginazione
Ci sono due approcci principali per implementare la paginazione in GraphQL:
- Offset-Based Pagination: Utilizza
limit
eoffset
per suddividere i risultati in pagine. - Cursor-Based Pagination: Utilizza cursori per gestire la paginazione, utile per evitare problemi come la duplicazione di record.
2.2. Offset-Based Pagination
Esempio di Definizione nello Schema
type Query {
products(limit: Int, offset: Int): [Product!]!
}
Implementazione del Resolver
const resolvers = {
Query: {
products: (parent, { limit, offset }) => {
return products.slice(offset, offset + limit);
},
},
};
Esempio di Query con Offset-Based Pagination
query {
products(limit: 2, offset: 0) {
id
name
price
}
}
2.3. Cursor-Based Pagination
La Cursor-Based Pagination utilizza un campo univoco come cursore per identificare l’ultima posizione nella lista di risultati.
Esempio di Definizione nello Schema
type PageInfo {
endCursor: String
hasNextPage: Boolean!
}
type ProductEdge {
cursor: String!
node: Product!
}
type ProductConnection {
edges: [ProductEdge!]!
pageInfo: PageInfo!
}
type Query {
products(first: Int, after: String): ProductConnection!
}
Implementazione del Resolver
const resolvers = {
Query: {
products: (parent, { first, after }) => {
let startIndex = 0;
if (after) {
const cursorIndex = products.findIndex(
(product) => product.id === after
);
startIndex = cursorIndex + 1;
}
const slicedProducts = products.slice(startIndex, startIndex + first);
const edges = slicedProducts.map((product) => ({
cursor: product.id,
node: product,
}));
return {
edges,
pageInfo: {
endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : null,
hasNextPage: startIndex + first < products.length,
},
};
},
},
};
Esempio di Query con Cursor-Based Pagination
query {
products(first: 2, after: "1") {
edges {
cursor
node {
id
name
price
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
3. Best Practices per Filtri e Paginazione
3.1. Combinare Filtri e Paginazione
Permetti ai client di combinare filtri e paginazione per ottenere esattamente i dati di cui hanno bisogno, senza sovraccaricare il server.
query {
products(filter: { priceMin: 100 }, first: 2, after: "1") {
edges {
node {
id
name
price
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
3.2. Fornire Feedback Chiaro sui Limiti
Quando implementi la paginazione, assicurati di fornire feedback chiaro su quali dati sono disponibili, utilizzando campi come hasNextPage
e endCursor
.
3.3. Implementare Paginazione nei Resolvers
Gestisci la logica di filtraggio e paginazione nei resolvers, garantendo che le query rimangano efficienti e scalabili, specialmente su grandi dataset.
3.4. Gestire l’Accesso ai Dati
Quando implementi filtri e paginazione, assicurati di gestire correttamente i permessi di accesso ai dati, specialmente in sistemi con livelli di autorizzazione complessi.
Conclusione
L’implementazione di filtri e paginazione in GraphQL è essenziale per creare API efficienti e scalabili. Queste tecniche permettono ai client di ottenere solo i dati rilevanti, riducendo il carico sul server e migliorando l’esperienza utente. Seguendo le best practices e utilizzando gli esempi forniti in questa guida, sarai in grado di costruire query flessibili e ottimizzate che migliorano le prestazioni e la manutenibilità delle tue applicazioni GraphQL.