Come costruire un’API GraphQL con Nest.js?

In questo articolo, imparerete a costruire un’API GraphQL con NestJS, sfruttando la sua architettura pulita e la sicurezza dei tipi.

NestJS è un framework NodeJS che aiuta gli utenti a creare applicazioni backend scalabili di livello aziendale, fornendo sicurezza di tipo e un modello architettonico rigoroso.

GraphQL è un linguaggio di interrogazione per le interfacce di programmazione delle applicazioni (API) ampiamente utilizzato come alternativa a REST grazie alla sua velocità e stabilità, che ottiene rispondendo alle query con i dati esattamente richiesti. Questa caratteristica di GraphQL risolve il problema dell’overfetching e dell’underfetching posto da REST.

Impostazione dell’ambiente di sviluppo

Se la CLI di Nest.js non è installata globalmente sul sistema, eseguire il comando seguente per installarla:

npm i -g @nestjs/cli

La CLI di Nest.js contiene diversi comandi che aiutano a generare codice boilerplate per facilitare il processo di sviluppo.

Quindi, creare il progetto e generare i file di avvio necessari eseguendo il comando seguente:

nest new graphql-api

Quindi, eseguire il comando seguente per accedere alla cartella del progetto:

cd graphql-api

Quindi, installare le dipendenze necessarie eseguendo il comando seguente:

npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-express

Le dipendenze installate sopra includono:

  • nest/graphql
  • nestjs/apollo
  • graphql
  • apollo-server-express

Quindi, installare il pacchetto TypeORM e il driver del database sqlite eseguendo il comando seguente:

npm install @nestjs/typeorm typeorm sqlite3

Per connettersi, leggere e scrivere su un database è necessario disporre dei pacchetti installati in precedenza.

Configurazione di GraphQL in Nest.js

Nest.js offre due modi per creare un’API GraphQL: approccio code-first e schema-first.

L’approccio code-first prevede l’uso di decoratori e classi TypeScript per generare lo schema GraphQL corrispondente.

L’approccio schema-first prevede l’uso dei file GraphQL Schema Definition Language (SDL), mentre Nest.js genera definizioni TypeScript utilizzando classi o interfacce basate sullo schema GraphQL.

In questa esercitazione si utilizzerà l’approccio code-first per evitare il passaggio da un linguaggio all’altro.

Per utilizzare l’approccio basato sul codice, spostarsi nel file di ingresso dell’applicazione, app.module.ts, e importare GraphQLModule da @nestjs/graphql. In questo modo:

import { GraphQLModule } from '@nestjs/graphql';

Quindi, nell’array imports, richiamare il metodo forRoot del GraphQLModule. Questo metodo prende come parametro un oggetto di configurazione. Le opzioni di configurazione vengono passate attraverso il driver GraphQL sottostante (questa esercitazione prevede l’uso del driver Apollo). Impostare la proprietà autoSchemaFile nell’oggetto di configurazione a schema.gql. In questo modo:

//app.module.ts
GraphQLModule.forRoot({ autoSchemaFile: 'schema.gql' }),

La proprietà autoSchemaFile è il percorso del file in cui verrà memorizzato lo schema generato automaticamente.

Connessione a un database

Per collegare l’applicazione a un database, spostarsi nel file app.module.ts e importare TypeOrmModule da @nestjs/typeorm.

Così:

import { TypeOrmModule } from '@nestjs/typeorm';

Quindi, aggiungere il blocco di codice sottostante all’array imports:

