Skeleton NodeJS project for a graphQL server.
This package provides a scaffold to be completed with contents generated by the code-generator.
Clone the repository and use the Code-generator to dynamically generate the contents of four folders with the models information:
- models
- schemas
- resolvers
- migrations
After getting ready the generated code for the models, proceed with the server set up.
$ npm install
$ node migrateDb.js up
$ npm startThe script $ node migrateDb.js will create the tables specified in the migrations folder of each database configuration, using the credentials specified in config/data_models_storage_config.json file.
NOTE: Databases should be already configured locally in config/data_models_storage_config.json.
Create a .env file with your desired environment variables.
ALLOW_ORIGIN- Sets theAccess-Control-Allow-Originheader to the specified value.JWT_SECRET- The secret string used to sign authorization tokens.
MAIL_SERVICE- For bulk add operations, the email service to use for sending progress reports.MAIL_HOST- Email service host (usually SMTP config).MAIL_ACCOUNT- Sender email account address.MAIL_PASSWORD- Sender email account password.
ERROR_LOG- Debug logs verbosity. Can be either "verbose" or "compact". Default value iscompact.EXPORT_TIME_OUT- Maximum amount of time in milliseconds before the server throws a timeout error when exporting data. Default is3600.LIMIT_RECORDS- Maximum number of records that each request can return, default value is10000.PORT- The port where the app is listening, default value is3000POST_REQUEST_MAX_BODY_SIZE- Maximum size of the GraphQL request in MB. Default is1mb.MAX_TIME_OUT- Maximum number of milliseconds that a zendro server will wait to connect with another zendro server. Default value is2000.REQUIRE_SIGN_IN- Boolean to toggle the required sign in to the graphql server. Default istrue.SALT_ROUNDS- Number of salt rounds when hashing a new password. Default is10.
This server is the only service in a Zendro deployment that talks to Keycloak directly - it already owns authorization (ACL), so it also owns authentication. Two things are built on top of that:
/graphiql- the GraphiQL IDE, served from thezendro-graphiqlsubmodule (git submodule update --initthennpm run build:graphiqlto build it). It's purely a UI: it renders a login button and an optional jq/JSONPath filter panel, but holds no credentials itself./auth- the actual OAuth2 Authorization Code + PKCE flow against Keycloak, implemented directly in this repo (utils/auth/), independent of/graphiql-/graphql's and/meta_query's own session middleware depend on it too, and a separate GraphiQL deployment with no Keycloak credentials of its own (likegraphiql-auth) reverse-proxies to it. See "Acting as an auth backend for other origins" below.
AUTH_ENABLED- Enables the/authrouter and the login button on/graphiql. Defaults tofalse.OAUTH2_GRAPHIQL_CLIENT_ID- Confidential Keycloak client id. Defaults tozendro_graphiql.OAUTH2_GRAPHIQL_CLIENT_SECRET- Kept server-side only; never sent to the browser.OAUTH2_GRAPHIQL_ISSUER_URI- Identity provider's OIDC issuer (realm) URL, e.g.http://keycloak/auth/realms/zendro. Endpoints are discovered from<issuerUri>/.well-known/openid-configuration. Must exactly match what the identity provider itself reports as itsissuer.OAUTH2_GRAPHIQL_ISSUER_INTERNAL_URI- Only needed whenOAUTH2_GRAPHIQL_ISSUER_URIisn't network-reachable from this process - e.g. Keycloak in Docker Compose with a fixed public hostname that only the browser can reach.SESSION_SECRET- HMAC secret signing the session and one-time OAuth flow cookies.AUTH_REDIRECT_URI- Comma-separated list. The first entry is this server's own/auth/callbackURL (must be registered on the Keycloak client); every entry is also the allowlist of other origins this server will run login/logout on behalf of (see below).GRAPHIQL_FILTER_ENABLED- Shows the jq/JSONPath filter panel on/graphiql. Defaults tofalse.
A separate GraphiQL deployment that only ever talks to one graphql-server doesn't need its own copy of the Keycloak client config - it can reverse-proxy /auth/* to this server's own /auth/*, the same way it already proxies /graphql and /meta_query.
For this to work, the reverse-proxied /auth/login and /auth/callback requests need to carry an X-Zendro-Auth-Redirect-Uri header set to the proxying frontend's own <its-origin>/auth/callback - otherwise Keycloak would redirect the browser back to this server's own address, which the browser only reached through the proxy, not directly. AUTH_REDIRECT_URI doubles as the allowlist of origins this is honored for (a trailing * matches as a prefix) - this is exactly the same list of patterns registered as valid redirect URIs on the Keycloak client, so there's no second, independently-maintained list. An unrecognized header is silently ignored and this server falls back to its own static redirect URI.
The actual cryptographic safety of this still rests on Keycloak: it independently enforces that the redirect_uri sent at token-exchange time exactly matches the one used at authorization time, and that value must be registered on the client. The allowlist here only prevents this server from being used as an open redirect_uri echo for origins nobody approved.
/graphql and /meta_query don't need any special handling on the proxy's part beyond forwarding the request (including cookies) as-is: the session cookie set during the proxied /auth/callback is opaque to the proxying frontend and gets read directly by this server's own session middleware once the request arrives here.
If you followed the example for generating the code described here, you can try the next queries and mutations. Otherwise, just adapt the same queries and mutations for your own models generated.
We will add the next 4 people to our table people.
| Name (firstName) | Last Name (lastName) | Email (email) |
|---|---|---|
| Albert | Einstein | albert.einstein@science.com |
| Thomas | Edison | thomas.edison@science.com |
| Vincent | van Gogh | vicent.vanGogh@art.com |
| Ludwig | Beethoven | ludwing.beethoven@art.com |
curl -XPOST http://localhost:3000/graphql -H 'Content-Type: application/graphql' -d 'mutation M { addPerson(firstName: "Albert", lastName: "Einstein", email: "albert.einstein@science.com"){ firstName email } }'
As result we will get firsName and email of the person just created:
{
"data": {
"addPerson": {
"firstName": "Albert",
"email": "albert.einstein@science.com"
}
}
}In the same way we add the next 3 people:
curl -XPOST http://localhost:3000/graphql -H 'Content-Type: application/graphql' -d 'mutation M { addPerson(firstName: "Thomas", lastName: "Edison", email: "thomas.edison@science.com") { firstName email } }'
curl -XPOST http://localhost:3000/graphql -H 'Content-Type: application/graphql' -d 'mutation M { addPerson(firstName: "Vicent", lastName: "van Gogh", email: "vicent.vanGogh@art.com"){ firstName email } }'
curl -XPOST http://localhost:3000/graphql -H 'Content-Type: application/graphql' -d 'mutation M { addPerson(firstName: "Ludwig", lastName: "Beethoven", email: "ludwig.beethoven@art.com"){ firstName email } }'We'll search people with 'science' as substring of their email and as result we'll get only their name and last name.
curl -XPOST http://localhost:3000/graphql -H 'Content-Type: application/graphql' -d '{ people(search:{field:email, value:{value:"%science%"}, operator:like}){ firstName lastName}}'
The result will be:
{
"data": {
"searchPerson": [
{
"firstName": "Albert",
"lastName": "Einstein"
},
{
"firstName": "Thomas",
"lastName": "Edison"
}
]
}
}Zendro is the product of a joint effort between the Forschungszentrum Jülich, Germany and the Comisión Nacional para el Conocimiento y Uso de la Biodiversidad, México, to generate a tool that allows efficiently building data warehouses capable of dealing with diverse data generated by different research groups in the context of the FAIR principles and multidisciplinary projects. The name Zendro comes from the words Zenzontle and Drossel, which are Mexican and German words denoting a mockingbird, a bird capable of “talking” different languages, similar to how Zendro can connect your data warehouse from any programming language or data analysis pipeline.
Francisca Acevedo1, Vicente Arriaga1, Katja Dohm3, Constantin Eiteneuer2, Sven Fahrner2, Frank Fischer4, Asis Hallab2, Alicia Mastretta-Yanes1, Roland Pieruschka2, Alejandro Ponce1, Yaxal Ponce2, Francisco Ramírez1, Irene Ramos1, Bernardo Terroba1, Tim Rehberg3, Verónica Suaste1, Björn Usadel2, David Velasco2, Thomas Voecking3, Dan Wang2
- CONABIO - Comisión Nacional para el Conocimiento y Uso de la Biodiversidad, México
- Forschungszentrum Jülich - Germany
- auticon - www.auticon.com
- InterTech - www.intertech.de
Asis Hallab and Alicia Mastretta-Yanes coordinated the project. Asis Hallab designed the software. Programming of code generators, the browser based single page application interface, and the GraphQL application programming interface was done by Katja Dohm, Constantin Eiteneuer, Francisco Ramírez, Tim Rehberg, Veronica Suaste, David Velasco, Thomas Voecking, and Dan Wang. Counselling and use case definitions were contributed by Francisca Acevedo, Vicente Arriaga, Frank Fischer, Roland Pieruschka, Alejandro Ponce, Irene Ramos, and Björn Usadel. User experience and application of Zendro on data management projects was carried out by Asis Hallab, Alicia Mastretta-Yanes, Yaxal Ponce, Irene Ramos, Verónica Suaste, and David Velasco. Logo design was made by Bernardo Terroba.