├── .gitignore ├── README.md ├── examples ├── countriesFlow.json └── githubLogin.json ├── graphql.html ├── graphql.js ├── graphql.png ├── images ├── customHeaders.png ├── editGraphQL.png ├── flow.png ├── flowOutput.png ├── githubFlow.png ├── githubGraphql.png └── githubGraphqlConfig.png ├── locales └── en-US │ └── graphql.json ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *~ 3 | .DS_Store 4 | .history 5 | icons 6 | node_modules 7 | .vscode 8 | temp.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-red-contrib-graphql 2 | 3 | [![Platform](https://img.shields.io/badge/platform-Node--RED-red)](https://nodered.org) 4 | ![Release](https://img.shields.io/npm/v/node-red-contrib-graphql.svg) 5 | ![NPM](https://img.shields.io/npm/dm/node-red-contrib-graphql.svg) 6 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/6cbeb40ab5604b3ab99e6badc9469e8a)](https://www.codacy.com/gh/rgstephens/node-red-contrib-graphql?utm_source=github.com&utm_medium=referral&utm_content=rgstephens/node-red-contrib-graphql&utm_campaign=Badge_Grade) 7 | 8 | A NodeRed node to execute GraphQL Queries. 9 | 10 | ## Change Log 11 | 12 | | Vers | Changes | 13 | | ----- | -------------------------------------------------------- | 14 | | 2.2.0 | Really fix payload issue | 15 | | 2.1.2 | Fix payload init issue | 16 | | 2.1.0 | Bearer Token Authentication | 17 | | 2.0.1 | Update dependencies (`axios` & `mustache`), fix node-red scorecard issues | 18 | | 2.0.0 | GraphQL response is now on `payload.graphql` instead of replacing `payload`. This is a breaking change. Addresses #32 | 19 | | 1.4.1 | Bump `follow-redirects` to 1.14.8 | 20 | | 1.4.0 | improve debug, bump `follow-redirects` | 21 | | 1.3.0 | bump axios to address CVE-2021-3749 | 22 | | 1.2.0 | [Fix node not showing in palette](https://github.com/rgstephens/node-red-contrib-graphql/pull/24), bump axios | 23 | | 1.1.0 | [Error Handling & Config Templates](https://github.com/rgstephens/node-red-contrib-graphql/pull/11/), [showDebug & customHeaders](https://github.com/rgstephens/node-red-contrib-graphql/pull/22/conflicts), [Bump axios](https://github.com/rgstephens/node-red-contrib-graphql/pull/20) | 24 | | 1.0.0 | pass Authorization via msg.authorization, [PR #21](https://github.com/rgstephens/node-red-contrib-graphql/pull/21)| 25 | | 0.0.6 | Initial Release | 26 | 27 | ## GraphQL Nodes 28 | 29 | Provides a `GraphQL` node to support queries and a configuration node called `graphql-server`. 30 | 31 | ### `graphql-server` Configuration Node Fields 32 | 33 | | Name | Use | 34 | | -------- | ------------------- | 35 | | Name | Node Name | 36 | | Endpoint | URL to the endpoint | 37 | | Token | Bearer Token | 38 | 39 | ### `graphql` Function Node Fields 40 | 41 | | Name | Use | 42 | | ---------- | -------------------------- | 43 | | Name | Node Name | 44 | | Endpoint | Configuration Node Name | 45 | | Query | Query or Mutation template | 46 | | Syntax | Mustache / plain | 47 | | Token | Bearer Token | 48 | | Show Debug | Enable debug | 49 | 50 | ## Countries API Example 51 | 52 | This example flow uses the `node-red-contrib-graphql` node to query the [Countries API](https://github.com/trevorblades/countries) built by GitHub user [Trevor Blades](https://github.com/trevorblades). 53 | 54 | The example flow is in the file `examples/countries.json`. Import this file from the clipboard under the NodeRed menu `Import > Clipboard`. You'll drag the example flow onto NodeRed. 55 | 56 | ![Example Flow](images/flow.png) 57 | 58 | ### Edit graphql node 59 | 60 | The GraphQL endpoint for is `https://countries.trevorblades.com/`. You can try it out [here](https://countries.trevorblades.com/). Here's the `graphql-node`: 61 | 62 | ![Edit GraphQL Node](images/editGraphQL.png) 63 | 64 | ### GraphQL Output 65 | 66 | This is the result sent to the debug window. 67 | 68 | ![Example Flow Output](images/flowOutput.png) 69 | 70 | ## Authentication Example 71 | 72 | A bearer token can be provided for authentication. This is an example using the GitHub GraphQL API which is documented [here](https://docs.github.com/en/graphql/guides/forming-calls-with-graphql#communicating-with-graphql). 73 | 74 | ![Github Flow](images/githubFlow.png) 75 | 76 | ### Config Node Token 77 | 78 | If you have an token with a long life, you can provide the token in the `graphql-server` configuration node. For GitHub, user your GitHub PAT. 79 | 80 | ![Github Config](images/githubGraphqlConfig.png) 81 | 82 | You can also provide the token in the `graphql` node. This is useful if a prior node performs the authentication and returns the token to be used for a limited session. 83 | 84 | ![GitHub GraphQL](images/githubGraphql.png) 85 | 86 | ## Custom Headers 87 | 88 | You can provide custom headers to the GraphQL node by attaching a `customHeaders` key to the `msg` and passing that to the GraphQL node. Here's an example that sets the `content-type` and a bearer token. 89 | 90 | ![Custom Headers](images/customHeaders.png) 91 | 92 | ## Templates and Variable Use 93 | 94 | There are two template flavors: 95 | 96 | 1. Plain 97 | 2. Mustache 98 | 99 | At the bottom of the template text area, you must select between plain or mustache template. 100 | 101 | If you select mustache, your template will be processed by Mustache with the message's payload as an argument. I.e. 102 | 103 | ``` 104 | submitted_template = mustache("template in text area", msg.payload) 105 | ``` 106 | 107 | If you select plain, the template is left as it is. 108 | 109 | ### Template variables 110 | 111 | You can add GraphQL query variables to the submitted query by defining them in the `msg.variables` property. 112 | Your variables will be passed over to the GraphQL query. 113 | 114 | For example, if you define 115 | 116 | ``` 117 | type Response { 118 | ok: boolean 119 | } 120 | 121 | input doSomethingInput { 122 | myVar: String 123 | } 124 | 125 | type Mutation { 126 | doSomething(input: doSomethingInput!): Response 127 | } 128 | 129 | ``` 130 | 131 | you can pass the `messageInput` parameter as such in Node-Red msg: 132 | 133 | ``` 134 | msg.variables = { 135 | "input": { 136 | "myVar": "myValue" 137 | } 138 | } 139 | ``` 140 | 141 | it will be added to the GraphQL query: 142 | 143 | ``` 144 | query: `mutation doSomething($input: messageInput!) { 145 | doSomething(input: $input) { 146 | ok 147 | } 148 | }`, 149 | variables: { 150 | input: { 151 | myVar: "myValue" 152 | } 153 | } 154 | ``` 155 | 156 | When using a scalar type like [JSON](https://github.com/taion/graphql-type-json), the entire payload can conveniently be 157 | passed as an input parameter: 158 | 159 | ``` 160 | scalar JSON 161 | 162 | type Response { 163 | ok: boolean 164 | } 165 | 166 | input payloadInput { 167 | payload: JSON 168 | } 169 | 170 | type Mutation { 171 | doSomething(input: payloadInput!): Response 172 | } 173 | 174 | ``` 175 | 176 | In node-red flow, prepare `payloadInput` variables: 177 | 178 | ``` 179 | msg.variables = { 180 | "input": { 181 | "payload": msg.payload 182 | } 183 | } 184 | ``` 185 | 186 | which will results in 187 | 188 | ``` 189 | query: `mutation doSomething($input: payloadInput!) { 190 | doSomething(input: $input) { 191 | ok 192 | } 193 | }`, 194 | variables: { 195 | input: { 196 | myVar: { whatever: "was in you msg.payload", val: 5, bool: true } 197 | } 198 | } 199 | ``` 200 | 201 | The execution will return the value in: 202 | 203 | ``` 204 | msg.payload.doSomething 205 | ``` 206 | 207 | object. 208 | 209 | ### Outputs 210 | 211 | `payload` is loaded with the output of the Query or Mutation. If the Query is named `doSomething`, the results of the query will be in `payload.doSomething`. 212 | 213 | ``` 214 | //msg.payload is: 215 | { 216 | doSomething: { 217 | ok: true 218 | } 219 | } 220 | ``` 221 | -------------------------------------------------------------------------------- /examples/countriesFlow.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "a12eeec2ef7081f3", 4 | "type": "graphql", 5 | "z": "e596e2e15cffe546", 6 | "name": "Get Country", 7 | "graphql": "5ec8b7409ad68ba9", 8 | "format": "json", 9 | "syntax": "mustache", 10 | "template": "{\n country(code: \"DE\") {\n name\n native\n capital\n currency\n phone\n states {\n code\n name\n }\n }\n}", 11 | "showDebug": false, 12 | "x": 290, 13 | "y": 80, 14 | "wires": [ 15 | [ 16 | "b1909e0fe42ed002", 17 | "b4a00ddbf587d5db" 18 | ], 19 | [ 20 | "b4a00ddbf587d5db" 21 | ] 22 | ] 23 | }, 24 | { 25 | "id": "8ce305107f24099c", 26 | "type": "inject", 27 | "z": "e596e2e15cffe546", 28 | "name": "", 29 | "props": [ 30 | { 31 | "p": "payload" 32 | }, 33 | { 34 | "p": "topic", 35 | "vt": "str" 36 | } 37 | ], 38 | "repeat": "", 39 | "crontab": "", 40 | "once": false, 41 | "onceDelay": 0.1, 42 | "topic": "", 43 | "payload": "", 44 | "payloadType": "date", 45 | "x": 100, 46 | "y": 80, 47 | "wires": [ 48 | [ 49 | "a12eeec2ef7081f3" 50 | ] 51 | ] 52 | }, 53 | { 54 | "id": "b4a00ddbf587d5db", 55 | "type": "debug", 56 | "z": "e596e2e15cffe546", 57 | "name": "", 58 | "active": true, 59 | "tosidebar": true, 60 | "console": false, 61 | "tostatus": false, 62 | "complete": "true", 63 | "targetType": "full", 64 | "statusVal": "", 65 | "statusType": "auto", 66 | "x": 490, 67 | "y": 100, 68 | "wires": [] 69 | }, 70 | { 71 | "id": "b1909e0fe42ed002", 72 | "type": "template", 73 | "z": "e596e2e15cffe546", 74 | "name": "Country Details", 75 | "field": "payload", 76 | "fieldType": "msg", 77 | "format": "handlebars", 78 | "syntax": "mustache", 79 | "template": "Country Name - {{payload.graphql.country.name}} / {{payload.graphql.country.native}}\nCapital: {{payload.graphql.country.capital}}\nCurrency: {{payload.graphql.country.currency}}\nStates:\n {{payload.graphql.country.states.0.name}}\n {{payload.graphql.country.states.1.name}}\n {{payload.graphql.country.states.2.name}}\n {{payload.graphql.country.states.3.name}}\n", 80 | "output": "str", 81 | "x": 520, 82 | "y": 60, 83 | "wires": [ 84 | [ 85 | "080fbc2ad4668f85" 86 | ] 87 | ] 88 | }, 89 | { 90 | "id": "080fbc2ad4668f85", 91 | "type": "debug", 92 | "z": "e596e2e15cffe546", 93 | "name": "", 94 | "active": true, 95 | "tosidebar": true, 96 | "console": false, 97 | "complete": "false", 98 | "statusVal": "", 99 | "statusType": "auto", 100 | "x": 730, 101 | "y": 60, 102 | "wires": [] 103 | }, 104 | { 105 | "id": "5ec8b7409ad68ba9", 106 | "type": "graphql-server", 107 | "name": "Countries", 108 | "endpoint": "https://countries.trevorblades.com" 109 | } 110 | ] -------------------------------------------------------------------------------- /examples/githubLogin.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "cc97ba59b7f7b1c6", 4 | "type": "inject", 5 | "z": "ecee231023f6944e", 6 | "name": "", 7 | "props": [ 8 | { 9 | "p": "payload" 10 | }, 11 | { 12 | "p": "topic", 13 | "vt": "str" 14 | } 15 | ], 16 | "repeat": "", 17 | "crontab": "", 18 | "once": false, 19 | "onceDelay": 0.1, 20 | "topic": "", 21 | "payload": "", 22 | "payloadType": "date", 23 | "x": 160, 24 | "y": 140, 25 | "wires": [ 26 | [ 27 | "5ce1d5e8c233c062" 28 | ] 29 | ] 30 | }, 31 | { 32 | "id": "5ce1d5e8c233c062", 33 | "type": "graphql", 34 | "z": "ecee231023f6944e", 35 | "name": "Get Login id", 36 | "graphql": "4a27e34566452125", 37 | "format": "handlebars", 38 | "template": "{\n viewer {\n login\n }\n}", 39 | "syntax": "mustache", 40 | "token": "", 41 | "showDebug": false, 42 | "credentials": {}, 43 | "x": 350, 44 | "y": 140, 45 | "wires": [ 46 | [ 47 | "fe44bcf58ee82812" 48 | ], 49 | [ 50 | "35a20afebf41f631" 51 | ] 52 | ] 53 | }, 54 | { 55 | "id": "fe44bcf58ee82812", 56 | "type": "debug", 57 | "z": "ecee231023f6944e", 58 | "name": "", 59 | "active": true, 60 | "tosidebar": true, 61 | "console": false, 62 | "tostatus": false, 63 | "complete": "payload.graphql.viewer.login", 64 | "targetType": "msg", 65 | "statusVal": "", 66 | "statusType": "auto", 67 | "x": 660, 68 | "y": 120, 69 | "wires": [] 70 | }, 71 | { 72 | "id": "35a20afebf41f631", 73 | "type": "debug", 74 | "z": "ecee231023f6944e", 75 | "name": "", 76 | "active": true, 77 | "tosidebar": true, 78 | "console": false, 79 | "tostatus": false, 80 | "complete": "true", 81 | "targetType": "full", 82 | "statusVal": "", 83 | "statusType": "auto", 84 | "x": 570, 85 | "y": 160, 86 | "wires": [] 87 | }, 88 | { 89 | "id": "4a27e34566452125", 90 | "type": "graphql-server", 91 | "name": "GitHub", 92 | "endpoint": "https://api.github.com/graphql", 93 | "token": "" 94 | } 95 | ] -------------------------------------------------------------------------------- /graphql.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 35 | 36 | 82 | 83 | 174 | 175 | -------------------------------------------------------------------------------- /graphql.js: -------------------------------------------------------------------------------- 1 | module.exports = function(RED) { 2 | // https://github.com/axios/axios 3 | var axios = require("axios"); 4 | var mustache = require("mustache"); 5 | 6 | var vers = "2.2.0"; 7 | 8 | function isReadable(value) { 9 | return typeof value === 'object' && typeof value._read === 'function' && typeof value._readableState === 'object' 10 | } 11 | 12 | function safeJSONStringify(input, maxDepth) { 13 | var output, 14 | refs = [], 15 | refsPaths = []; 16 | 17 | maxDepth = maxDepth || 5; 18 | 19 | function recursion(input, path, depth) { 20 | var output = {}, 21 | pPath, 22 | refIdx; 23 | 24 | path = path || ""; 25 | depth = depth || 0; 26 | depth++; 27 | 28 | if (maxDepth && depth > maxDepth) { 29 | return "{depth over " + maxDepth + "}"; 30 | } 31 | 32 | for (var p in input) { 33 | pPath = (path ? path + "." : "") + p; 34 | if (typeof input[p] === "function") { 35 | output[p] = "{function}"; 36 | } else if (input[p] && Buffer.isBuffer(input[p])) { 37 | output[p] = "[object Buffer]"; 38 | } else if (input[p] && isReadable(input[p])) { 39 | output[p] = "[object Readable]"; 40 | } else if (typeof input[p] === "object") { 41 | refIdx = refs.indexOf(input[p]); 42 | 43 | if (-1 !== refIdx) { 44 | output[p] = "{reference to " + refsPaths[refIdx] + "}"; 45 | } else { 46 | refs.push(input[p]); 47 | refsPaths.push(pPath); 48 | output[p] = recursion(input[p], pPath, depth); 49 | } 50 | } else { 51 | output[p] = input[p]; 52 | } 53 | } 54 | 55 | return output; 56 | } 57 | 58 | if (typeof input === "object") { 59 | output = recursion(input); 60 | } else { 61 | output = input; 62 | } 63 | 64 | return JSON.stringify(output); 65 | } 66 | 67 | function GraphqlNode(config) { 68 | RED.nodes.createNode(this, config); 69 | var node = this; 70 | 71 | RED.log.debug("--- GraphqlNode v" + vers + " ---"); 72 | RED.log.debug("GraphqlNode node: " + safeJSONStringify(node)); 73 | RED.log.trace("GraphqlNode config: " + safeJSONStringify(config)); 74 | node.endpoint = config.endpoint; 75 | node.token = config.token 76 | RED.log.debug("node.endpoint: " + node.endpoint); 77 | RED.log.debug("node.token: " + node.token) 78 | } 79 | 80 | RED.nodes.registerType("graphql-server", GraphqlNode, { 81 | credentials: { 82 | token: { type: "password" }, 83 | } 84 | }); 85 | 86 | function GraphqlExecNode(config) { 87 | RED.nodes.createNode(this, config); 88 | var node = this; 89 | 90 | node.graphqlConfig = RED.nodes.getNode(config.graphql); // Retrieve the config node 91 | 92 | node.template = config.template; 93 | node.name = config.name; 94 | node.varsField = config.varsField || "variables"; 95 | node.syntax = config.syntax || "mustache"; 96 | node.showDebug = config.showDebug || false 97 | node.token = node.credentials.token || ""; 98 | node.customHeaders = config.customHeaders || {} 99 | node.varsField = config.varsField || "variables"; 100 | RED.log.debug("--- GraphqlExecNode ---"); 101 | 102 | if (!node.graphqlConfig) { 103 | node.error("invalid graphql config"); 104 | } 105 | 106 | function dataobject(context, msg){ 107 | data = {} 108 | data.msg = msg; 109 | data.global = {}; 110 | data.flow = {}; 111 | g_keys = context.global.keys(); 112 | f_keys = context.flow.keys(); 113 | for (k in g_keys){ 114 | data.global[g_keys[k]] = context.global.get(g_keys[k]); 115 | }; 116 | for (k in f_keys){ 117 | data.flow[f_keys[k]] = context.flow.get(f_keys[k]); 118 | }; 119 | return data 120 | } 121 | 122 | function callGraphQLServer(query, variables = {}, customHeaders = {}) { 123 | let data = dataobject(node.context(), node.msg); 124 | let url = mustache.render(node.graphqlConfig.endpoint, data); 125 | let headers = customHeaders 126 | const token = node.token || node.graphqlConfig.token || ""; 127 | if (token) { 128 | headers["Authorization"] = `Bearer ${token}` 129 | } 130 | 131 | if (node.showDebug) { 132 | node.log(safeJSONStringify(data)); 133 | node.log(headers.Authorization); 134 | } 135 | 136 | axios({ 137 | method: "POST", 138 | url, 139 | headers, 140 | timeout: 20000, 141 | data: { 142 | query: query, 143 | variables: variables 144 | } 145 | }) 146 | .then(function(response) { 147 | if (!node.msg.payload) node.msg.payload = {}; 148 | switch (true) { 149 | case response.status == 200 && !response.data.errors: 150 | node.status({ 151 | fill: "green", 152 | shape: "dot", 153 | text: RED._("graphql.status.success") 154 | }); 155 | node.msg.payload.graphql = response.data.data; // remove .data to see entire response 156 | if (node.showDebug){ 157 | node.msg.debugInfo = { 158 | data: response.data, 159 | headers, 160 | query, 161 | variables 162 | } 163 | } 164 | node.send(node.msg); 165 | break; 166 | case response.status == 200 && !!response.data.errors: 167 | node.status({ 168 | fill: "yellow", 169 | shape: "dot", 170 | text: RED._("graphql.status.gqlError") 171 | }); 172 | node.msg.payload.graphql = response.data.errors; 173 | node.send([null, node.msg]); 174 | break; 175 | default: 176 | node.status({ 177 | fill: "red", 178 | shape: "dot", 179 | text: "status: " + response.status 180 | }); 181 | node.msg.payload.graphql = { 182 | statusCode: response.status, 183 | body: response.data 184 | }; 185 | node.send([null, node.msg]); 186 | break; 187 | } 188 | }) 189 | .catch(function(error) { 190 | RED.log.debug("error:" + error); 191 | node.status({ fill: "red", shape: "dot", text: "error" }); 192 | if (!node.msg.payload) node.msg.payload = {}; 193 | node.msg.payload.graphql = { error }; 194 | node.error("error: " + error); 195 | node.send([null, node.msg]); 196 | }); 197 | } 198 | 199 | node.on("input", function(msg) { 200 | RED.log.debug("--- on(input) ---"); 201 | RED.log.debug("msg: " + safeJSONStringify(msg)); 202 | node.msg = msg; 203 | node.template = msg.template || node.template; 204 | node.syntax = msg.syntax || node.syntax; 205 | node.customHeaders = {...node.customHeaders, ...msg.customHeaders} 206 | var query; 207 | if (node.syntax === "mustache") { 208 | query = mustache.render(node.template, msg); 209 | } else { 210 | query = node.template; 211 | } 212 | var variables = msg[node.varsField] || {} 213 | 214 | callGraphQLServer(query, variables, node.customHeaders); 215 | }); 216 | 217 | node.on("close", function() { 218 | RED.log.debug("--- closing node ---"); 219 | node.graphqlConfig.credentials.token = node.token || ""; 220 | RED.nodes.addCredentials( 221 | node.graphqlConfig, 222 | node.graphqlConfig.credentials 223 | ); 224 | }); 225 | } 226 | 227 | RED.nodes.registerType("graphql", GraphqlExecNode, { 228 | credentials: { 229 | token: { type: "password" }, 230 | } 231 | }); 232 | }; 233 | -------------------------------------------------------------------------------- /graphql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgstephens/node-red-contrib-graphql/8c20aa77eccbdd08b89ece4e79bc8b081a87704b/graphql.png -------------------------------------------------------------------------------- /images/customHeaders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgstephens/node-red-contrib-graphql/8c20aa77eccbdd08b89ece4e79bc8b081a87704b/images/customHeaders.png -------------------------------------------------------------------------------- /images/editGraphQL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgstephens/node-red-contrib-graphql/8c20aa77eccbdd08b89ece4e79bc8b081a87704b/images/editGraphQL.png -------------------------------------------------------------------------------- /images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgstephens/node-red-contrib-graphql/8c20aa77eccbdd08b89ece4e79bc8b081a87704b/images/flow.png -------------------------------------------------------------------------------- /images/flowOutput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgstephens/node-red-contrib-graphql/8c20aa77eccbdd08b89ece4e79bc8b081a87704b/images/flowOutput.png -------------------------------------------------------------------------------- /images/githubFlow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgstephens/node-red-contrib-graphql/8c20aa77eccbdd08b89ece4e79bc8b081a87704b/images/githubFlow.png -------------------------------------------------------------------------------- /images/githubGraphql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgstephens/node-red-contrib-graphql/8c20aa77eccbdd08b89ece4e79bc8b081a87704b/images/githubGraphql.png -------------------------------------------------------------------------------- /images/githubGraphqlConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgstephens/node-red-contrib-graphql/8c20aa77eccbdd08b89ece4e79bc8b081a87704b/images/githubGraphqlConfig.png -------------------------------------------------------------------------------- /locales/en-US/graphql.json: -------------------------------------------------------------------------------- 1 | { 2 | "graphql": { 3 | "label": { 4 | "endpoint": "Endpoint", 5 | "syntax": "Syntax", 6 | "tokenplaceholder": "Token if required by API", 7 | "query": "Query", 8 | "mustache": "mustache", 9 | "plain": "plain", 10 | "apivers": "API Vers", 11 | "token": "Bearer Token", 12 | "showDebug": "Show Debug" 13 | }, 14 | "status": { 15 | "calling": "calling", 16 | "success": "success", 17 | "gqlError": "graphql error", 18 | "failedConn": "Failed connection", 19 | "failed": "failed", 20 | "connecting": "connecting", 21 | "connected": "connected", 22 | "notLoggedIn": "Not logged in" 23 | }, 24 | "errors": { 25 | "tokenExp": "Token has expired or is not good, re-logging in", 26 | "tokenExpLogin": "Token has expired or is not good, re-logging in", 27 | "badRest": "Bad REST endpoint, not a valid call", 28 | "error401": "401 error, calling close", 29 | "badCreds": "Bad credentials", 30 | "error404": "Bad REST endpoint, URL, resource not found", 31 | "error400": "resource not found", 32 | "alreadyLoggedOut": "already logged out?", 33 | "server": "Server not found", 34 | "timeout": "Timeout connecting to server, check port", 35 | "nouserid": "No e-mail userid set", 36 | "nopassword": "No e-mail password set", 37 | "nocredentials": "No Email credentials found. See info panel.", 38 | "nopayload": "No payload to send", 39 | "fetchfail": "Failed to fetch folder: __folder__", 40 | "messageerror": "Fetch message error: __error__" 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-contrib-graphql", 3 | "version": "2.2.0", 4 | "description": "A Node-RED node to make GraphQL calls", 5 | "dependencies": { 6 | "axios": "~1.2.1", 7 | "follow-redirects": "~1.15.2", 8 | "mustache": "~4.2.0" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/rgstephens/node-red-contrib-graphql" 13 | }, 14 | "license": "Apache-2.0", 15 | "keywords": [ 16 | "node-red", 17 | "graphql" 18 | ], 19 | "scripts": { 20 | "debug": "node /Users/greg/.nvm/versions/node/v14.16.1/lib/node_modules/node-red/red.js" 21 | }, 22 | "node-red": { 23 | "version": ">=2", 24 | "nodes": { 25 | "graphql": "graphql.js" 26 | } 27 | }, 28 | "author": { 29 | "name": "Greg Stephens", 30 | "email": "greg@udon.org", 31 | "url": "http://gstephens.org" 32 | }, 33 | "engines": { 34 | "node": ">=12" 35 | } 36 | } 37 | --------------------------------------------------------------------------------