TypeOrmModule.forRoot({
      type: 'sqlite',
      database: ':memory:',
      entities: ['dist/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),

Il blocco di codice precedente crea una connessione al database tra l’applicazione e un database Sqlite e condivide la connessione con tutti i moduli dell’applicazione.

Generazione di risorse

Quindi, creare un modulo per organizzare il codice eseguendo il comando seguente:

nest g module blog

Il comando precedente genera un modulo blog per l’applicazione.

Quindi, eseguire il comando seguente per creare un servizio:

nest g service blog

Il blocco di codice precedente genera e registra un servizio nell’applicazione. Questo servizio conterrà e gestirà tutta la logica di business dell’applicazione.

Quindi, eseguire il comando seguente per creare un resolver:

nest g resolver

Il blocco di codice precedente genera e registra un resolver nell’applicazione. Un resolver specifica le istruzioni per trasformare le query e le mutazioni GraphQL in dati e restituisce la forma dei dati specificata in uno schema in modo sincrono o asincrono.

Creazione di un’entità

Un’entità è un insieme di campi che specifica il modo in cui i dati vengono memorizzati in un database.

Per creare un’entità, nella cartella del modulo (blog), creare un file entità seguendo la convenzione .entity.ts, ad esempio blog.entity.ts.

Nel file dell’entità, importare la colonna, l’entità e la colonna PrimaryGeneratedColumn da typeorm. In questo modo:

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

Il decoratore Column aggiunge i campi che annota come colonne in un database. Il decoratore Entity contrassegna la classe che annota come entità. Il decoratore PrimaryGeneratedColumn contrassegna la proprietà che annota come identificatore univoco della tabella e la genera automaticamente.

Quindi, creare ed esportare una classe, Blog. Quindi, annotare la classe con il decoratore Entity per contrassegnarla come entità. In questo modo:

@Entity()
export class Blog {}

Poiché questa esercitazione presenta un’API per blog, aggiungere alla classe le proprietà id, title, body, author e date. Quindi, annotare ciascuna proprietà con il decoratore Column, tranne la proprietà id. Annotate la proprietà id con il decoratore PrimaryGeneratedColumn.

Così:

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Blog {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  body: string;

  @Column()
  author: string;

  @Column()
  date: string;
}

Creare un tipo di oggetto

Un tipo di oggetto è una definizione di schema GraphQL che rappresenta l’oggetto con cui il client deve interagire. Ad esempio, se si sta costruendo un’API per un blog, sarà necessario definire lo schema/entità del blog con una classe TypeScript e annotare ogni campo con i decoratori appropriati.

Quindi, nel file dell’entità, importare il tipo Int e i decoratori Field e ObjectType da @nestjs/graphql. In questo modo:

import { Field, Int, ObjectType } from '@nestjs/graphql';

Il decoratore Field contrassegna una proprietà specifica della classe come campo GraphQL. Solo le proprietà annotate con questo decoratore saranno definite nello schema. Accetta come argomenti una funzione opzionale di tipo return e un oggetto di configurazione.

Il tipo Int è necessario per eliminare potenziali ambiguità tra i sistemi di tipi TypeScript e GraphQL. È richiesto solo per le proprietà con valori numerici, che possono essere sia interi che float.

Il decoratore ObjectType contrassegna una classe come tipo GraphQL.

Quindi, annotare la classe Blog con il decoratore ObjectType per contrassegnarla come tipo GraphQL. In questo modo:

@ObjectType()
export class Blog {}

Infine, specificare le proprietà necessarie del tipo all’interno della classe e annotare ciascuna di esse con il decoratore Field. Per esempio:

//blog.entity.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
@ObjectType()
export class Blog {
  @PrimaryGeneratedColumn()
  @Field((type) => Int)
  id: number;

  @Column()
  @Field()
  title: string;

  @Column()
  @Field()
  body: string;

  @Column()
  @Field()
  author: string;

  @Column()
  @Field()
  date: string;
}

Il tipo di oggetto finito dovrebbe assomigliare al blocco di codice qui sopra.

Si noti che l’id con un tipo di numero nel blocco di codice precedente è stato esplicitamente specificato come un intero, per evitare ambiguità tra i sistemi di tipi di TypeScript e GraphQL.

Infine, iniettare l’entità nell’applicazione per creare un livello di accesso tra l’applicazione e il database.

A tale scopo, spostarsi nel file blog.module.ts, importare TypeOrmModule da @nestjs/typeorm e importare l’entità Blog.

Quindi, aggiungere il blocco di codice sottostante al decoratore del modulo:

imports: [TypeOrmModule.forFeature([Blog])],

Il blocco di codice precedente crea un repository che fungerà da livello di accesso tra l’applicazione e il database.

Creazione di un servizio

Passare al file del servizio generato in precedenza, blog.service.ts, e importare InjectRepository da @nestjs/typeorm e il proprio tipo di oggetto.

Così:

import { InjectRepository } from '@nestjs/typeorm';
import { Blog } from './blog.entity';

Quindi, aggiungere il blocco di codice sottostante alla classe BlogService per iniettare il repository:

constructor(
    @InjectRepository(Blog)
    private blogRepository: Repository<Blog>,
  ) {}

Quindi, aggiungere il blocco di codice sottostante alla classe di servizio per implementare la logica di recupero di tutti i blog presenti nel database:

async getAll(): Promise<Blog[]> {
    return await this.blogRepository.find();
  }

Successivamente, sarà necessario implementare la logica per aggiungere un nuovo blog al database, ma prima è necessario creare un tipo di input.

Creazione di un tipo di ingresso

Per creare un tipo di input per le mutazioni che accettano oggetti complessi, creare una cartella dtos nella cartella del blog. Quindi, nella cartella dtos, creare un nuovo file, create-blog.input.ts.

Nel file create-blog.input.ts, importare i decoratori Field e InputType. In questo modo:

import { Field, InputType } from '@nestjs/graphql';

Il decoratore InputType contrassegna una classe come tipo di input GraphQL.

Quindi, creare ed esportare una classe, BlogInput. Quindi, annotare la classe con il decoratore InputType. In questo modo:

@InputType()
export class BlogInput {}

Infine, aggiungere tutte le proprietà del blog necessarie e annotarle con il decoratore Field.

Così:

//create-blog.input.ts
import { Field, InputType } from '@nestjs/graphql';

@InputType()
export class BlogInput {
  @Field()
  id: number;

  @Field()
  title: string;

  @Field()
  body: string;

  @Field()
  author: string;

  @Field()
  date: string;
}

Il tipo di input finito dovrebbe assomigliare al blocco di codice qui sopra.

Quindi, importare il tipo di input nel file del servizio e aggiungere il blocco di codice sottostante alla classe del servizio per implementare la logica di aggiunta di un blog al database:

async createBlog(blogInput: BlogInput): Promise<Blog> {
    const newBlog = this.blogRepository.create(blogInput);

    return this.blogRepository.save(newBlog);
  }

Il tipo di input viene applicato nello stesso modo in cui viene applicato un normale oggetto di trasferimento dati Nest.js, come meccanismo di validazione dei dati in arrivo. Come per i dto, si possono eseguire ulteriori convalide sui dati, utilizzando il pacchetto class-validator.

Creare un resolver

Passare al file Resolver generato in precedenza, blog.resolver.ts, e importare l’entità, il tipo di input e il servizio. In questo modo:

import { Blog } from './blog.entity';
import { BlogService } from './blog.service';
import { BlogInput } from './dto/create-blog.input';

Quindi, aggiungere il blocco di codice sottostante alla classe Resolver per iniettare il servizio nel resolver:

constructor(private blogService: BlogService) {}

Quindi, importare i decoratori Args, Mutation e Query da @nestjs/graphql.

Così:

import { Args, Mutation, Query } from '@nestjs/graphql';

Il decoratore Args estrae l’oggetto argomenti dalla piattaforma sottostante e popola il parametro decorato con il valore di tutti gli argomenti o di un singolo argomento specificato.

Il decoratore Mutazione specifica un percorso come una mutazione.

Il decoratore Query specifica una rotta come una query.

Quindi, passare la funzione sottostante nel decoratore Resolver per specificare il tipo di oggetto:

(of) => Blog

Quindi, aggiungere il blocco di codice sottostante alla classe Resolver per risolvere il metodo getAll.

 @Query((type) => [Blog])
  async getAllBlogs() {
    return this.blogService.getAll();
  }

Infine, aggiungere il blocco di codice sottostante alla classe Resolver per risolvere il metodo createBlog.

 @Mutation((returns) => Blog)
  createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
    return this.blogService.createBlog(blogInput);
  }

Nota: assicurarsi di specificare la funzione del tipo di ritorno per ogni decoratore.

La classe Resolver finita dovrebbe assomigliare al blocco di codice sottostante:

@Resolver((of) => Blog)
export class BlogResolver {
  constructor(private blogService: BlogService) {}

  @Query((type) => [Blog])
  async getAllBlogs() {
    return this.blogService.getAll();
  }

  @Mutation((returns) => Blog)
  createBlog(@Args('blogInput') blogInput: BlogInput): Promise<Blog> {
    return this.blogService.createBlog(blogInput);
  }
}

Testare l’API utilizzando GraphQL Playground

Per testare l’applicazione utilizzando l’area di gioco GraphQL, disponibile per impostazione predefinita, eseguire il comando seguente e navigare all’indirizzo http://localhost:3000/graphql.

npm start

Successivamente, si dovrebbe vedere un’area di gioco in cui è possibile testare le query e le mutazioni, come mostrato nell’immagine seguente.

Parco giochi GraphQL

Per recuperare tutti i blog dal database, eseguire la query seguente:

query getAll{
  getAllBlogs {
    id,
    title,
    body,
    author,
    date
  }
}

Un esempio è mostrato nell’immagine seguente:

Per aggiungere un blog al database, eseguire la mutazione seguente:

mutation createBlog {
  createBlog (blogInput: {
    id: 1, 
    title: "GraphQL is great", 
    body: "GraphQL APIs are faster than REST APIs", 
    author: "David Ekete",
    date: "01-02-03"
  }) {
    id,
    title,
    body,
    author,
    date,
  }
}

Un esempio è mostrato nell’immagine seguente:

Parco giochi GraphQL

Back4app e GraphQL

A causa dei numerosi vantaggi che GraphQL offre rispetto a REST, come il controllo del fetch dei dati, la piattaforma Back4app fornisce un’API GraphQL integrata nelle applicazioni parse in esecuzione sulla piattaforma.

Questa integrazione consente di verificare lo stato di salute del server, di eseguire operazioni di creazione, lettura, aggiornamento e cancellazione (CRUD) sul database e molto altro ancora scrivendo semplici query e mutazioni GraphQL.

Ad esempio, per verificare lo stato di salute del server backend utilizzando l’API GraphQL, eseguire la query seguente:

query Health {
  health
}

La query di cui sopra restituisce un risultato:

{
  "data": {
    "health": true
  }
}

Per iniziare a utilizzare la funzione GraphQL, navigare nella dashboard di Parse e selezionare e fare clic sulla propria applicazione. Nell’interfaccia utente dell’applicazione, selezionare e fare clic su Core, quindi selezionare e fare clic su Console API e scegliere la console GraphQL.

Seguendo le istruzioni di cui sopra, si aprirà una console GraphQL in cui è possibile eseguire le query e le mutazioni simili all’area di gioco GraphQL utilizzata nello sviluppo. Per saperne di più sulla funzione GraphQL di Back4app, consultare la documentazione di Back4app.

Conclusione

Questo articolo ha illustrato come creare un’API GraphQL in Nest.js e testare l’API con il GraphQL playground. Le API GraphQL presentano diversi vantaggi rispetto alle API REST.

Tuttavia, non sono ideali per tutte le applicazioni. Assicuratevi di considerare i pro e i contro di GraphQL e REST prima di scegliere quale sarebbe adatto alla vostra applicazione.

Indipendentemente dalla scelta, GraphQL o REST, è comunque necessario distribuire l’applicazione. Back4app è un BaaS low-code basato su tecnologie open-source che semplifica il processo di creazione del backend eliminando le attività ripetitive grazie a modelli riutilizzabili e gestendo la maggior parte dell’infrastruttura del backend.

Alcune delle caratteristiche di Back4app includono un database in tempo reale, funzioni cloud, API e SDK nativi, un sistema completo di gestione degli utenti e molto altro ancora.

Questa piattaforma dispone anche di un livello gratuito che consente di esplorare la piattaforma senza pagare una tariffa.

Buona costruzione!

FAQ

Che cos’è GraphQL?

GraphQL è un linguaggio open source per la manipolazione e l’interrogazione dei dati per API, creato da Facebook nel 2012.

Che cos’è Nest.JS?

Nest.JS è un framework Node.js open source per la creazione di applicazioni lato server efficienti e scalabili.

Come creare una API GraphQL con Nest.JS?

– Configura GraphQL in Nest.js
– Connettiti a un database
– Genera risorse
– Crea un’entità
– Crea un tipo di oggetto
– Crea un servizio


Leave a reply

Your email address will not be published.