🚀 Nuova versione beta disponibile! Feedback o problemi? Contattaci

Introduzione ai Risolutori in GraphQL: Il Cuore della Logica Backend

Codegrind TeamSep 03 2024

In GraphQL, i risolutori (resolver) sono il cuore della logica che sta dietro le operazioni di query, mutazioni e subscription. Sono responsabili della gestione e dell’elaborazione dei dati richiesti dagli utenti. I risolutori svolgono un ruolo essenziale nel collegare lo schema GraphQL ai dati reali, che possono provenire da database, API esterne o altri servizi backend. In questo articolo, esploreremo cosa sono i risolutori, come funzionano e come costruirli per gestire le operazioni di GraphQL in modo efficiente.

Cosa sono i Risolutori in GraphQL?

Un risolutore è una funzione associata a un campo nel tuo schema GraphQL. Ogni volta che un client esegue una query o una mutazione, il risolutore viene chiamato per ottenere (o manipolare) i dati necessari per rispondere alla richiesta. I risolutori mappano i campi definiti nello schema ai dati effettivi provenienti da una fonte, come un database o un’API REST.

Esempio di Schema GraphQL

Considera il seguente schema GraphQL:

type Query {
  user(id: ID!): User
}

type User {
  id: ID!
  name: String!
  email: String!
}

Questo schema definisce una query user che restituisce un oggetto User con i campi id, name e email. Tuttavia, senza risolutori, lo schema non sa come ottenere i dati effettivi per popolare questi campi.

Come Funzionano i Risolutori

Un risolutore è una funzione che segue una firma specifica. Un risolutore di base ha questa forma:

(fieldName: any, args: any, context: any, info: any) => returnType;

Parametri del Risolutore

  1. fieldName: L’oggetto genitore o la risoluzione precedente (ad esempio, quando si risolve un campo di un oggetto).
  2. args: Gli argomenti passati nella query o mutazione dal client (ad esempio, id per un utente).
  3. context: Un oggetto condiviso che contiene informazioni comuni per tutte le operazioni (ad esempio, autenticazione, accesso al database).
  4. info: Informazioni sulla query o l’operazione corrente, inclusi dettagli dello schema, e metadati sull’esecuzione.

Risolutore di Base

Ecco come sarebbe un risolutore per la query user nello schema di esempio:

const resolvers = {
  Query: {
    user: (parent, args, context, info) => {
      const { id } = args;
      // Simulazione di un recupero di dati da un database
      return users.find((user) => user.id === id);
    },
  },
};

In questo esempio, il risolutore user accetta l’ID passato come argomento e restituisce l’utente corrispondente dall’array users. Il risolutore utilizza l’argomento args per ottenere l’ID e accede ai dati per restituire l’utente corretto.

Tipi di Risolutori

In GraphQL, ci sono diversi tipi di risolutori che si occupano di varie operazioni. Ecco i più comuni:

1. Risolutori di Query

I risolutori di query vengono utilizzati per ottenere dati. Sono associati a campi definiti nel tipo Query dello schema.

Esempio

const resolvers = {
  Query: {
    users: () => {
      return users; // Restituisce una lista di utenti
    },
  },
};

2. Risolutori di Mutazione

I risolutori di mutazione vengono utilizzati per modificare o aggiungere dati. Sono associati ai campi definiti nel tipo Mutation.

Esempio

const resolvers = {
  Mutation: {
    createUser: (parent, args, context) => {
      const { name, email } = args;
      const newUser = { id: users.length + 1, name, email };
      users.push(newUser);
      return newUser;
    },
  },
};

3. Risolutori di Campi

I risolutori possono essere associati a singoli campi di un oggetto. Questo è utile quando è necessario risolvere campi derivati o calcolati.

Esempio

const resolvers = {
  User: {
    email: (parent) => {
      // Formatta l'email o esegui una logica aggiuntiva
      return parent.email.toLowerCase();
    },
  },
};

In questo esempio, il risolutore per il campo email formatta l’email dell’utente in lettere minuscole prima di restituirla.

4. Risolutori di Subscription

I risolutori di subscription vengono utilizzati per gestire aggiornamenti in tempo reale. Sono associati al tipo Subscription nello schema GraphQL.

Esempio

const resolvers = {
  Subscription: {
    userAdded: {
      subscribe: (parent, args, { pubsub }) =>
        pubsub.asyncIterator("USER_ADDED"),
    },
  },
};

In questo esempio, il risolutore userAdded utilizza il sistema di pubblicazione/sottoscrizione per inviare aggiornamenti in tempo reale quando un nuovo utente viene aggiunto.

Best Practices per i Risolutori

  1. Mantieni i Risolutori Snelli: Un risolutore dovrebbe essere responsabile solo di ottenere i dati. Logiche complesse o manipolazioni di dati dovrebbero essere gestite in servizi separati o nel livello di business logic.
  2. Usa il Context per la Condivisione dei Dati: Utilizza l’oggetto context per condividere informazioni comuni tra i risolutori, come l’accesso al database, l’autenticazione o altri servizi.
  3. Gestisci gli Errori Adeguatamente: Implementa una corretta gestione degli errori nei risolutori. Quando qualcosa va storto, ad esempio una query fallisce, restituisci messaggi di errore chiari.
  4. Caching Intelligente: Usa tecniche di caching nei risolutori per ridurre il carico su database e API esterne, migliorando le prestazioni.
  5. Lazy Loading: Utilizza il lazy loading per caricare i dati solo quando necessario. Ciò può migliorare le prestazioni e ridurre le richieste superflue.

Conclusione

I risolutori sono una componente cruciale in GraphQL perché gestiscono la logica che collega il frontend ai dati reali. Ogni query, mutazione o subscription in GraphQL viene gestita da un risolutore, che decide come e dove recuperare o modificare i dati. Comprendere come funzionano i risolutori e come configurarli in modo efficace è fondamentale per costruire un’API GraphQL efficiente e manutenibile.

Seguendo le best practices delineate in questo articolo, sarai in grado di creare risolutori performanti e puliti, migliorando così la qualità e la scalabilità della tua API GraphQL.