├── .gitignore ├── README.md ├── index.js ├── package-lock.json ├── package.json ├── src ├── data │ └── index.js └── node.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building a GraphQL Server 2 | 3 | ## Setup 4 | 5 | - Clone or fork this repository 6 | - Make sure you have [node.js](https://nodejs.org/) installed 7 | - run `npm install` to install dependencies 8 | - run `npm start` to fire up dev server 9 | - open browser to [`http://localhost:3000`](http://localhost:3000) 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const graphqlHTTP = require('express-graphql'); 5 | const { 6 | GraphQLSchema, 7 | GraphQLObjectType, 8 | GraphQLInputObjectType, 9 | GraphQLList, 10 | GraphQLNonNull, 11 | GraphQLID, 12 | GraphQLString, GraphQLInt, 13 | GraphQLBoolean, 14 | } = require('graphql'); 15 | const { getVideoById, getVideos, createVideo } = require('./src/data'); 16 | const { 17 | globalIdField, 18 | connectionDefinitions, 19 | connectionFromPromisedArray, 20 | connectionArgs, 21 | mutationWithClientMutationId, 22 | } = require('graphql-relay'); 23 | const { nodeInterface, nodeField } = require('./src/node'); 24 | 25 | const PORT = process.env.PORT || 3000; 26 | const server = express(); 27 | 28 | const videoType = new GraphQLObjectType({ 29 | name: 'Video', 30 | description: 'A video on Egghead.io', 31 | fields: { 32 | id: globalIdField(), 33 | title: { 34 | type: GraphQLString, 35 | description: 'The title of the video.', 36 | }, 37 | duration: { 38 | type: GraphQLInt, 39 | description: 'The duration of the video (in seconds).', 40 | }, 41 | released: { 42 | type: GraphQLBoolean, 43 | description: 'Whether or not the video has been released.', 44 | }, 45 | }, 46 | interfaces: [nodeInterface], 47 | }); 48 | exports.videoType = videoType; 49 | 50 | const { connectionType: VideoConnection } = connectionDefinitions({ 51 | nodeType: videoType, 52 | connectionFields: () => ({ 53 | totalCount: { 54 | type: GraphQLInt, 55 | description: 'A count of the total number of objects in this connection.', 56 | resolve: (conn) => { 57 | return conn.edges.length; 58 | }, 59 | }, 60 | }), 61 | }); 62 | 63 | const queryType = new GraphQLObjectType({ 64 | name: 'QueryType', 65 | description: 'The root query type.', 66 | fields: { 67 | node: nodeField, 68 | videos: { 69 | type: VideoConnection, 70 | args: connectionArgs, 71 | resolve: (_, args) => connectionFromPromisedArray( 72 | getVideos(), 73 | args 74 | ), 75 | }, 76 | video: { 77 | type: videoType, 78 | args: { 79 | id: { 80 | type: new GraphQLNonNull(GraphQLID), 81 | description: 'The id of the video.', 82 | }, 83 | }, 84 | resolve: (_, args) => { 85 | return getVideoById(args.id); 86 | }, 87 | }, 88 | }, 89 | }); 90 | 91 | const videoMutation = mutationWithClientMutationId({ 92 | name: 'AddVideo', 93 | inputFields: { 94 | title: { 95 | type: new GraphQLNonNull(GraphQLString), 96 | description: 'The title of the video.', 97 | }, 98 | duration: { 99 | type: new GraphQLNonNull(GraphQLInt), 100 | description: 'The duration of the video (in seconds).', 101 | }, 102 | released: { 103 | type: new GraphQLNonNull(GraphQLBoolean), 104 | description: 'Whether or not the video is released.', 105 | }, 106 | }, 107 | outputFields: { 108 | video: { 109 | type: videoType, 110 | }, 111 | }, 112 | mutateAndGetPayload: (args) => new Promise((resolve, reject) => { 113 | Promise.resolve(createVideo(args)) 114 | .then((video) => resolve({ video })) 115 | .catch(reject); 116 | }), 117 | }); 118 | 119 | const mutationType = new GraphQLObjectType({ 120 | name: 'Mutation', 121 | description: 'The root Mutation type.', 122 | fields: { 123 | createVideo: videoMutation, 124 | }, 125 | }); 126 | 127 | const schema = new GraphQLSchema({ 128 | query: queryType, 129 | mutation: mutationType, 130 | }); 131 | 132 | server.use('/graphql', graphqlHTTP({ 133 | schema, 134 | graphiql: true, 135 | })); 136 | 137 | server.listen(PORT, () => { 138 | console.log(`Listening on http://localhost:${PORT}`); 139 | }); 140 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.18.2", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 24 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 25 | "requires": { 26 | "bytes": "3.0.0", 27 | "content-type": "1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "1.1.2", 30 | "http-errors": "1.6.3", 31 | "iconv-lite": "0.4.19", 32 | "on-finished": "2.3.0", 33 | "qs": "6.5.1", 34 | "raw-body": "2.3.2", 35 | "type-is": "1.6.16" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.0.0", 40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 41 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.2", 45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 46 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 47 | }, 48 | "content-type": { 49 | "version": "1.0.4", 50 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 51 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 52 | }, 53 | "cookie": { 54 | "version": "0.3.1", 55 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 56 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 57 | }, 58 | "cookie-signature": { 59 | "version": "1.0.6", 60 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 61 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 62 | }, 63 | "debug": { 64 | "version": "2.6.9", 65 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 66 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 67 | "requires": { 68 | "ms": "2.0.0" 69 | } 70 | }, 71 | "depd": { 72 | "version": "1.1.2", 73 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 74 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 75 | }, 76 | "destroy": { 77 | "version": "1.0.4", 78 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 79 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 80 | }, 81 | "ee-first": { 82 | "version": "1.1.1", 83 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 84 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 85 | }, 86 | "encodeurl": { 87 | "version": "1.0.2", 88 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 89 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 90 | }, 91 | "escape-html": { 92 | "version": "1.0.3", 93 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 94 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 95 | }, 96 | "etag": { 97 | "version": "1.8.1", 98 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 99 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 100 | }, 101 | "express": { 102 | "version": "4.16.3", 103 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", 104 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", 105 | "requires": { 106 | "accepts": "1.3.5", 107 | "array-flatten": "1.1.1", 108 | "body-parser": "1.18.2", 109 | "content-disposition": "0.5.2", 110 | "content-type": "1.0.4", 111 | "cookie": "0.3.1", 112 | "cookie-signature": "1.0.6", 113 | "debug": "2.6.9", 114 | "depd": "1.1.2", 115 | "encodeurl": "1.0.2", 116 | "escape-html": "1.0.3", 117 | "etag": "1.8.1", 118 | "finalhandler": "1.1.1", 119 | "fresh": "0.5.2", 120 | "merge-descriptors": "1.0.1", 121 | "methods": "1.1.2", 122 | "on-finished": "2.3.0", 123 | "parseurl": "1.3.2", 124 | "path-to-regexp": "0.1.7", 125 | "proxy-addr": "2.0.3", 126 | "qs": "6.5.1", 127 | "range-parser": "1.2.0", 128 | "safe-buffer": "5.1.1", 129 | "send": "0.16.2", 130 | "serve-static": "1.13.2", 131 | "setprototypeof": "1.1.0", 132 | "statuses": "1.4.0", 133 | "type-is": "1.6.16", 134 | "utils-merge": "1.0.1", 135 | "vary": "1.1.2" 136 | } 137 | }, 138 | "express-graphql": { 139 | "version": "0.6.12", 140 | "resolved": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.6.12.tgz", 141 | "integrity": "sha512-ouLWV0hRw4hnaLtXzzwhdC79ewxKbY2PRvm05mPc/zOH5W5WVCHDQ1SmNxEPBQdUeeSNh29aIqW9zEQkA3kMuA==", 142 | "requires": { 143 | "accepts": "1.3.5", 144 | "content-type": "1.0.4", 145 | "http-errors": "1.6.3", 146 | "raw-body": "2.3.2" 147 | } 148 | }, 149 | "finalhandler": { 150 | "version": "1.1.1", 151 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 152 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 153 | "requires": { 154 | "debug": "2.6.9", 155 | "encodeurl": "1.0.2", 156 | "escape-html": "1.0.3", 157 | "on-finished": "2.3.0", 158 | "parseurl": "1.3.2", 159 | "statuses": "1.4.0", 160 | "unpipe": "1.0.0" 161 | } 162 | }, 163 | "forwarded": { 164 | "version": "0.1.2", 165 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 166 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 167 | }, 168 | "fresh": { 169 | "version": "0.5.2", 170 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 171 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 172 | }, 173 | "graphql": { 174 | "version": "0.10.5", 175 | "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.10.5.tgz", 176 | "integrity": "sha512-Q7cx22DiLhwHsEfUnUip1Ww/Vfx7FS0w6+iHItNuN61+XpegHSa3k5U0+6M5BcpavQImBwFiy0z3uYwY7cXMLQ==", 177 | "requires": { 178 | "iterall": "1.2.2" 179 | } 180 | }, 181 | "graphql-relay": { 182 | "version": "0.5.5", 183 | "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.5.5.tgz", 184 | "integrity": "sha1-1oFebt1hjoeNXZIcE/xmAz7IZ+I=" 185 | }, 186 | "http-errors": { 187 | "version": "1.6.3", 188 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 189 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 190 | "requires": { 191 | "depd": "1.1.2", 192 | "inherits": "2.0.3", 193 | "setprototypeof": "1.1.0", 194 | "statuses": "1.4.0" 195 | } 196 | }, 197 | "iconv-lite": { 198 | "version": "0.4.19", 199 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 200 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 201 | }, 202 | "inherits": { 203 | "version": "2.0.3", 204 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 205 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 206 | }, 207 | "ipaddr.js": { 208 | "version": "1.6.0", 209 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", 210 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" 211 | }, 212 | "iterall": { 213 | "version": "1.2.2", 214 | "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", 215 | "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" 216 | }, 217 | "media-typer": { 218 | "version": "0.3.0", 219 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 220 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 221 | }, 222 | "merge-descriptors": { 223 | "version": "1.0.1", 224 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 225 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 226 | }, 227 | "methods": { 228 | "version": "1.1.2", 229 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 230 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 231 | }, 232 | "mime": { 233 | "version": "1.4.1", 234 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 235 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 236 | }, 237 | "mime-db": { 238 | "version": "1.33.0", 239 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 240 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" 241 | }, 242 | "mime-types": { 243 | "version": "2.1.18", 244 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 245 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 246 | "requires": { 247 | "mime-db": "1.33.0" 248 | } 249 | }, 250 | "ms": { 251 | "version": "2.0.0", 252 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 253 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 254 | }, 255 | "negotiator": { 256 | "version": "0.6.1", 257 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 258 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 259 | }, 260 | "on-finished": { 261 | "version": "2.3.0", 262 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 263 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 264 | "requires": { 265 | "ee-first": "1.1.1" 266 | } 267 | }, 268 | "parseurl": { 269 | "version": "1.3.2", 270 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 271 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 272 | }, 273 | "path-to-regexp": { 274 | "version": "0.1.7", 275 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 276 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 277 | }, 278 | "proxy-addr": { 279 | "version": "2.0.3", 280 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", 281 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", 282 | "requires": { 283 | "forwarded": "0.1.2", 284 | "ipaddr.js": "1.6.0" 285 | } 286 | }, 287 | "qs": { 288 | "version": "6.5.1", 289 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 290 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 291 | }, 292 | "range-parser": { 293 | "version": "1.2.0", 294 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 295 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 296 | }, 297 | "raw-body": { 298 | "version": "2.3.2", 299 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 300 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 301 | "requires": { 302 | "bytes": "3.0.0", 303 | "http-errors": "1.6.2", 304 | "iconv-lite": "0.4.19", 305 | "unpipe": "1.0.0" 306 | }, 307 | "dependencies": { 308 | "depd": { 309 | "version": "1.1.1", 310 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 311 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 312 | }, 313 | "http-errors": { 314 | "version": "1.6.2", 315 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 316 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 317 | "requires": { 318 | "depd": "1.1.1", 319 | "inherits": "2.0.3", 320 | "setprototypeof": "1.0.3", 321 | "statuses": "1.4.0" 322 | } 323 | }, 324 | "setprototypeof": { 325 | "version": "1.0.3", 326 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 327 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 328 | } 329 | } 330 | }, 331 | "safe-buffer": { 332 | "version": "5.1.1", 333 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 334 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 335 | }, 336 | "send": { 337 | "version": "0.16.2", 338 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 339 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 340 | "requires": { 341 | "debug": "2.6.9", 342 | "depd": "1.1.2", 343 | "destroy": "1.0.4", 344 | "encodeurl": "1.0.2", 345 | "escape-html": "1.0.3", 346 | "etag": "1.8.1", 347 | "fresh": "0.5.2", 348 | "http-errors": "1.6.3", 349 | "mime": "1.4.1", 350 | "ms": "2.0.0", 351 | "on-finished": "2.3.0", 352 | "range-parser": "1.2.0", 353 | "statuses": "1.4.0" 354 | } 355 | }, 356 | "serve-static": { 357 | "version": "1.13.2", 358 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 359 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 360 | "requires": { 361 | "encodeurl": "1.0.2", 362 | "escape-html": "1.0.3", 363 | "parseurl": "1.3.2", 364 | "send": "0.16.2" 365 | } 366 | }, 367 | "setprototypeof": { 368 | "version": "1.1.0", 369 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 370 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 371 | }, 372 | "statuses": { 373 | "version": "1.4.0", 374 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 375 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 376 | }, 377 | "type-is": { 378 | "version": "1.6.16", 379 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 380 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 381 | "requires": { 382 | "media-typer": "0.3.0", 383 | "mime-types": "2.1.18" 384 | } 385 | }, 386 | "unpipe": { 387 | "version": "1.0.0", 388 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 389 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 390 | }, 391 | "utils-merge": { 392 | "version": "1.0.1", 393 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 394 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 395 | }, 396 | "vary": { 397 | "version": "1.1.2", 398 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 399 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 400 | } 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "dependencies": { 10 | "express": "^4.14.0", 11 | "express-graphql": "^0.6.1", 12 | "graphql": "^0.10.2", 13 | "graphql-relay": "^0.5.5" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/data/index.js: -------------------------------------------------------------------------------- 1 | const videoA = { 2 | id: 'a', 3 | title: 'Create a GraphQL Schema', 4 | duration: 120, 5 | released: true, 6 | }; 7 | const videoB = { 8 | id: 'b', 9 | title: 'Ember.js CLI', 10 | duration: 240, 11 | released: false, 12 | }; 13 | const videos = [videoA, videoB]; 14 | const getVideoById = (id) => new Promise((resolve) => { 15 | const [video] = videos.filter((video) => { 16 | return video.id === id; 17 | }); 18 | 19 | resolve(video); 20 | }); 21 | const getVideos = () => new Promise((resolve) => resolve(videos)); 22 | const createVideo = ({ title, duration, released }) => { 23 | const video = { 24 | id: (new Buffer(title, 'utf8')).toString('base64'), 25 | title, 26 | duration, 27 | released, 28 | }; 29 | 30 | videos.push(video); 31 | 32 | return video; 33 | }; 34 | const getObjectById = (type, id) => { 35 | const types = { 36 | video: getVideoById, 37 | }; 38 | 39 | return types[type](id); 40 | }; 41 | 42 | exports.getVideoById = getVideoById; 43 | exports.getVideos = getVideos; 44 | exports.createVideo = createVideo; 45 | exports.getObjectById = getObjectById; 46 | -------------------------------------------------------------------------------- /src/node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { 4 | nodeDefinitions, 5 | fromGlobalId, 6 | } = require('graphql-relay'); 7 | const { getObjectById } = require('./data'); 8 | 9 | const { nodeInterface, nodeField } = nodeDefinitions( 10 | (globalId) => { 11 | const { type, id } = fromGlobalId(globalId); 12 | 13 | return getObjectById(type.toLowerCase(), id); 14 | }, 15 | (object) => { 16 | const { videoType } = require('../'); 17 | 18 | if (object.title) { 19 | return videoType; 20 | } 21 | 22 | return null; 23 | } 24 | ); 25 | 26 | exports.nodeInterface = nodeInterface; 27 | exports.nodeField = nodeField; 28 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | accepts@^1.3.0, accepts@~1.3.3: 4 | version "1.3.3" 5 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" 6 | dependencies: 7 | mime-types "~2.1.11" 8 | negotiator "0.6.1" 9 | 10 | array-flatten@1.1.1: 11 | version "1.1.1" 12 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 13 | 14 | bytes@2.4.0: 15 | version "2.4.0" 16 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" 17 | 18 | content-disposition@0.5.1: 19 | version "0.5.1" 20 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" 21 | 22 | content-type@^1.0.0, content-type@~1.0.2: 23 | version "1.0.2" 24 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" 25 | 26 | cookie-signature@1.0.6: 27 | version "1.0.6" 28 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 29 | 30 | cookie@0.3.1: 31 | version "0.3.1" 32 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 33 | 34 | debug@~2.2.0: 35 | version "2.2.0" 36 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" 37 | dependencies: 38 | ms "0.7.1" 39 | 40 | depd@~1.1.0: 41 | version "1.1.0" 42 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" 43 | 44 | destroy@~1.0.4: 45 | version "1.0.4" 46 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 47 | 48 | ee-first@1.1.1: 49 | version "1.1.1" 50 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 51 | 52 | encodeurl@~1.0.1: 53 | version "1.0.1" 54 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" 55 | 56 | escape-html@~1.0.3: 57 | version "1.0.3" 58 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 59 | 60 | etag@~1.7.0: 61 | version "1.7.0" 62 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" 63 | 64 | express: 65 | version "4.14.0" 66 | resolved "https://registry.yarnpkg.com/express/-/express-4.14.0.tgz#c1ee3f42cdc891fb3dc650a8922d51ec847d0d66" 67 | dependencies: 68 | accepts "~1.3.3" 69 | array-flatten "1.1.1" 70 | content-disposition "0.5.1" 71 | content-type "~1.0.2" 72 | cookie "0.3.1" 73 | cookie-signature "1.0.6" 74 | debug "~2.2.0" 75 | depd "~1.1.0" 76 | encodeurl "~1.0.1" 77 | escape-html "~1.0.3" 78 | etag "~1.7.0" 79 | finalhandler "0.5.0" 80 | fresh "0.3.0" 81 | merge-descriptors "1.0.1" 82 | methods "~1.1.2" 83 | on-finished "~2.3.0" 84 | parseurl "~1.3.1" 85 | path-to-regexp "0.1.7" 86 | proxy-addr "~1.1.2" 87 | qs "6.2.0" 88 | range-parser "~1.2.0" 89 | send "0.14.1" 90 | serve-static "~1.11.1" 91 | type-is "~1.6.13" 92 | utils-merge "1.0.0" 93 | vary "~1.1.0" 94 | 95 | express-graphql: 96 | version "0.6.1" 97 | resolved "https://registry.yarnpkg.com/express-graphql/-/express-graphql-0.6.1.tgz#cd9d144ac4d191b34a4261ac3a56fa407d5e19e8" 98 | dependencies: 99 | accepts "^1.3.0" 100 | content-type "^1.0.0" 101 | http-errors "^1.3.0" 102 | raw-body "^2.1.0" 103 | 104 | finalhandler@0.5.0: 105 | version "0.5.0" 106 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" 107 | dependencies: 108 | debug "~2.2.0" 109 | escape-html "~1.0.3" 110 | on-finished "~2.3.0" 111 | statuses "~1.3.0" 112 | unpipe "~1.0.0" 113 | 114 | forwarded@~0.1.0: 115 | version "0.1.0" 116 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" 117 | 118 | fresh@0.3.0: 119 | version "0.3.0" 120 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" 121 | 122 | graphql: 123 | version "0.7.2" 124 | resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.7.2.tgz#cc894a32823399b8a0cb012b9e9ecad35cd00f72" 125 | dependencies: 126 | iterall "1.0.2" 127 | 128 | graphql-relay: 129 | version "0.4.4" 130 | resolved "https://registry.yarnpkg.com/graphql-relay/-/graphql-relay-0.4.4.tgz#876a654445b6af4539f81cb9befd5cd7ead129dd" 131 | 132 | http-errors@^1.3.0, http-errors@~1.5.0: 133 | version "1.5.0" 134 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.0.tgz#b1cb3d8260fd8e2386cad3189045943372d48211" 135 | dependencies: 136 | inherits "2.0.1" 137 | setprototypeof "1.0.1" 138 | statuses ">= 1.3.0 < 2" 139 | 140 | iconv-lite@0.4.13: 141 | version "0.4.13" 142 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" 143 | 144 | inherits@2.0.1: 145 | version "2.0.1" 146 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" 147 | 148 | ipaddr.js@1.1.1: 149 | version "1.1.1" 150 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230" 151 | 152 | iterall@1.0.2: 153 | version "1.0.2" 154 | resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.0.2.tgz#41a2e96ce9eda5e61c767ee5dc312373bb046e91" 155 | 156 | media-typer@0.3.0: 157 | version "0.3.0" 158 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 159 | 160 | merge-descriptors@1.0.1: 161 | version "1.0.1" 162 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 163 | 164 | methods@~1.1.2: 165 | version "1.1.2" 166 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 167 | 168 | mime-db@~1.24.0: 169 | version "1.24.0" 170 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.24.0.tgz#e2d13f939f0016c6e4e9ad25a8652f126c467f0c" 171 | 172 | mime-types@~2.1.11: 173 | version "2.1.12" 174 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.12.tgz#152ba256777020dd4663f54c2e7bc26381e71729" 175 | dependencies: 176 | mime-db "~1.24.0" 177 | 178 | mime@1.3.4: 179 | version "1.3.4" 180 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" 181 | 182 | ms@0.7.1: 183 | version "0.7.1" 184 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" 185 | 186 | negotiator@0.6.1: 187 | version "0.6.1" 188 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" 189 | 190 | on-finished@~2.3.0: 191 | version "2.3.0" 192 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 193 | dependencies: 194 | ee-first "1.1.1" 195 | 196 | parseurl@~1.3.1: 197 | version "1.3.1" 198 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" 199 | 200 | path-to-regexp@0.1.7: 201 | version "0.1.7" 202 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 203 | 204 | proxy-addr@~1.1.2: 205 | version "1.1.2" 206 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.2.tgz#b4cc5f22610d9535824c123aef9d3cf73c40ba37" 207 | dependencies: 208 | forwarded "~0.1.0" 209 | ipaddr.js "1.1.1" 210 | 211 | qs@6.2.0: 212 | version "6.2.0" 213 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" 214 | 215 | range-parser@~1.2.0: 216 | version "1.2.0" 217 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" 218 | 219 | raw-body@^2.1.0: 220 | version "2.1.7" 221 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" 222 | dependencies: 223 | bytes "2.4.0" 224 | iconv-lite "0.4.13" 225 | unpipe "1.0.0" 226 | 227 | send@0.14.1: 228 | version "0.14.1" 229 | resolved "https://registry.yarnpkg.com/send/-/send-0.14.1.tgz#a954984325392f51532a7760760e459598c89f7a" 230 | dependencies: 231 | debug "~2.2.0" 232 | depd "~1.1.0" 233 | destroy "~1.0.4" 234 | encodeurl "~1.0.1" 235 | escape-html "~1.0.3" 236 | etag "~1.7.0" 237 | fresh "0.3.0" 238 | http-errors "~1.5.0" 239 | mime "1.3.4" 240 | ms "0.7.1" 241 | on-finished "~2.3.0" 242 | range-parser "~1.2.0" 243 | statuses "~1.3.0" 244 | 245 | serve-static@~1.11.1: 246 | version "1.11.1" 247 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.1.tgz#d6cce7693505f733c759de57befc1af76c0f0805" 248 | dependencies: 249 | encodeurl "~1.0.1" 250 | escape-html "~1.0.3" 251 | parseurl "~1.3.1" 252 | send "0.14.1" 253 | 254 | setprototypeof@1.0.1: 255 | version "1.0.1" 256 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.1.tgz#52009b27888c4dc48f591949c0a8275834c1ca7e" 257 | 258 | "statuses@>= 1.3.0 < 2", statuses@~1.3.0: 259 | version "1.3.1" 260 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" 261 | 262 | type-is@~1.6.13: 263 | version "1.6.13" 264 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.13.tgz#6e83ba7bc30cd33a7bb0b7fb00737a2085bf9d08" 265 | dependencies: 266 | media-typer "0.3.0" 267 | mime-types "~2.1.11" 268 | 269 | unpipe@~1.0.0, unpipe@1.0.0: 270 | version "1.0.0" 271 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 272 | 273 | utils-merge@1.0.0: 274 | version "1.0.0" 275 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" 276 | 277 | vary@~1.1.0: 278 | version "1.1.0" 279 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" 280 | 281 | --------------------------------------------------------------------------------