|
1 | | -// import the library.. aaaaaaaaah |
2 | | -import { defaultCipherList } from "constants"; |
3 | | -import createApp from "../src/index"; |
| 1 | +/* |
| 2 | + * Let's imagine that there is a service that lets you build services that has graphql api for interaction. |
| 3 | + * But sadly the service doesn't offer you any way to implement anykind of RBAC (Role Based Access Control) on top of |
| 4 | + * it. You don't want every user (using your app?) having unrestricted access to the entire service, do you? |
| 5 | + * . Apparently then you have to put some kind of middleware that checks if the app user |
| 6 | + * has proper permissions and if so then proxy the request to the service. |
| 7 | + * |
| 8 | + * express-graphql-proxy library lets you do that by parsing the user queries and mutations into a javascript object |
| 9 | + * which can be used to validate the request. |
| 10 | + */ |
4 | 11 |
|
5 | | -// handle fetching pokemons |
6 | | -function pokemons() { |
7 | | - return true; |
8 | | -} |
| 12 | +// import the default function which can be used to create an express app |
| 13 | +import createApp from "@antstackio/express-graphql-proxy"; |
9 | 14 |
|
10 | | -// all the handlers |
| 15 | +// define handlers for each query/mutation type |
11 | 16 | const handlers = { |
12 | | - mutation: {}, |
13 | 17 | query: { |
14 | | - pokemons, |
| 18 | + getTodo, |
| 19 | + getTodos, |
| 20 | + }, |
| 21 | + mutation: { |
| 22 | + addTodo, |
15 | 23 | }, |
16 | 24 | }; |
17 | 25 |
|
| 26 | +// handler "getTodo" query type |
| 27 | +// say user is making some query like --> becomes |
| 28 | +// ---------------------------------- ---------- |
| 29 | +// query geTodo { { |
| 30 | +// getTodo(id: 111, userId: 435) { args: { id: 111, userId: 435 }, |
| 31 | +// id selectedFields: { |
| 32 | +// task id: 1, |
| 33 | +// status task: 1, |
| 34 | +// } status: 1 |
| 35 | +// } } |
| 36 | +// } |
| 37 | +function getTodo(args, selectedFields, context) { |
| 38 | + //arguments will have the arguments passed to the query |
| 39 | + |
| 40 | + // only a user should be able to retreieve their todo |
| 41 | + if (!args.userId == context.user._id) { |
| 42 | + return false; |
| 43 | + } |
| 44 | + |
| 45 | + return true; |
| 46 | +} |
| 47 | + |
| 48 | +// handler "getTodo" query type |
| 49 | +function getTodos(args, selectedFields, { req, res, user }) { |
| 50 | + //arguments will have the arguments passed to the query |
| 51 | + |
| 52 | + // only a user should be able to retreieve their todo |
| 53 | + // context also has req and res objects that are avaialable in express handlers. |
| 54 | + // they can be used to override the default behavior of the library |
| 55 | + if (!args.userId == user._id) { |
| 56 | + // sending a custom response |
| 57 | + return res.status(401).send("Unauthorized"); |
| 58 | + } |
| 59 | + |
| 60 | + return true; |
| 61 | +} |
| 62 | + |
| 63 | +function addTodo(args, selectedFields, context) { |
| 64 | + if (args.userId == context.user._id) { |
| 65 | + return false; |
| 66 | + } |
| 67 | + return true; |
| 68 | +} |
| 69 | + |
18 | 70 | function handlerFunc(gqlObject, context) { |
19 | | - //user deets received in header |
| 71 | + // imaginary user details that being send as header.. |
| 72 | + // usually these are token, or auth cookies that needs to be parsed and validated |
20 | 73 | const userId = context.req.headers.user; |
21 | 74 | const admin = !!context.req.headers.admin; |
22 | 75 |
|
| 76 | + // each req can have multiple queries or mutations inside hence |
| 77 | + // gqlObject will have queryObjects instead of single one. |
| 78 | + // Will have to iterate over each of them and decide if user has required permissions |
23 | 79 | const result = gqlObject.queryObjects.every((item) => |
24 | 80 | // map the gqlObject to a suitable handler |
25 | 81 | handlers[gqlObject.type][item.operationName]( |
26 | 82 | item.variables, |
27 | 83 | item.selectedFields, |
| 84 | + // inject the parsed user deeets so its available in the handlers |
28 | 85 | { ...context, user: { _id: userId, admin } } |
29 | 86 | ) |
30 | 87 | ); |
31 | 88 |
|
| 89 | + // if true then req is proxied, else responded with 401 Unauthorized |
32 | 90 | return result; |
33 | 91 | } |
34 | 92 |
|
| 93 | +// create an express app |
35 | 94 | const app = createApp( |
36 | | - { resourceUri: "https://graphql-pokemon2.vercel.app", headers: {} }, |
| 95 | + { |
| 96 | + resourceUri: "https://my-app.imaginary-service.com/graphql", |
| 97 | + headers: { |
| 98 | + api_key: process.env.API_KEY, |
| 99 | + }, |
| 100 | + }, |
37 | 101 | handlerFunc |
38 | 102 | ); |
39 | 103 |
|
| 104 | +// listen on port 3000 |
40 | 105 | app.listen(3000, () => { |
41 | 106 | console.log("app listening on port 3000"); |
42 | 107 | }); |
0 commit comments