GraphQL, KOA e MongoDB: come creare una semplice API sfruttando la versatilità di GraphQL

Danilo Del Fio
Aug 19, 2019 · 5 min read
GraphQL, KOA, MongoDB header Image
GraphQL, KOA, MongoDB header Image

In un precedente articolo (REST API or GraphQL that is the question), ho cercato di spiegare il perché è utile utilizzare GraphQL anziché REST per esporre le nostre API.

In questo articolo vorrei presentare un esempio completo che fa uso di di Koa come Web Framework di node.js (nel precedente avevo utilizzato ExpressJS), MongoDB come database e utilizzare Mongoose come Object Modeling.

Per prima cosa inizializziamo il nostro progetto e installiamo le librerie di cui abbiamo bisogno:

> mkdir koa-graphql-mongodb-example
> cd koa-graphql-mongodb-example
> npm init -y
> npm i mongoose
> npm i graphql
> npm i koa
> npm i koa-mount koa-graphql

Per avviare in modalità watch il progetto è necessario installare pm2 (PM2 è un gestore di processi di produzione per applicazioni Node.js con un load balancer integrato).

npm i pm2 -g

A questo punto il nostro file “package.json”, sarà:

{   "name": "koa-graphQL-mongo-example",
"version": "1.0.0",
"description": "Simple GraphQL API...",
"main": "server.js",
"scripts": {
"start": "pm2 start server.js"
},
"keywords": [],
"author": "...",
"license": "ISC",
"dependencies": {
"graphql": "^14.4.2",
"koa": "^2.7.0",
"koa-graphql": "^0.8.0",
"koa-mount": "^4.0.0",
"mongoose": "^5.6.9"
}
}

Database

Creare un db su una vostra istanza di MongoDB. Se non ne avete una locale, consiglio il servizio offerto da mlab, che offre un DB as a service per MongoDB.

Per connetterci al database, creiamo un file chiamato database.js e creiamo una connessione attraverso mongoose, che poi importeremo nel nostro server.js.

const mongoose = require('mongoose');
const initDB = () => {
mongoose.connect(
'mongodb://127.0.0.1:27017/koa-graphql',
{ useNewUrlParser: true }
);
mongoose.connection.once('open', () => {
console.log('Connesso al db...');
});
}
module.exports = initDB;

Il database creato, nel mio caso, si chiama koa-graphql, ma potete dagli qualunque nome desideriate.

Una nota importante nel caso abbiate delle credenziali per accedere al database. In questo caso, la connessione ad esso, dovrà avere la seguente firma:

mongoose.connect(
'mongodb://user:password@127.0.0.1:27017/koa-graphql',
{ useNewUrlParser: true }
);

Per importare ed utilizzare tale configurazione nel nostro progetto, sarà necessario importare il file database.js e chiamare il metodo initDB (che abbiamo opportunamente esportato. Il nostro file server.js, tra le altre cose, conterrà le seguenti linee di codice:

...
const initDB = require('./database');
...
initDB();

Nel database che ho creato in locale, ho aggiunto una collection chiamata projects ed ho inserito un semplice documento di esempio:

Tale elemento sarà quello che andremo a ricercare nella nostra prima query.

Mongoose

Come accennato in precedenza, per accedere ad database dall’applicazione node.js, ho utilizzato mongoose, col quale ho mappato la collection appena creata, attraverso la sintassi:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ProjectSchema = new Schema({
name: String,
last_update: String,
owner: String,
type: String,
});module.exports = mongoose.model('Project', ProjectSchema);

GraphQL

Occupiamoci ora di creare le nostra API che ci permetterà di ottenere la risorsa appena creata.

Innanzitutto creiamo un “tipo di oggetto” che mapperà la nostra risorsa:

const graphql = require('graphql');
const { GraphQLObjectType, GraphQLString } = graphql;
const ProjectType = new GraphQLObjectType({
name: 'Project',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
last_update: { type: GraphQLString },
owner: { type: GraphQLString },
type: { type: GraphQLString }
})
});
module.exports = ProjectType;

