Integrazione di Database Relazionali in un'API GraphQL con Apollo Server
Integrare un database relazionale, come PostgreSQL o MySQL, in un’API GraphQL con Apollo Server consente di sfruttare la potenza dei database relazionali per gestire dati strutturati in modo efficiente. In questa guida, ti mostrerò come configurare e utilizzare un database relazionale con Apollo Server, utilizzando un ORM (Object-Relational Mapper) come TypeORM o Sequelize per interagire con il database in modo semplice e tipizzato.
Prerequisiti
Prima di iniziare, assicurati di avere:
- Node.js installato
- Un’istanza di PostgreSQL o MySQL in esecuzione (localmente o in cloud)
- Conoscenza di base di GraphQL e Apollo Server
Step 1: Creazione del Progetto e Installazione delle Dipendenze
Inizia creando un nuovo progetto Node.js e installando le dipendenze necessarie:
mkdir my-graphql-api
cd my-graphql-api
npm init -y
npm install apollo-server graphql typeorm reflect-metadata pg
- apollo-server: La libreria per configurare il server GraphQL.
- graphql: La libreria per definire e utilizzare GraphQL.
- typeorm: Un ORM che semplifica l’interazione con database relazionali.
- reflect-metadata: Necessario per la decorazione e la gestione delle classi con TypeORM.
- pg: Il client Node.js per PostgreSQL (per MySQL, sostituisci con
mysql2
).
Step 2: Configurazione di TypeORM con PostgreSQL
Configurazione del Database
Crea un file ormconfig.json
nella radice del progetto per configurare TypeORM:
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "your-username",
"password": "your-password",
"database": "your-database",
"synchronize": true,
"logging": true,
"entities": ["src/entity/**/*.ts"]
}
Sostituisci i valori di username
, password
, e database
con le tue credenziali e il nome del database.
Definizione di un’EntitĂ
Le entitĂ in TypeORM rappresentano le tabelle del database. Crea una directory src/entity
e un file User.ts
per definire l’entità User
:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
}
Questa entitĂ User
rappresenta una tabella nel database con tre colonne: id
, name
, e email
.
Step 3: Configurazione di Apollo Server con GraphQL
Crea il file src/index.ts
per configurare Apollo Server:
import "reflect-metadata";
import { ApolloServer, gql } from "apollo-server";
import { createConnection } from "typeorm";
import { User } from "./entity/User";
createConnection()
.then(async (connection) => {
// Definisci lo schema GraphQL
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
}
type Query {
users: [User]
user(id: ID!): User
}
type Mutation {
createUser(name: String!, email: String!): User
updateUser(id: ID!, name: String, email: String): User
deleteUser(id: ID!): Boolean
}
`;
// Definisci i resolver
const resolvers = {
Query: {
users: async () => {
return await connection.getRepository(User).find();
},
user: async (_: any, { id }: { id: number }) => {
return await connection.getRepository(User).findOne(id);
},
},
Mutation: {
createUser: async (
_: any,
{ name, email }: { name: string; email: string }
) => {
const user = new User();
user.name = name;
user.email = email;
return await connection.getRepository(User).save(user);
},
updateUser: async (
_: any,
{ id, name, email }: { id: number; name?: string; email?: string }
) => {
const user = await connection.getRepository(User).findOne(id);
if (!user) throw new Error("User not found");
if (name) user.name = name;
if (email) user.email = email;
return await connection.getRepository(User).save(user);
},
deleteUser: async (_: any, { id }: { id: number }) => {
const result = await connection.getRepository(User).delete(id);
return result.affected === 1;
},
},
};
// Configura Apollo Server
const server = new ApolloServer({
typeDefs,
resolvers,
});
// Avvia il server
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
})
.catch((error) => console.log(error));
Spiegazione del Codice
- Schema GraphQL: Definiamo un tipo
User
, query per ottenere utenti (users
,user
), e mutazioni per creare, aggiornare e cancellare utenti (createUser
,updateUser
,deleteUser
). - Resolvers: I resolver gestiscono l’interazione con PostgreSQL utilizzando TypeORM per eseguire operazioni CRUD (Create, Read, Update, Delete).
Step 4: Compilazione ed Esecuzione
Assicurati che TypeScript sia installato nel progetto:
npm install typescript ts-node @types/node --save-dev
Aggiungi uno script di avvio al package.json
:
"scripts": {
"start": "ts-node src/index.ts"
}
Esegui il server:
npm start
Il server Apollo sarà avviato e collegato al database PostgreSQL. Puoi accedere a GraphQL Playground all’indirizzo http://localhost:4000/
.
Step 5: Test dell’API GraphQL
Esempio di Query per Ottenere Tutti gli Utenti
query {
users {
id
name
email
}
}
Esempio di Mutazione per Creare un Nuovo Utente
mutation {
createUser(name: "Alice", email: "alice@example.com") {
id
name
email
}
}
Esempio di Mutazione per Aggiornare un Utente
mutation {
updateUser(id: 1, name: "Alice Updated", email: "alice.new@example.com") {
id
name
email
}
}
Esempio di Mutazione per Eliminare un Utente
mutation {
deleteUser(id: 1)
}
Step 6: Best Practices
- Gestione degli Errori: Implementa una gestione robusta degli errori nei resolver per gestire eventuali problemi durante le operazioni con il database.
- Migrazioni: Usa il sistema di migrazioni di TypeORM per gestire i cambiamenti allo schema del database in modo sicuro e controllato.
- Validazione dei Dati: Utilizza decoratori TypeORM come
@IsEmail
,@Length
, ecc., per convalidare i dati prima di salvarli nel database. - Sicurezza: Proteggi le mutazioni sensibili implementando l’autenticazione e l’autorizzazione, specialmente per operazioni come la creazione e la cancellazione di record.
Conclusione
Integrare un database relazionale come PostgreSQL o MySQL in un’API GraphQL con Apollo Server è un processo potente e flessibile. Utilizzando TypeORM, puoi interagire con il database in modo semplice e tipizzato, semplificando le operazioni CRUD e migliorando la manutenibilità del codice. Questa configurazione ti permette di creare API scalabili e sicure, adatte a qualsiasi tipo di applicazione.