Graphql schema storage as dockerized on-premise service for federated graphql gateway server (based on apollo server) as alternative to Apollo studio
- Stores versioned schema for graphql-federated services
- Serves schema for graphql gateway based on provided services & their versions
- Validates new schema to be compatible with other running services
- Provides UI for developers to see stored schema & its history diff
- Stores & shows in UI persisted queries passed by the gateway for debugging
- client tracking (for breaking changes)
- schema usage tracking (for breaking changes)
- separate APQs (use cache only) from backend-registered persisted queries (use DB only)
Assuming you have nvm & docker installed:
nvm use
npm install
npm run build
docker-compose up --build
We rely on docker network and uses hostnames from docker-compose.yml
.
Check app/config.js
to see credentials that node service uses to connect to mysql & redis and change it if you install it with own setup. If you use dynamic service discovery (consul/etcd), edit diplomat.js
The following are the different environment variables that are looked up that allow configuring the schema registry in different ways.
Variable Name | Description | Default |
---|---|---|
DB_HOST | Host name of the MySQL server | gql-schema-registry-db |
DB_USERNAME | Username to connect to MySQL | root |
DB_SECRET | Password used to connect to MySQL | root |
DB_PORT | Port used when connecting to MySQL | 3306 |
DB_NAME | Name of the MySQL database to connect to | schema-registry |
DB_EXECUTE_MIGRATIONS | Controls whether DB migrations are executed upon registry startup or not | true |
REDIS_HOST | Host name of the Redis server | gql-schema-registry-redis |
REDIS_PORT | Port used when connecting to Redis | 6379 |
REDIS_SECRET | Password used to connect to MySQL | Empty |
ASSETS_URL | Controls the url that web assets are served from | localhost:6001 |
On pre-commit / deploy make a POST /schema/validate to see if its compatible with current schema.
On service start-up (runtime), make POST to /schema/push to register schema (see API reference for details). Make sure to handle failure.
Frontend (/client folder) |
Backend (/app folder) |
---|---|
react | nodejs 14 |
apollo client | express, hapi/joi |
styled-components | apollo-server-express, dataloader |
redis 6 | |
knex | |
mysql 8 |
graphql-schema-registry service is one of the components for graphql federation, but it needs tight integration with gateway. Check out examples folder on how to implement it. Note however, that gateway is very simplified and does not have proper error handling, cost limits or fail-safe mechanisms.
Migrations are done using knex
To create new DB migration, use:
npm install knex -g
knex migrate:make my_migration_name_here --migrations-directory migrations
If not using the default configuration of executing DB migrations on service startup, you can run the following npm
command prior to starting the registry:
npm run migrate-db
The command can be prefixed with any environment variable necessary to configure DB connection (in case you ALTER DB with another user), such as:
DB_HOST=my-db-host DB_PORT=6000 npm run migrate-db
- Before making PR, make sure to run
npm run version
& fill CHANGELOG
Original internal mission that resulted in this project consisted of (in alphabetical order):
Simplified version of /schema/compose where latest versions from different services is composed. Needed mostly for debugging
Lists schema based on passed services & their versions. Used by graphql gateway to fetch schema based on current containers
{
"services": [
{"name": "service_a", "version": "ke9j34fuuei"},
{"name": "service_b", "version": "e302fj38fj3"},
]
}
- ✅ 200
{
"success": true,
"data": [
{
"id": 2,
"service_id": 3,
"version": "ke9j34fuuei",
"name": "service_a",
"added_time": "2020-12-11T11:59:40.000Z",
"type_defs": "\n\ttype Query {\n\t\thello: String\n\t}\n",
"is_active": 1
},
{
"id": 3,
"service_id": 4,
"version": "v1",
"name": "service_b",
"added_time": "2020-12-14T18:51:04.000Z",
"type_defs": "type Query {\n world: String\n}\n",
"is_active": 1
}
]
}
- ❌ 400 "services[0].version" must be a string
- ❌ 500 Internal error (DB is down)
- services{ name, version}
If services
is not passed, schema-registry tries to find most recent versions. Logic behind the scenes is that schema with highest added_time
OR updated_time
is picked as latest. If time is the same, schema.id
is used.
Validates and registers new schema for a service.
{
"name": "service_a",
"version": "ke9j34fuuei",
"type_defs": "\n\ttype Query {\n\t\thello: String\n\t}\n"
}
Validates schema, without adding to DB
- name
- version
- type_defs
Compares schemas and finds breaking or dangerous changes between provided and latest schemas.
- name
- version
- type_defs
Deletes specified schema
Property | Type | Comments |
---|---|---|
schemaId |
number | ID of sechema |
Looks up persisted query from DB & caches it in redis if its found
Property | Type | Comments |
---|---|---|
key |
string | hash of APQ (with apq: prefix) |
Adds persisted query to DB & redis cache
Property | Type | Comments |
---|---|---|
key |
string | hash of APQ (with apq: prefix) |
value |
string | Graphql query |