Le risorse di questo tipo si chiamano ‘Project’ e sono caratterizzate dagli attributi definiti all’interno della funzione fields: ()=> {}.

Al livello più alto di ogni server GraphQL c’è un tipo che rappresenta tutti i possibili punti di ingresso nell’API GraphQL, spesso viene chiamato Root Type o Query Type.

ProjectType appena creato, lo utilizziamo per creare la nostra prima RootQueryType di GraphQL:

const { GraphQLSchema, GraphQLObjectType, GraphQLString } = require('graphql');const projectGraphQLType =  require('./projectType');
const Project = require('../models/projects');
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
project: {
type: projectGraphQLType,
args: { id: { type: GraphQLString }},
resolve(parent, args) {
return Project.findById(args.id);
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery
});

Come possiamo vedere dal codice, la nostra semplice QueryType ha un nome prefissato ‘RootQueryType’, mentre la nostra risorsa che vogliamo esporre è definita all’interno dell’attributo fields.

La risorsa a cui potremmo accedere da qualsivoglia frontend l’abbiamo chiamata, con molta fantasia, project, il cui tipo è quello che abbiamo definito nella sezione precedente.

Tale risorsa potrà essere cercata attraverso l’argomento ‘id’ che, se chiamato, permetterà l’esecuzione della query mongoose definita nella funzione resolve() (In questo caso Project.findById()).

Mettiamo insieme i pezzi

Nell’esempio descritto abbiamo creato un database locale, definito una collection al suo interno e inserito un documento in essa.

Abbiamo mappato, attraverso mongoose, la collection appena creata e l’abbiamo agganciata alla prima “query” di GraphQL che sarà la nostra Query Root di tutte le nostre API esposte (la cui forma l’abbiamo definita attraverso la definizione di un ProjectType.

A questo punto non ci resta che esporre la nostra API attraverso KOA. Per far questo, nel codice d’ingresso della nostra applicazione, ovvero nel file server.js, scriveremo:

const Koa = require('koa');
const mount = require('koa-mount');
const graphqlHTTP = require('koa-graphql');
const schema = require('./graphql/schema');
const initDB = require('./database');
const app = new Koa();
app.use(mount('/api', graphqlHTTP({
schema: schema,
graphiql: true
})));
app.listen(9000);
initDB();
app.on('error', err => {
log.error('server error', err)
});

Nel codice definiamo la nostra ‘applicazione” KOA a cui montiamo i nostri endpoint app.use(mount(‘/api’, graphqlHTTP({}))), che vengono configurati puntando al nostro schema (creato nella sezione precedente) e a cui forniamo un ulteriore parametro graphiql: true (GraphiQL è uno strumento integrato nel browser per la scrittura, la convalida e il test di query GraphQL.).

Test

Per testare la nostra prima API, basta lanciare il nostro server node.js, attraverso il comando

npm start

A questo punto, se apriamo il browser all’indirizzo http://localhost:9000/api come abbiamo definito nel nostro server.js, visualizzeremo l’interfaccia GraphiQL:

La query che ho specificato per ottenere il risultato mostrato nell’esempio é:

{
project(id: "5d52d1efffac403dce11d114") {
name
owner
type
}
}

In questo modo stiamo chiedendo al nostro server GraphQL di eseguire una query alla nostra risorsa project attraverso il parametro id (che abbiamo recuperato dell’id del documento salvato nel database), e fornirci come risultato un json contenente soltanto gli attributi name, owner e type.

Codice dell’esempio

Potete trovare tutto il codice dell’esempio nel seguente GitHub repo.

Conclusioni

Questa introduzione aveva come scopo quello di creare le basi per l’implementazione di un backend che facesse uso di un server GraphQL, del Web Framework Koa e del database MongoDB a cui accediamo attraverso il framework Mongoose.

Nel prossimo articolo, vedremo anche come gestire la persistenza di una risorsa attraverso l’uso delle mutation.

Danilo Del Fio

Written by

Crazy developer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade