Sample GraphQL data fetching client for retrieving and processing structured profile information from the R&A Data API.
⚠ Important: This is NOT an official Oracle API client. It is a sample client built for demo purposes. Use it at your own risk.
📌 Table of Contents
- 🚀 Loads connection parameters from environment variables
- 🔄 Automatically refreshes OAuth tokens before expiration
- 📡 Supports GraphQL queries with request/response or request/stream modes
- 📥 Enables streaming mode if
@streamdirective is present - 🗑 Filters out null values from responses (optional)
- 📂 Saves fetched data in structured JSON files
- 📑 Logs execution details at different levels (info, debug, silly)
- 🔧 Supports dynamic query files and filter variables from an external file
- 🔌 Custom plugins (
./src/plugins) for stream processing -fileWriter.tsprovided as sample
Below a conceptual architecture diagram illustrating the workflow of this client:
-
Install Node.js:
It is recommended to use Node Version Manager (nvm) for managing Node versions:
NODE_VERSION=v20.11.0 nvm install $NODE_VERSION nvm alias default $NODE_VERSION nvm use default
-
Install project dependencies:
npm install
Before running the application, set the required environment variables. If testing locally, these can be stored in an .env file:
touch .envHere are the essential environment variables and their purpose:
-
🌍 APIGW_URL: The API Gateway URL for accessing Oracle Hospitality APIs. Example:
APIGW_URL=https://your-api-gateway-url
-
🔑 APP_KEY, CLIENT_ID, CLIENT_SECRET: Credentials required for authentication. Example:
APP_KEY=your-app-key CLIENT_ID=your-client-id CLIENT_SECRET=your-client-secret
-
🏢 ENTERPRISE_ID: The enterprise identifier related to the customer environment. Example:
ENTERPRISE_ID=your-enterprise-id
-
🗑 EXCLUDE_NULL: Set to
trueto remove null values from responses. Example:EXCLUDE_NULL=true
-
📝 PLUGIN_NAME: Specifies which plugin should be used for processing data streams. Example:
PLUGIN_NAME=fileWriter
-
🔍 QUERY_NAME & FILTER_NAME: Define which query and filter files (from
./queriesand./filters) should be used. Example:QUERY_NAME=profileIndividuals FILTER_NAME=profileIndividuals
-
🔄 FILTER_VARS: Defines dynamic variables for filters. Any placeholder like
{{var}}in the filter file will be replaced with the specified values. Example:FILTER_VARS=chainCode:MyChain,hotelId:HOTEL123,limit:100
-
📝 LOGLEVEL: Defines the log verbosity level (
silly,trace,debug,info,warn,error,fatal). Example:LOGLEVEL=info
ℹ️ To use an environment file other than
.env, set theENVPATHvariable, e.g.,export ENVPATH=./dev.env.
Once all required environment variables are set, the project can be executed with:
npm startTo run in development mode:
npm run devYou can extend or customize how streamed GraphQL chunks are processed by dropping your own plugin modules under src/plugins/. Each plugin module should export a default function named processChunk with this signature:
// src/plugins/myPlugin.ts
export default function processChunk(
chunk: any,
config: { fileName: string; outDir: string; [key: string]: any }
): void {
// your processing logic here
}chunkis the parsed JSON for each incremental payloadconfig.fileNameis the base name of the query being executedconfig.outDiris the directory where plugins typically write output- You can add additional keys to
configas needed
To enable your plugin, set PLUGIN_NAME to the module filename (without extension). Example:
PLUGIN_NAME=myCustomPluginQueries live in queries/*.gql and must be valid GraphQL operations. The client supports two modes:
-
Standard request/response
- Any query without the
@streamdirective will be sent via ApolloClient and return a complete response object.
- Any query without the
-
Streaming mode
- Annotate a list field in your query with
@stream(and optional args likeinitialCount). - Example:
query ListProfiles { profileIndividuals @stream(initialCount: 50) { id name { given family } } }
- Chunks are processed as they arrive, passed to your
processChunkplugin.
- Annotate a list field in your query with
Filters are JSON templates with placeholder syntax {{variableName}}. These are replaced at runtime based on the FILTER_VARS env var (e.g. hotelId:HOTEL123,limit:100).
Beyond the core env vars, you can override:
OAUTH_ENDPOINT– Path for token fetch (default/oauth/v1/tokens)DATA_ENDPOINT– Path for GraphQL API (default/rna/v1/graphql)TOKEN_EXPIRY– Milliseconds before the client refreshes the OAuth tokenQUERY_FOLDER,FILTER_FOLDER,PLUGIN_FOLDER– Paths to custom foldersENVPATH– Path to your env file if not.env
- Increase verbosity via
LOGLEVEL(traceorsilly) to see HTTP requests and chunk parsing. - Common errors:
- Missing env vars: The client will throw if
QUERY_NAMEorFILTER_NAMEisn’t set. - Variable replacement: You’ll see an error if any
{{…}}placeholder isn’t provided inFILTER_VARS. - Authentication failures: Invalid credentials return 401/403; check
CLIENT_ID,CLIENT_SECRET, andAPP_KEY. - Network timeouts: Increase
TOKEN_EXPIRYor adjust timeouts inCall.tsif needed.
- Missing env vars: The client will throw if
Logs are printed to stdout; make sure your console or container captures them.
Contributions are welcome! To get involved:
- Fork the repo and create branches against
main. - Open issues for bugs or feature requests.
- Submit PRs with clear descriptions and minimal focused changes.
- Run
npm install, then:npm run lintto check code style.npm run devto test in watch mode.
Please adhere to existing TypeScript conventions and update this README for any new features you add.
This project is available under the Universal Permissive License v 1.0.
See LICENSE for details.
This is NOT an official Oracle product. It is provided for demonstration purposes only, without any guarantees of reliability, accuracy, or completeness. Use it at your own risk.
