🚀 Nuova versione beta disponibile! Feedback o problemi? Contattaci

Filtri e Paginazione in GraphQL: Guida Completa

Codegrind Team•Aug 28 2024

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:

  1. Offset-Based Pagination: Utilizza limit e offset per suddividere i risultati in pagine.
  2. 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.