Skip to content

Zendro-dev/graphql-server

 
 

Repository files navigation

Zendro GraphQL Server

Skeleton NodeJS project for a graphQL server.

This package provides a scaffold to be completed with contents generated by the code-generator.

Set up

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 start

The 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.

Environment Variables

Create a .env file with your desired environment variables.

Mandatory

  • ALLOW_ORIGIN - Sets the Access-Control-Allow-Origin header to the specified value.
  • JWT_SECRET - The secret string used to sign authorization tokens.

Optional (without defaults)

  • 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.

Optional (with sensible defaults)

  • ERROR_LOG - Debug logs verbosity. Can be either "verbose" or "compact". Default value is compact.
  • EXPORT_TIME_OUT - Maximum amount of time in milliseconds before the server throws a timeout error when exporting data. Default is 3600.
  • LIMIT_RECORDS - Maximum number of records that each request can return, default value is 10000.
  • PORT - The port where the app is listening, default value is 3000
  • POST_REQUEST_MAX_BODY_SIZE - Maximum size of the GraphQL request in MB. Default is 1mb.
  • MAX_TIME_OUT - Maximum number of milliseconds that a zendro server will wait to connect with another zendro server. Default value is 2000.
  • REQUIRE_SIGN_IN - Boolean to toggle the required sign in to the graphql server. Default is true.
  • SALT_ROUNDS - Number of salt rounds when hashing a new password. Default is 10.

GraphiQL & Authentication

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 the zendro-graphiql submodule (git submodule update --init then npm run build:graphiql to 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 (like graphiql-auth) reverse-proxies to it. See "Acting as an auth backend for other origins" below.

Auth environment variables

  • AUTH_ENABLED - Enables the /auth router and the login button on /graphiql. Defaults to false.
  • OAUTH2_GRAPHIQL_CLIENT_ID - Confidential Keycloak client id. Defaults to zendro_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 its issuer.
  • OAUTH2_GRAPHIQL_ISSUER_INTERNAL_URI - Only needed when OAUTH2_GRAPHIQL_ISSUER_URI isn'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/callback URL (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 to false.

Acting as an auth backend for other origins

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.

Examples

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

CREATE PERSON

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 } }'

SEARCH PEOPLE WITH FILTER

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"
      }
    ]
  }
}

Contributions

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.

Zendro contributors in alphabetical order

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

Author affiliations

  1. CONABIO - Comisión Nacional para el Conocimiento y Uso de la Biodiversidad, México
  2. Forschungszentrum Jülich - Germany
  3. auticon - www.auticon.com
  4. InterTech - www.intertech.de

Zendro author contributions

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.

About

Skeleton NodeJS project for a graphQL server.

Resources

License

Stars

0 stars

Watchers

2 watching

Forks

Packages

 
 
 

Contributors

Languages

  • JavaScript 100.0%