{"id":27093746,"url":"https://github.com/gzileni/grpc-fastify","last_synced_at":"2026-05-08T15:39:46.904Z","repository":{"id":144086817,"uuid":"509370094","full_name":"gzileni/gRPC-fastify","owner":"gzileni","description":"Server API REST Fustily and gRPC Server","archived":false,"fork":false,"pushed_at":"2022-07-01T14:20:45.000Z","size":160,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-23T20:06:26.912Z","etag":null,"topics":["fastify","grpc"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gzileni.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-07-01T08:00:36.000Z","updated_at":"2024-05-02T18:36:23.000Z","dependencies_parsed_at":"2023-04-22T23:46:03.411Z","dependency_job_id":null,"html_url":"https://github.com/gzileni/gRPC-fastify","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gzileni/gRPC-fastify","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gzileni%2FgRPC-fastify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gzileni%2FgRPC-fastify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gzileni%2FgRPC-fastify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gzileni%2FgRPC-fastify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gzileni","download_url":"https://codeload.github.com/gzileni/gRPC-fastify/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gzileni%2FgRPC-fastify/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32786560,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"ssl_error","status_checked_at":"2026-05-08T08:22:45.650Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastify","grpc"],"created_at":"2025-04-06T08:49:54.287Z","updated_at":"2026-05-08T15:39:46.858Z","avatar_url":"https://github.com/gzileni.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gRPC + Fastify\n\nSpesso ci siamo trovati difronte al problema di come rendere performati i nostri progetti API REST, soprattutto quando le applicazioni client richiedono una mole di dati notevole, come nel caso di grossi gestionali, con query SQL complesse eseguite su un database remoto.\n\nPer risolvere questo problema possiamo considerare le elevate prestazioni di _protocol buffer (Protobuf)_ sviluppato e utilizzato da Google per la comunicazione interna dei propri server per diminuire il tempo di latenza nella risposta dei numerosi microservizi collegati tra di loro.\n\nIn questo post aggiungeremo ad un Server REST sviluppato con [Fastify](https://www.fastify.io), un server molto efficiente e decisamente uno dei più veloci framework web, [gRPC](https://grpc.io).\n\nL'archittetura prevede alcuni servizi che ricevono dati da altri microservizi esterni e con una connessione ad un database PostGres remoto.\n\n\u003ccenter\u003e\n\u003cimg src=\"https://github.com/gzileni/gzileni.github.io/raw/master/assets/img/posts/gRPC.jpg\" style=\"width: 50%; margin: 20px;\" /\u003e\n\u003c/center\u003e\n\n## [Introduzione a gRPC](https://www.youtube.com/embed/72mPlAfHIjs)\n\nInnanzitutto facciamo una breve introduzione a **gRPC**, un framework RPC ad utilizzo universale compatibile con diversi linguaggi di programmazione e pensato per ottenere elevate prestazioni grazie al _[protocol buffer](https://developers.google.com/protocol-buffers)_ su HTTP/2 sviluppato da Google che permette di serializzare i dati strutturati, come JSON o XML, tranne per il fatto che gRPC occupa pochissimo spazio ed è molto più veloce. _protocol buffer_ è indipendente dal linguaggio di programmazione ed è definito da un linguaggio di definizione creato nei file _.proto_.\n\n## Installazione\n\nIl progetto crea un backend per un semplice planning dei turni di lavoro per i dipendenti:\n\n```bash\ngit clone https://github.com/gzileni/gRPC-fastify\ncd gRPC-fastify\nnpm install\n```\n\nSuccesivamente è necessario rinominare il file _.env.template_ in _.env_ e sostituire le variabile di un ambiente con valori per la connessione ad un vostro server PostGres e API esterna.\n\n## gRPC Server  \n\nNella cartella gRPC ci sono tutti gli script del server e client gRPC, compreso il file .proto di definizione.\n\n_gRPC/planning.proto_\n\n```proto\n\nsyntax = \"proto3\";\n\npackage planning;\n\nservice Planning {\n\n    /** Employees */\n    rpc GetEmployees(Request) returns (Response_Employee) {}\n    \n    /** Employee Presence */\n    rpc GetPresence(Request) returns (Response_Presence) {}\n    rpc PostPresence(Presence) returns (Response_CRUD) {}\n    rpc PutPresence(Presence) returns (Response_CRUD) {}\n    rpc DelPresence(Presence) returns (Response_CRUD) {} \n\n    /** Vehicles */\n    rpc GetPlanning(Request) returns (Response_Planning) {}\n    rpc PostPlanning(Planning_Payload) returns (Response_CRUD) {}\n    rpc PutPlanning(Planning_Payload) returns (Response_CRUD) {}\n    rpc DelPlanning(Planning_Payload) returns (Response_CRUD) {} \n\n}\n\nmessage Employee {\n    int32 id = 1;\n    string name = 2;\n    string username = 3;\n    string role = 4;\n}\n\nmessage Response_Employee {\n    repeated Employee data = 1;\n}\n\nmessage Request {\n    optional string token = 1;\n    optional string where = 2;\n    optional int32 id = 3;\n    optional string name = 4;\n    optional int32 page = 5;\n    optional int32 rows = 6;\n}\n\nmessage Presence {\n    int32 id = 1;\n    string presence = 2;\n}\n\nmessage Response_Presence {\n    repeated Presence data = 1;\n}\n\nmessage Planning_Message {\n    int32 id = 1;\n    int32 id_employee = 2;\n    int32 id_presence = 3;\n    string date = 4;\n    string employee = 5;\n    string presence = 6;\n}\n\nmessage Planning_Payload {\n    optional int32 id = 1;\n    optional string token = 2;\n    string username = 3;\n    string presence = 4;\n    string date = 5;\n}\n\nmessage Response_Planning {\n    repeated Planning_Message data = 1;\n}\n\nmessage Response_CRUD {\n    string result = 1;\n    string error = 2;\n}\n\n```\n\nNel file di definizione sono definiti i servizi rpc per i metodi POST, PUT, DELETE del Server REST per un classico CRUD. Il file _server.js_ avrà due metodi _start()_ per avviare il server da Fastify e _shutdown()_.\n\n_gRPC/server.js_\n\n```javascript\n\nconst routeguide = require('./planning');\nconst models = require('../models');\nconst api = require('../api');\nconst grpc = require('@grpc/grpc-js');\n\nvar server = null;\n\n/** start gRPC Services */\nconst start = (environments) =\u003e {\n\n  const server_url = `${environments.GRPC_SERVER}:${environments.GRPC_PORT}`;\n  \n  server = new grpc.Server();\n\n  const services = {\n    getPresence: models.Presence.get_gRPC,\n    postPresence: models.Presence.post_gRPC,\n    putPresence: models.Presence.put_gRPC,\n    delPresence: models.Presence.del_gRPC,\n    getPlanning: models.Planning.get_gRPC,\n    postPlanning: models.Planning.post_gRPC,\n    putPlanning: models.Planning.put_gRPC,\n    delPlanning: models.Planning.del_gRPC,\n    getEmployees: api.getEmployees_gRPC,\n  }\n\n  server.addService(routeguide.Planning.service, services);\n  server.bindAsync(server_url, grpc.ServerCredentials.createInsecure(), () =\u003e {\n    server.start();\n  });\n\n}\n\n/**\n * Shutdows gRPC Server\n */\nconst shutdown = async () =\u003e {\n  await server.forceShutdown();\n}\n\nmodule.exports = {\n  start,\n  shutdown\n}\n\n```\n\nmentre il client.js sarà un'istanza creata all'avvio da Fastify connesso al server per eseguire i servizi interni.\n\n_gRPC/client.js_\n\n```javascript\nconst routeguide = require('./planning');\nvar grpc = require('@grpc/grpc-js');\n\nconst server_gRPC = `localhost:${process.env.GRPC_PORT}`;\n\nconst client = process.env.GRPC_PORT !== null \n               \u0026\u0026 process.env.GRPC_PORT !== undefined \n               \u0026\u0026 process.env.GRPC_PORT \u003e 0 ?\n               new routeguide.Planning(server_gRPC, grpc.credentials.createInsecure()) :\n               null;\n\nmodule.exports = client;\n```\n\nBisogna aggiungere l'avvio del server gRPC insieme ad un'istanza del client tramite [Hook onReady](https://www.fastify.io/docs/latest/Reference/Hooks/#onready) di Fastify, prima che il server Fastify cominci ad ascoltare le richieste dei client, ed eseguire lo shutdown prima della chiusura di Fastify nell'[Hook onClose](https://www.fastify.io/docs/latest/Reference/Hooks/#onclose)\n\n_app.js_\n\n```javascript\n\n  fastify.addHook('onReady', (done) =\u003e {\n\n    models.init(fastify.config)\n\n    if (fastify.config.GRPC_PORT !== null \n        \u0026\u0026 fastify.config.GRPC_PORT !== undefined \n        \u0026\u0026 parseInt(fastify.config.GRPC_PORT) !== 0) {\n        \n        /** start server gRPC */\n        fastify.gRPC.server.start(fastify.config);\n        fastify.log.info(`Server gRPC started on PORT ${fastify.config.GRPC_SERVER}:${fastify.config.GRPC_PORT}`);\n\n        /** create client gRPC */\n        if (fastify.gRPC.client !== null \u0026\u0026 fastify.gRPC.client !== undefined) {\n            fastify.gRPC.client.waitForReady(Infinity, (err) =\u003e {\n                fastify.log.info(err !== null \u0026\u0026 err !== undefined ? \n                                 err : \n                                 `Client connected to gRPC Server ${fastify.config.GRPC_SERVER}:${fastify.config.GRPC_PORT}`);\n            })\n        };\n\n    };\n\n    done();\n  });\n\n  fastify.addHook('onClose', async () =\u003e {\n    /** stop gRPC server */\n    fastify.log.info(`shutdown gRPC Server ${fastify.config.GRPC_SERVER}:${fastify.config.GRPC_PORT}`);\n    if (fastify.gRPC.server !== null \u0026\u0026 fastify.gRPC.server !== undefined) {\n      fastify.gRPC.client.close();\n      await fastify.gRPC.server.shutdown();\n    }\n  });\n\n\n```\n\nil progetto prevede la possibilità di non avviare il server gRPC, basterà impostare la variabile di ambiente GRPC_PORT a 0. Anche il routing del server dovrà prevedere entrambe le possibilità, ad esempio avremo endpoints per inserire, modificare e cancellare il tipo di presenza del dipendente, e per il planning come questi:\n\n_routes/v1/planning.js_\n\n```javascript\n'use strict'\nconst _ = require('lodash');\nconst Planning = require('../../models/Planning');\n\nmodule.exports = async (fastify, opts) =\u003e {\n\n    let planning = null;\n    let payload = null;\n\n    fastify.addHook('preHandler', (request, reply, done) =\u003e {\n\n        if (request.method === 'GET') {\n\n            const p = {\n                token: request.user.token,\n                where: request.params.username,\n                page: request.query.page,\n                rows: request.query.rows\n            };\n\n            if (fastify.gRPC.client !== null \u0026\u0026 fastify.gRPC.client !== undefined) {\n                fastify.gRPC.client.getPlanning(p, (error, response) =\u003e {\n\n                    if (error) {\n                        fastify.log.error(error);\n                        reply.status(500).send(error);\n                    } else {\n                        planning = response;\n                        done();\n                    }\n                })\n            } else {\n                Planning.get(p).then(response =\u003e {\n                    planning = response;\n                    done();\n                }).catch(error =\u003e {\n                    fastify.log.error(error);\n                    reply.status(500).send(error);\n                })\n            }\n        } else {\n\n            payload = {\n                id: request.params.id ? request.params.id : null,\n                token: request.user.token,\n                username: request.body.username,\n                presence: request.body.presence,\n                date: request.body.date\n            }\n\n            done();\n        }\n    });\n\n    /**\n     * GET employees status\n     */\n    fastify.get('/planning', { response: fastify.schemas.response }, (request, reply) =\u003e {\n\n        _.forEach(planning.data, item =\u003e {\n            fastify.resource.link('items', encodeURI(`${request.url}/${item.username}`));\n        });\n\n        reply.status(200).send({\n            ...planning,\n            ...fastify.resource.toJSON()\n        })\n\n    });\n\n    /**\n     * \n     */\n    fastify.get('/planning/:username', { response: fastify.schemas.response }, (request, reply) =\u003e {\n\n        reply.status(200).send({\n            ...planning,\n            ...fastify.resource.toJSON()\n        })\n\n    });\n\n    /**\n     * POST new status\n     */\n    fastify.post('/planning', { body: fastify.schemas.bodyPlanning }, (request, reply) =\u003e {\n        \n        fastify.payload_events = {\n            key: 'presence',\n            value: 'new',\n        };\n\n        if (fastify.gRPC.client !== null \u0026\u0026 fastify.gRPC.client !== undefined) {\n            fastify.gRPC.client.postPlanning(payload, (error, response) =\u003e {\n                if (error) {\n                    reply.status(500).send(error);\n                } else {\n                    reply.status(201).send(response);\n                }\n            })\n        } else {\n            Presence.post(payload).then(response =\u003e {\n                reply.status(201).send(response)\n            }).catch(error =\u003e {\n                reply.status(500).send(error)\n            })\n        };\n    \n    });\n\n    /**\n     * PUT update status\n     */\n    fastify.put('/planning/:id', { body: fastify.schemas.bodyPlanning }, (request, reply) =\u003e {\n        \n        fastify.payload_events = {\n            key: 'presence',\n            value: 'edit',\n        };\n\n        if (fastify.gRPC.client !== null \u0026\u0026 fastify.gRPC.client !== undefined) {\n            fastify.gRPC.client.putPlanning(payload, (error, response) =\u003e {\n                if (error) {\n                    reply.status(500).send(error);\n                } else {\n                    reply.status(201).send(response);\n                }\n            })\n        } else {\n            Presence.put(payload).then(response =\u003e {\n                reply.status(201).send(response)\n            }).catch(error =\u003e {\n                reply.status(500).send(error)\n            })\n        };\n    \n    });\n\n    /**\n     * DELETE status\n     */\n    fastify.delete('/planning/:id', (request, reply) =\u003e {\n        fastify.payload_events = {\n            key: 'presence',\n            value: 'delete',\n        }\n\n        if (fastify.gRPC.client !== null \u0026\u0026 fastify.gRPC.client !== undefined) {\n            fastify.gRPC.client.delPlanning(payload, (error, response) =\u003e {\n                if (error) {\n                    reply.status(500).send(error);\n                } else {\n                    reply.status(201).send(response);\n                }\n            })\n        } else {\n            Status.del(payload).then(response =\u003e {\n                reply.status(201).send(response)\n            }).catch(error =\u003e {\n                reply.status(500).send(error)\n            })\n        };\n\n    });\n\n}\n\n```\n\nIl repository completo è disponibile [qui](https://github.com/gzileni/gRPC-fastify), insieme alle configurazioni per avviare un container Docker.\n\nInfine avviate il server con i comandi:\n\n```javascript\nnpm start\n```\n\nor \n\n```\nnpm run dev\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgzileni%2Fgrpc-fastify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgzileni%2Fgrpc-fastify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgzileni%2Fgrpc-fastify/lists"}