🚀 Nuova versione beta disponibile! Feedback o problemi? Contattaci

Scrittura di Query in GraphQL: Guida Completa

Codegrind Team•Sep 04 2024

In GraphQL, le query sono il cuore del sistema di recupero dati, consentendo ai client di richiedere esattamente i dati di cui hanno bisogno in modo flessibile. A differenza delle API REST, dove è spesso necessario fare più richieste per ottenere tutti i dati, in GraphQL puoi richiedere e ricevere solo i dati specifici in un’unica richiesta. Questo articolo esplorerà come scrivere query in GraphQL, con esempi pratici su come implementare query di base, avanzate e ottimizzate.

Cos’è una Query in GraphQL?

Le query sono operazioni di lettura in GraphQL che permettono ai client di ottenere dati dal server. Ogni query specifica i campi e le relazioni che il client desidera ricevere. Le query possono essere semplici, come richiedere un singolo oggetto, oppure complesse, come richiedere una lista di oggetti con dati correlati.

Esempio di Query di Base

query {
  user(id: "1") {
    id
    name
    email
  }
}

In questo esempio:

  • La query richiede i dettagli di un utente specifico (user) passando il parametro id.
  • Restituisce i campi id, name, e email relativi all’utente richiesto.

Scrivere una Query in GraphQL

Una query in GraphQL consiste in un insieme di campi specificati dal client, che il server risolverĂ . Ogni campo nel risultato della query deve essere esplicitamente richiesto.

1. Query Semplice

Un esempio di query per ottenere i dettagli di un singolo utente:

query {
  user(id: "1") {
    id
    name
    email
  }
}

Questa query richiede i campi id, name e email per l’utente con id = 1. Il server risponderà con i dettagli dell’utente, come definito nello schema.

2. Query su PiĂš Oggetti

È possibile eseguire query che restituiscono elenchi di oggetti, come tutti gli utenti o tutti i post.

query {
  users {
    id
    name
    email
  }
}

Questa query restituisce una lista di utenti, ognuno dei quali ha i campi id, name e email.

3. Relazioni tra Oggetti

Le query GraphQL supportano relazioni tra oggetti. Puoi richiedere dati correlati in una singola query.

Esempio: Relazione Utente-Post

query {
  user(id: "1") {
    id
    name
    posts {
      title
      content
    }
  }
}

In questo esempio:

  • La query richiede i dati di un utente e i relativi post.
  • Per ogni post, vengono restituiti il titolo e il contenuto.

4. Passare Argomenti alle Query

Le query possono accettare argomenti che permettono di filtrare i risultati o recuperare informazioni specifiche.

Esempio di Query con Argomenti

query {
  post(id: "101") {
    id
    title
    author {
      name
    }
  }
}

In questo esempio:

  • La query richiede un post specifico utilizzando l’argomento id.
  • Per quel post, restituisce l’id, il title e il nome dell’author.

Implementazione dei Risolutori di Query

Un risolutore di query è una funzione che restituisce i dati richiesti in una query. Ogni campo in una query GraphQL può avere un risolutore associato che gestisce il recupero dei dati.

Esempio di Risolutore per una Query Semplice

const resolvers = {
  Query: {
    user: async (parent, { id }, context) => {
      // Recupera l'utente dal database utilizzando l'id
      return await context.db.getUserById(id);
    },
  },
};

In questo esempio:

  • La query user recupera un utente in base all’id fornito.
  • Utilizza una funzione getUserById nel contesto per recuperare i dati dal database.

Esempio di Risolutore con Relazioni

Quando si lavora con dati relazionali, come utenti e post, è necessario scrivere risolutori per ogni relazione.

const resolvers = {
  Query: {
    user: async (parent, { id }, context) => {
      return await context.db.getUserById(id);
    },
  },
  User: {
    posts: async (user, args, context) => {
      return await context.db.getPostsByUserId(user.id);
    },
  },
};

In questo esempio:

  • Il risolutore user restituisce un utente.
  • Il risolutore posts nel tipo User recupera tutti i post associati a quell’utente.

Ottimizzazione delle Query

In GraphQL, è importante ottimizzare le query per evitare problemi di prestazioni, soprattutto quando si gestiscono relazioni complesse o grandi volumi di dati.

1. Evitare il Problema N+1

Il problema N+1 si verifica quando una query che richiede dati correlati esegue troppe chiamate al database. Ad esempio, se recuperi 10 utenti e poi esegui una query separata per ogni post, avrai 1 query per ottenere gli utenti e N query per i post correlati (dove N è il numero di utenti).

Soluzione: Utilizzare DataLoader

DataLoader è uno strumento che consente di batchare e memorizzare in cache le richieste, riducendo il numero di query eseguite.

const DataLoader = require("dataloader");

const postLoader = new DataLoader(async (userIds) => {
  const posts = await context.db.getPostsByUserIds(userIds);
  return userIds.map((id) => posts.filter((post) => post.userId === id));
});

const resolvers = {
  User: {
    posts: (user, args, context) => postLoader.load(user.id),
  },
};

In questo esempio:

  • DataLoader raggruppa le richieste per i post degli utenti, riducendo il numero di query necessarie per recuperare i dati correlati.

2. Paginazione

La paginazione è essenziale per gestire query che possono restituire molti risultati. Senza paginazione, una query potrebbe restituire troppi dati, causando rallentamenti.

Esempio di Paginazione

query {
  users(limit: 10, offset: 0) {
    id
    name
  }
}

In questo esempio:

  • La query users accetta due argomenti: limit per limitare il numero di risultati e offset per specificare l’inizio del set di dati.
  • Questo consente di paginare i risultati, richiedendo solo 10 utenti alla volta.

3. Caching per Migliorare le Performance

Se i dati richiesti non cambiano frequentemente, puoi utilizzare il caching per ridurre il carico sul database e migliorare le performance delle query.

Esempio di Caching con Redis

const redis = require("redis");
const client = redis.createClient();

const resolvers = {
  Query: {
    user: async (parent, { id }, context) => {
      const cacheKey = `user:${id}`;
      const cachedUser = await client.get(cacheKey);

      if (cachedUser) {
        return JSON.parse(cachedUser);
      }

      const user = await context.db.getUserById(id);
      if (user) {
        client.set(cacheKey, JSON.stringify(user), "EX", 3600); // Cache per 1 ora
      }

      return user;
    },
  },
};

In questo esempio, i dati dell’utente vengono memorizzati nella cache per un’ora, riducendo la necessità di interrogare il database ogni volta.

Best Practices per la Scrittura di Query

1. Richiedere Solo i Dati Necessari

GraphQL ti consente di richiedere solo i dati di cui hai bisogno, quindi evita di fare overfetching (richiedere dati non necessari).

Esempio di Query Ottimizzata

query {
  user(id: "1") {
    name
    email
  }
}

In questo esempio, vengono richiesti solo name e email, ignorando gli altri campi.

2. Gestione degli Errori

Assicurati di gestire correttamente gli errori nei risolutori, restituendo messaggi significativi al client in caso di problemi.

const resolvers = {
  Query: {
    user: async (
      parent,

      { id },
      context
    ) => {
      try {
        const user = await context.db.getUserById(id);
        if (!user) {
          throw new Error("Utente non trovato");
        }
        return user;
      } catch (error) {
        throw new Error(`Errore nel recupero dell'utente: ${error.message}`);
      }
    },
  },
};

3. Paginazione e Filtri

Implementa la paginazione e i filtri per evitare di sovraccaricare il server quando si recuperano elenchi di oggetti.

query {
  users(limit: 10, offset: 20, filter: { name: "Alice" }) {
    id
    name
  }
}

In questo esempio, viene applicato un filtro per ottenere solo utenti con nome “Alice”, oltre a limitare il numero di risultati.

Conclusione

Le query in GraphQL offrono un modo potente e flessibile per recuperare i dati, consentendo ai client di richiedere esattamente ciò di cui hanno bisogno in un’unica richiesta. Scrivere query efficienti richiede una comprensione delle relazioni tra i dati, l’uso di tecniche di ottimizzazione come DataLoader per evitare il problema N+1, e l’implementazione della paginazione e del caching per migliorare le prestazioni. Seguendo queste best practices, puoi costruire API GraphQL efficienti, scalabili e facili da usare.