├── .eslintrc.js ├── .gitignore ├── README.md ├── graphql.config.json ├── graphql.schema.json ├── package.json ├── scripts └── updateGraphQLSchema.js └── src ├── components ├── App.js ├── Details.js └── List.js ├── routes.js └── server.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es6: true 4 | }, 5 | parserOptions: { 6 | sourceType: 'module', 7 | ecmaFeatures: { 8 | jsx: true 9 | } 10 | }, 11 | plugins: ['graphql'], 12 | rules: { 13 | 'graphql/template-strings': [ 14 | 'error', 15 | { 16 | env: 'apollo', 17 | schemaJson: require('./graphql.schema.json') 18 | } 19 | ] 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | 3 | ### Node template 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-apollo-ssr-example 2 | Example app with server-side rendering using react, react-router and react-apollo 3 | 4 | In order to run the app: 5 | ``` 6 | npm run build && npm start 7 | ``` 8 | -------------------------------------------------------------------------------- /graphql.config.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "README_schema" : "Specifies how to load the GraphQL schema that completion, error highlighting, and documentation is based on in the IDE", 4 | "schema": { 5 | 6 | "README_file" : "Remove 'file' to use request url below. A relative or absolute path to the JSON from a schema introspection query, e.g. '{ data: ... }'. Changes to the file are watched.", 7 | "file": "graphql.schema.json", 8 | 9 | "README_request" : "To request the schema from a url instead, remove the 'file' JSON property above (and optionally delete the default graphql.schema.json file).", 10 | "request": { 11 | "url" : "http://--graphql-server--/path-to-schema-json-or-introspection-endpoint", 12 | "method" : "POST", 13 | "README_postIntrospectionQuery" : "Whether to POST an introspectionQuery to the url. If the url always returns the schema JSON, set to false and consider using GET", 14 | "postIntrospectionQuery" : true, 15 | "README_options" : "See the 'Options' section at https://github.com/then/then-request", 16 | "options" : { 17 | "headers": { 18 | "user-agent" : "JS GraphQL" 19 | } 20 | } 21 | } 22 | 23 | }, 24 | 25 | "README_endpoints": "A list of GraphQL endpoints that can be queried from '.graphql' files in the IDE", 26 | "endpoints" : [ 27 | { 28 | "name": "Default (http://localhost:8080/graphql)", 29 | "url": "http://localhost:8080/graphql", 30 | "options" : { 31 | "headers": { 32 | "user-agent" : "JS GraphQL" 33 | } 34 | } 35 | } 36 | ] 37 | 38 | } -------------------------------------------------------------------------------- /graphql.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "__schema": { 4 | "queryType": { 5 | "name": "RedditAPI" 6 | }, 7 | "mutationType": null, 8 | "subscriptionType": null, 9 | "types": [ 10 | { 11 | "kind": "OBJECT", 12 | "name": "RedditAPI", 13 | "description": "The Reddit API", 14 | "fields": [ 15 | { 16 | "name": "subreddit", 17 | "description": null, 18 | "args": [ 19 | { 20 | "name": "name", 21 | "description": "Name of the subreddit", 22 | "type": { 23 | "kind": "NON_NULL", 24 | "name": null, 25 | "ofType": { 26 | "kind": "SCALAR", 27 | "name": "String", 28 | "ofType": null 29 | } 30 | }, 31 | "defaultValue": null 32 | } 33 | ], 34 | "type": { 35 | "kind": "OBJECT", 36 | "name": "RedditSubreddit", 37 | "ofType": null 38 | }, 39 | "isDeprecated": false, 40 | "deprecationReason": null 41 | }, 42 | { 43 | "name": "user", 44 | "description": null, 45 | "args": [ 46 | { 47 | "name": "username", 48 | "description": "Username of the user", 49 | "type": { 50 | "kind": "NON_NULL", 51 | "name": null, 52 | "ofType": { 53 | "kind": "SCALAR", 54 | "name": "String", 55 | "ofType": null 56 | } 57 | }, 58 | "defaultValue": null 59 | } 60 | ], 61 | "type": { 62 | "kind": "OBJECT", 63 | "name": "RedditUser", 64 | "ofType": null 65 | }, 66 | "isDeprecated": false, 67 | "deprecationReason": null 68 | } 69 | ], 70 | "inputFields": null, 71 | "interfaces": [], 72 | "enumValues": null, 73 | "possibleTypes": null 74 | }, 75 | { 76 | "kind": "SCALAR", 77 | "name": "String", 78 | "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", 79 | "fields": null, 80 | "inputFields": null, 81 | "interfaces": null, 82 | "enumValues": null, 83 | "possibleTypes": null 84 | }, 85 | { 86 | "kind": "OBJECT", 87 | "name": "RedditSubreddit", 88 | "description": "Information about and listings in a subreddit", 89 | "fields": [ 90 | { 91 | "name": "name", 92 | "description": "Name of the subreddit", 93 | "args": [], 94 | "type": { 95 | "kind": "NON_NULL", 96 | "name": null, 97 | "ofType": { 98 | "kind": "SCALAR", 99 | "name": "String", 100 | "ofType": null 101 | } 102 | }, 103 | "isDeprecated": false, 104 | "deprecationReason": null 105 | }, 106 | { 107 | "name": "fullnameId", 108 | "description": "The Reddit API fullname of the subreddit", 109 | "args": [], 110 | "type": { 111 | "kind": "NON_NULL", 112 | "name": null, 113 | "ofType": { 114 | "kind": "SCALAR", 115 | "name": "String", 116 | "ofType": null 117 | } 118 | }, 119 | "isDeprecated": false, 120 | "deprecationReason": null 121 | }, 122 | { 123 | "name": "title", 124 | "description": "Title of the subreddit", 125 | "args": [], 126 | "type": { 127 | "kind": "NON_NULL", 128 | "name": null, 129 | "ofType": { 130 | "kind": "SCALAR", 131 | "name": "String", 132 | "ofType": null 133 | } 134 | }, 135 | "isDeprecated": false, 136 | "deprecationReason": null 137 | }, 138 | { 139 | "name": "publicDescription", 140 | "description": "Description of the subreddit", 141 | "args": [], 142 | "type": { 143 | "kind": "NON_NULL", 144 | "name": null, 145 | "ofType": { 146 | "kind": "SCALAR", 147 | "name": "String", 148 | "ofType": null 149 | } 150 | }, 151 | "isDeprecated": false, 152 | "deprecationReason": null 153 | }, 154 | { 155 | "name": "accountsActive", 156 | "description": "Accounts active right now on the subreddit", 157 | "args": [], 158 | "type": { 159 | "kind": "NON_NULL", 160 | "name": null, 161 | "ofType": { 162 | "kind": "SCALAR", 163 | "name": "Int", 164 | "ofType": null 165 | } 166 | }, 167 | "isDeprecated": false, 168 | "deprecationReason": null 169 | }, 170 | { 171 | "name": "subscribers", 172 | "description": "Number of subscribers to the subreddit", 173 | "args": [], 174 | "type": { 175 | "kind": "NON_NULL", 176 | "name": null, 177 | "ofType": { 178 | "kind": "SCALAR", 179 | "name": "Int", 180 | "ofType": null 181 | } 182 | }, 183 | "isDeprecated": false, 184 | "deprecationReason": null 185 | }, 186 | { 187 | "name": "created", 188 | "description": "Creation date of the subreddit, in Unix Time (UTC)", 189 | "args": [], 190 | "type": { 191 | "kind": "NON_NULL", 192 | "name": null, 193 | "ofType": { 194 | "kind": "SCALAR", 195 | "name": "Float", 196 | "ofType": null 197 | } 198 | }, 199 | "isDeprecated": false, 200 | "deprecationReason": null 201 | }, 202 | { 203 | "name": "createdISO", 204 | "description": "Creation date of the subreddit, in ISO8601", 205 | "args": [], 206 | "type": { 207 | "kind": "NON_NULL", 208 | "name": null, 209 | "ofType": { 210 | "kind": "SCALAR", 211 | "name": "String", 212 | "ofType": null 213 | } 214 | }, 215 | "isDeprecated": false, 216 | "deprecationReason": null 217 | }, 218 | { 219 | "name": "hotListings", 220 | "description": "Hot/\"Front Page\" listings of the subreddit", 221 | "args": [ 222 | { 223 | "name": "after", 224 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 225 | "type": { 226 | "kind": "SCALAR", 227 | "name": "String", 228 | "ofType": null 229 | }, 230 | "defaultValue": null 231 | }, 232 | { 233 | "name": "before", 234 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 235 | "type": { 236 | "kind": "SCALAR", 237 | "name": "String", 238 | "ofType": null 239 | }, 240 | "defaultValue": null 241 | }, 242 | { 243 | "name": "count", 244 | "description": "The number of items already seen in this listing", 245 | "type": { 246 | "kind": "SCALAR", 247 | "name": "Int", 248 | "ofType": null 249 | }, 250 | "defaultValue": null 251 | }, 252 | { 253 | "name": "limit", 254 | "description": "The maximum number of items to return in this slice of the listing.", 255 | "type": { 256 | "kind": "SCALAR", 257 | "name": "Int", 258 | "ofType": null 259 | }, 260 | "defaultValue": null 261 | } 262 | ], 263 | "type": { 264 | "kind": "NON_NULL", 265 | "name": null, 266 | "ofType": { 267 | "kind": "LIST", 268 | "name": null, 269 | "ofType": { 270 | "kind": "OBJECT", 271 | "name": "RedditLink", 272 | "ofType": null 273 | } 274 | } 275 | }, 276 | "isDeprecated": false, 277 | "deprecationReason": null 278 | }, 279 | { 280 | "name": "newListings", 281 | "description": "Newest listings of the subreddit", 282 | "args": [ 283 | { 284 | "name": "after", 285 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 286 | "type": { 287 | "kind": "SCALAR", 288 | "name": "String", 289 | "ofType": null 290 | }, 291 | "defaultValue": null 292 | }, 293 | { 294 | "name": "before", 295 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 296 | "type": { 297 | "kind": "SCALAR", 298 | "name": "String", 299 | "ofType": null 300 | }, 301 | "defaultValue": null 302 | }, 303 | { 304 | "name": "count", 305 | "description": "The number of items already seen in this listing", 306 | "type": { 307 | "kind": "SCALAR", 308 | "name": "Int", 309 | "ofType": null 310 | }, 311 | "defaultValue": null 312 | }, 313 | { 314 | "name": "limit", 315 | "description": "The maximum number of items to return in this slice of the listing.", 316 | "type": { 317 | "kind": "SCALAR", 318 | "name": "Int", 319 | "ofType": null 320 | }, 321 | "defaultValue": null 322 | } 323 | ], 324 | "type": { 325 | "kind": "NON_NULL", 326 | "name": null, 327 | "ofType": { 328 | "kind": "LIST", 329 | "name": null, 330 | "ofType": { 331 | "kind": "OBJECT", 332 | "name": "RedditLink", 333 | "ofType": null 334 | } 335 | } 336 | }, 337 | "isDeprecated": false, 338 | "deprecationReason": null 339 | }, 340 | { 341 | "name": "risingListings", 342 | "description": "Rising listings of the subreddit", 343 | "args": [ 344 | { 345 | "name": "after", 346 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 347 | "type": { 348 | "kind": "SCALAR", 349 | "name": "String", 350 | "ofType": null 351 | }, 352 | "defaultValue": null 353 | }, 354 | { 355 | "name": "before", 356 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 357 | "type": { 358 | "kind": "SCALAR", 359 | "name": "String", 360 | "ofType": null 361 | }, 362 | "defaultValue": null 363 | }, 364 | { 365 | "name": "count", 366 | "description": "The number of items already seen in this listing", 367 | "type": { 368 | "kind": "SCALAR", 369 | "name": "Int", 370 | "ofType": null 371 | }, 372 | "defaultValue": null 373 | }, 374 | { 375 | "name": "limit", 376 | "description": "The maximum number of items to return in this slice of the listing.", 377 | "type": { 378 | "kind": "SCALAR", 379 | "name": "Int", 380 | "ofType": null 381 | }, 382 | "defaultValue": null 383 | } 384 | ], 385 | "type": { 386 | "kind": "NON_NULL", 387 | "name": null, 388 | "ofType": { 389 | "kind": "LIST", 390 | "name": null, 391 | "ofType": { 392 | "kind": "OBJECT", 393 | "name": "RedditLink", 394 | "ofType": null 395 | } 396 | } 397 | }, 398 | "isDeprecated": false, 399 | "deprecationReason": null 400 | }, 401 | { 402 | "name": "controversialListings", 403 | "description": "Controversial listings of the subreddit", 404 | "args": [ 405 | { 406 | "name": "after", 407 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 408 | "type": { 409 | "kind": "SCALAR", 410 | "name": "String", 411 | "ofType": null 412 | }, 413 | "defaultValue": null 414 | }, 415 | { 416 | "name": "before", 417 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 418 | "type": { 419 | "kind": "SCALAR", 420 | "name": "String", 421 | "ofType": null 422 | }, 423 | "defaultValue": null 424 | }, 425 | { 426 | "name": "count", 427 | "description": "The number of items already seen in this listing", 428 | "type": { 429 | "kind": "SCALAR", 430 | "name": "Int", 431 | "ofType": null 432 | }, 433 | "defaultValue": null 434 | }, 435 | { 436 | "name": "limit", 437 | "description": "The maximum number of items to return in this slice of the listing.", 438 | "type": { 439 | "kind": "SCALAR", 440 | "name": "Int", 441 | "ofType": null 442 | }, 443 | "defaultValue": null 444 | }, 445 | { 446 | "name": "timeInterval", 447 | "description": "Time interval to retrieve listings", 448 | "type": { 449 | "kind": "ENUM", 450 | "name": "TimeInterval", 451 | "ofType": null 452 | }, 453 | "defaultValue": null 454 | } 455 | ], 456 | "type": { 457 | "kind": "NON_NULL", 458 | "name": null, 459 | "ofType": { 460 | "kind": "LIST", 461 | "name": null, 462 | "ofType": { 463 | "kind": "OBJECT", 464 | "name": "RedditLink", 465 | "ofType": null 466 | } 467 | } 468 | }, 469 | "isDeprecated": false, 470 | "deprecationReason": null 471 | }, 472 | { 473 | "name": "topListings", 474 | "description": "Top listings of the subreddit", 475 | "args": [ 476 | { 477 | "name": "after", 478 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 479 | "type": { 480 | "kind": "SCALAR", 481 | "name": "String", 482 | "ofType": null 483 | }, 484 | "defaultValue": null 485 | }, 486 | { 487 | "name": "before", 488 | "description": "FullnameId of an item in the listing to use as the anchor point of the slice.", 489 | "type": { 490 | "kind": "SCALAR", 491 | "name": "String", 492 | "ofType": null 493 | }, 494 | "defaultValue": null 495 | }, 496 | { 497 | "name": "count", 498 | "description": "The number of items already seen in this listing", 499 | "type": { 500 | "kind": "SCALAR", 501 | "name": "Int", 502 | "ofType": null 503 | }, 504 | "defaultValue": null 505 | }, 506 | { 507 | "name": "limit", 508 | "description": "The maximum number of items to return in this slice of the listing.", 509 | "type": { 510 | "kind": "SCALAR", 511 | "name": "Int", 512 | "ofType": null 513 | }, 514 | "defaultValue": null 515 | }, 516 | { 517 | "name": "timeInterval", 518 | "description": "Time interval to retrieve listings", 519 | "type": { 520 | "kind": "ENUM", 521 | "name": "TimeInterval", 522 | "ofType": null 523 | }, 524 | "defaultValue": null 525 | } 526 | ], 527 | "type": { 528 | "kind": "NON_NULL", 529 | "name": null, 530 | "ofType": { 531 | "kind": "LIST", 532 | "name": null, 533 | "ofType": { 534 | "kind": "OBJECT", 535 | "name": "RedditLink", 536 | "ofType": null 537 | } 538 | } 539 | }, 540 | "isDeprecated": false, 541 | "deprecationReason": null 542 | } 543 | ], 544 | "inputFields": null, 545 | "interfaces": [], 546 | "enumValues": null, 547 | "possibleTypes": null 548 | }, 549 | { 550 | "kind": "SCALAR", 551 | "name": "Int", 552 | "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. ", 553 | "fields": null, 554 | "inputFields": null, 555 | "interfaces": null, 556 | "enumValues": null, 557 | "possibleTypes": null 558 | }, 559 | { 560 | "kind": "SCALAR", 561 | "name": "Float", 562 | "description": "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ", 563 | "fields": null, 564 | "inputFields": null, 565 | "interfaces": null, 566 | "enumValues": null, 567 | "possibleTypes": null 568 | }, 569 | { 570 | "kind": "OBJECT", 571 | "name": "RedditLink", 572 | "description": "A link posted to a subreddit", 573 | "fields": [ 574 | { 575 | "name": "title", 576 | "description": "Title of the link", 577 | "args": [], 578 | "type": { 579 | "kind": "NON_NULL", 580 | "name": null, 581 | "ofType": { 582 | "kind": "SCALAR", 583 | "name": "String", 584 | "ofType": null 585 | } 586 | }, 587 | "isDeprecated": false, 588 | "deprecationReason": null 589 | }, 590 | { 591 | "name": "fullnameId", 592 | "description": "The Reddit API fullname of the link", 593 | "args": [], 594 | "type": { 595 | "kind": "NON_NULL", 596 | "name": null, 597 | "ofType": { 598 | "kind": "SCALAR", 599 | "name": "String", 600 | "ofType": null 601 | } 602 | }, 603 | "isDeprecated": false, 604 | "deprecationReason": null 605 | }, 606 | { 607 | "name": "score", 608 | "description": "Score of the link", 609 | "args": [], 610 | "type": { 611 | "kind": "NON_NULL", 612 | "name": null, 613 | "ofType": { 614 | "kind": "SCALAR", 615 | "name": "Int", 616 | "ofType": null 617 | } 618 | }, 619 | "isDeprecated": false, 620 | "deprecationReason": null 621 | }, 622 | { 623 | "name": "numComments", 624 | "description": "Number of comments", 625 | "args": [], 626 | "type": { 627 | "kind": "NON_NULL", 628 | "name": null, 629 | "ofType": { 630 | "kind": "SCALAR", 631 | "name": "Int", 632 | "ofType": null 633 | } 634 | }, 635 | "isDeprecated": false, 636 | "deprecationReason": null 637 | }, 638 | { 639 | "name": "url", 640 | "description": "URL of the link", 641 | "args": [], 642 | "type": { 643 | "kind": "NON_NULL", 644 | "name": null, 645 | "ofType": { 646 | "kind": "SCALAR", 647 | "name": "String", 648 | "ofType": null 649 | } 650 | }, 651 | "isDeprecated": false, 652 | "deprecationReason": null 653 | }, 654 | { 655 | "name": "author", 656 | "description": "Author of the link", 657 | "args": [], 658 | "type": { 659 | "kind": "NON_NULL", 660 | "name": null, 661 | "ofType": { 662 | "kind": "OBJECT", 663 | "name": "RedditUser", 664 | "ofType": null 665 | } 666 | }, 667 | "isDeprecated": false, 668 | "deprecationReason": null 669 | }, 670 | { 671 | "name": "comments", 672 | "description": "Comments on the link", 673 | "args": [ 674 | { 675 | "name": "depth", 676 | "description": "Maximum depth of subtrees in the thread", 677 | "type": { 678 | "kind": "SCALAR", 679 | "name": "Int", 680 | "ofType": null 681 | }, 682 | "defaultValue": null 683 | }, 684 | { 685 | "name": "limit", 686 | "description": "Maximum number of comments to return", 687 | "type": { 688 | "kind": "SCALAR", 689 | "name": "Int", 690 | "ofType": null 691 | }, 692 | "defaultValue": null 693 | } 694 | ], 695 | "type": { 696 | "kind": "NON_NULL", 697 | "name": null, 698 | "ofType": { 699 | "kind": "LIST", 700 | "name": null, 701 | "ofType": { 702 | "kind": "OBJECT", 703 | "name": "RedditComment", 704 | "ofType": null 705 | } 706 | } 707 | }, 708 | "isDeprecated": false, 709 | "deprecationReason": null 710 | } 711 | ], 712 | "inputFields": null, 713 | "interfaces": [], 714 | "enumValues": null, 715 | "possibleTypes": null 716 | }, 717 | { 718 | "kind": "OBJECT", 719 | "name": "RedditUser", 720 | "description": "Information about a Reddit user", 721 | "fields": [ 722 | { 723 | "name": "fullnameId", 724 | "description": "The Reddit API fullname of the user", 725 | "args": [], 726 | "type": { 727 | "kind": "NON_NULL", 728 | "name": null, 729 | "ofType": { 730 | "kind": "SCALAR", 731 | "name": "String", 732 | "ofType": null 733 | } 734 | }, 735 | "isDeprecated": false, 736 | "deprecationReason": null 737 | }, 738 | { 739 | "name": "username", 740 | "description": "The user's unique username.", 741 | "args": [], 742 | "type": { 743 | "kind": "NON_NULL", 744 | "name": null, 745 | "ofType": { 746 | "kind": "SCALAR", 747 | "name": "String", 748 | "ofType": null 749 | } 750 | }, 751 | "isDeprecated": false, 752 | "deprecationReason": null 753 | }, 754 | { 755 | "name": "created", 756 | "description": "Creation date of the user, in Unix Time (UTC)", 757 | "args": [], 758 | "type": { 759 | "kind": "NON_NULL", 760 | "name": null, 761 | "ofType": { 762 | "kind": "SCALAR", 763 | "name": "Float", 764 | "ofType": null 765 | } 766 | }, 767 | "isDeprecated": false, 768 | "deprecationReason": null 769 | }, 770 | { 771 | "name": "createdISO", 772 | "description": "Creation date of the user, in ISO8601", 773 | "args": [], 774 | "type": { 775 | "kind": "NON_NULL", 776 | "name": null, 777 | "ofType": { 778 | "kind": "SCALAR", 779 | "name": "String", 780 | "ofType": null 781 | } 782 | }, 783 | "isDeprecated": false, 784 | "deprecationReason": null 785 | }, 786 | { 787 | "name": "linkKarma", 788 | "description": "Karma by links of the user", 789 | "args": [], 790 | "type": { 791 | "kind": "NON_NULL", 792 | "name": null, 793 | "ofType": { 794 | "kind": "SCALAR", 795 | "name": "Int", 796 | "ofType": null 797 | } 798 | }, 799 | "isDeprecated": false, 800 | "deprecationReason": null 801 | }, 802 | { 803 | "name": "commentKarma", 804 | "description": "Karma by comments of the user", 805 | "args": [], 806 | "type": { 807 | "kind": "NON_NULL", 808 | "name": null, 809 | "ofType": { 810 | "kind": "SCALAR", 811 | "name": "Int", 812 | "ofType": null 813 | } 814 | }, 815 | "isDeprecated": false, 816 | "deprecationReason": null 817 | } 818 | ], 819 | "inputFields": null, 820 | "interfaces": [], 821 | "enumValues": null, 822 | "possibleTypes": null 823 | }, 824 | { 825 | "kind": "OBJECT", 826 | "name": "RedditComment", 827 | "description": "A comment on a link", 828 | "fields": [ 829 | { 830 | "name": "author", 831 | "description": "Author of the comment", 832 | "args": [], 833 | "type": { 834 | "kind": "NON_NULL", 835 | "name": null, 836 | "ofType": { 837 | "kind": "OBJECT", 838 | "name": "RedditUser", 839 | "ofType": null 840 | } 841 | }, 842 | "isDeprecated": false, 843 | "deprecationReason": null 844 | }, 845 | { 846 | "name": "body", 847 | "description": "Body of the comment", 848 | "args": [], 849 | "type": { 850 | "kind": "NON_NULL", 851 | "name": null, 852 | "ofType": { 853 | "kind": "SCALAR", 854 | "name": "String", 855 | "ofType": null 856 | } 857 | }, 858 | "isDeprecated": false, 859 | "deprecationReason": null 860 | }, 861 | { 862 | "name": "replies", 863 | "description": "Replies to the comment", 864 | "args": [ 865 | { 866 | "name": "depth", 867 | "description": "Maximum depth of subtrees in the thread", 868 | "type": { 869 | "kind": "SCALAR", 870 | "name": "Int", 871 | "ofType": null 872 | }, 873 | "defaultValue": null 874 | }, 875 | { 876 | "name": "limit", 877 | "description": "Maximum number of comments to return", 878 | "type": { 879 | "kind": "SCALAR", 880 | "name": "Int", 881 | "ofType": null 882 | }, 883 | "defaultValue": null 884 | } 885 | ], 886 | "type": { 887 | "kind": "NON_NULL", 888 | "name": null, 889 | "ofType": { 890 | "kind": "LIST", 891 | "name": null, 892 | "ofType": { 893 | "kind": "OBJECT", 894 | "name": "RedditComment", 895 | "ofType": null 896 | } 897 | } 898 | }, 899 | "isDeprecated": false, 900 | "deprecationReason": null 901 | } 902 | ], 903 | "inputFields": null, 904 | "interfaces": [], 905 | "enumValues": null, 906 | "possibleTypes": null 907 | }, 908 | { 909 | "kind": "ENUM", 910 | "name": "TimeInterval", 911 | "description": "Time interval by which listings are queried", 912 | "fields": null, 913 | "inputFields": null, 914 | "interfaces": null, 915 | "enumValues": [ 916 | { 917 | "name": "hour", 918 | "description": null, 919 | "isDeprecated": false, 920 | "deprecationReason": null 921 | }, 922 | { 923 | "name": "day", 924 | "description": null, 925 | "isDeprecated": false, 926 | "deprecationReason": null 927 | }, 928 | { 929 | "name": "week", 930 | "description": null, 931 | "isDeprecated": false, 932 | "deprecationReason": null 933 | }, 934 | { 935 | "name": "month", 936 | "description": null, 937 | "isDeprecated": false, 938 | "deprecationReason": null 939 | }, 940 | { 941 | "name": "year", 942 | "description": null, 943 | "isDeprecated": false, 944 | "deprecationReason": null 945 | }, 946 | { 947 | "name": "all", 948 | "description": null, 949 | "isDeprecated": false, 950 | "deprecationReason": null 951 | } 952 | ], 953 | "possibleTypes": null 954 | }, 955 | { 956 | "kind": "OBJECT", 957 | "name": "__Schema", 958 | "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", 959 | "fields": [ 960 | { 961 | "name": "types", 962 | "description": "A list of all types supported by this server.", 963 | "args": [], 964 | "type": { 965 | "kind": "NON_NULL", 966 | "name": null, 967 | "ofType": { 968 | "kind": "LIST", 969 | "name": null, 970 | "ofType": { 971 | "kind": "NON_NULL", 972 | "name": null, 973 | "ofType": { 974 | "kind": "OBJECT", 975 | "name": "__Type", 976 | "ofType": null 977 | } 978 | } 979 | } 980 | }, 981 | "isDeprecated": false, 982 | "deprecationReason": null 983 | }, 984 | { 985 | "name": "queryType", 986 | "description": "The type that query operations will be rooted at.", 987 | "args": [], 988 | "type": { 989 | "kind": "NON_NULL", 990 | "name": null, 991 | "ofType": { 992 | "kind": "OBJECT", 993 | "name": "__Type", 994 | "ofType": null 995 | } 996 | }, 997 | "isDeprecated": false, 998 | "deprecationReason": null 999 | }, 1000 | { 1001 | "name": "mutationType", 1002 | "description": "If this server supports mutation, the type that mutation operations will be rooted at.", 1003 | "args": [], 1004 | "type": { 1005 | "kind": "OBJECT", 1006 | "name": "__Type", 1007 | "ofType": null 1008 | }, 1009 | "isDeprecated": false, 1010 | "deprecationReason": null 1011 | }, 1012 | { 1013 | "name": "subscriptionType", 1014 | "description": "If this server support subscription, the type that subscription operations will be rooted at.", 1015 | "args": [], 1016 | "type": { 1017 | "kind": "OBJECT", 1018 | "name": "__Type", 1019 | "ofType": null 1020 | }, 1021 | "isDeprecated": false, 1022 | "deprecationReason": null 1023 | }, 1024 | { 1025 | "name": "directives", 1026 | "description": "A list of all directives supported by this server.", 1027 | "args": [], 1028 | "type": { 1029 | "kind": "NON_NULL", 1030 | "name": null, 1031 | "ofType": { 1032 | "kind": "LIST", 1033 | "name": null, 1034 | "ofType": { 1035 | "kind": "NON_NULL", 1036 | "name": null, 1037 | "ofType": { 1038 | "kind": "OBJECT", 1039 | "name": "__Directive", 1040 | "ofType": null 1041 | } 1042 | } 1043 | } 1044 | }, 1045 | "isDeprecated": false, 1046 | "deprecationReason": null 1047 | } 1048 | ], 1049 | "inputFields": null, 1050 | "interfaces": [], 1051 | "enumValues": null, 1052 | "possibleTypes": null 1053 | }, 1054 | { 1055 | "kind": "OBJECT", 1056 | "name": "__Type", 1057 | "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.", 1058 | "fields": [ 1059 | { 1060 | "name": "kind", 1061 | "description": null, 1062 | "args": [], 1063 | "type": { 1064 | "kind": "NON_NULL", 1065 | "name": null, 1066 | "ofType": { 1067 | "kind": "ENUM", 1068 | "name": "__TypeKind", 1069 | "ofType": null 1070 | } 1071 | }, 1072 | "isDeprecated": false, 1073 | "deprecationReason": null 1074 | }, 1075 | { 1076 | "name": "name", 1077 | "description": null, 1078 | "args": [], 1079 | "type": { 1080 | "kind": "SCALAR", 1081 | "name": "String", 1082 | "ofType": null 1083 | }, 1084 | "isDeprecated": false, 1085 | "deprecationReason": null 1086 | }, 1087 | { 1088 | "name": "description", 1089 | "description": null, 1090 | "args": [], 1091 | "type": { 1092 | "kind": "SCALAR", 1093 | "name": "String", 1094 | "ofType": null 1095 | }, 1096 | "isDeprecated": false, 1097 | "deprecationReason": null 1098 | }, 1099 | { 1100 | "name": "fields", 1101 | "description": null, 1102 | "args": [ 1103 | { 1104 | "name": "includeDeprecated", 1105 | "description": null, 1106 | "type": { 1107 | "kind": "SCALAR", 1108 | "name": "Boolean", 1109 | "ofType": null 1110 | }, 1111 | "defaultValue": "false" 1112 | } 1113 | ], 1114 | "type": { 1115 | "kind": "LIST", 1116 | "name": null, 1117 | "ofType": { 1118 | "kind": "NON_NULL", 1119 | "name": null, 1120 | "ofType": { 1121 | "kind": "OBJECT", 1122 | "name": "__Field", 1123 | "ofType": null 1124 | } 1125 | } 1126 | }, 1127 | "isDeprecated": false, 1128 | "deprecationReason": null 1129 | }, 1130 | { 1131 | "name": "interfaces", 1132 | "description": null, 1133 | "args": [], 1134 | "type": { 1135 | "kind": "LIST", 1136 | "name": null, 1137 | "ofType": { 1138 | "kind": "NON_NULL", 1139 | "name": null, 1140 | "ofType": { 1141 | "kind": "OBJECT", 1142 | "name": "__Type", 1143 | "ofType": null 1144 | } 1145 | } 1146 | }, 1147 | "isDeprecated": false, 1148 | "deprecationReason": null 1149 | }, 1150 | { 1151 | "name": "possibleTypes", 1152 | "description": null, 1153 | "args": [], 1154 | "type": { 1155 | "kind": "LIST", 1156 | "name": null, 1157 | "ofType": { 1158 | "kind": "NON_NULL", 1159 | "name": null, 1160 | "ofType": { 1161 | "kind": "OBJECT", 1162 | "name": "__Type", 1163 | "ofType": null 1164 | } 1165 | } 1166 | }, 1167 | "isDeprecated": false, 1168 | "deprecationReason": null 1169 | }, 1170 | { 1171 | "name": "enumValues", 1172 | "description": null, 1173 | "args": [ 1174 | { 1175 | "name": "includeDeprecated", 1176 | "description": null, 1177 | "type": { 1178 | "kind": "SCALAR", 1179 | "name": "Boolean", 1180 | "ofType": null 1181 | }, 1182 | "defaultValue": "false" 1183 | } 1184 | ], 1185 | "type": { 1186 | "kind": "LIST", 1187 | "name": null, 1188 | "ofType": { 1189 | "kind": "NON_NULL", 1190 | "name": null, 1191 | "ofType": { 1192 | "kind": "OBJECT", 1193 | "name": "__EnumValue", 1194 | "ofType": null 1195 | } 1196 | } 1197 | }, 1198 | "isDeprecated": false, 1199 | "deprecationReason": null 1200 | }, 1201 | { 1202 | "name": "inputFields", 1203 | "description": null, 1204 | "args": [], 1205 | "type": { 1206 | "kind": "LIST", 1207 | "name": null, 1208 | "ofType": { 1209 | "kind": "NON_NULL", 1210 | "name": null, 1211 | "ofType": { 1212 | "kind": "OBJECT", 1213 | "name": "__InputValue", 1214 | "ofType": null 1215 | } 1216 | } 1217 | }, 1218 | "isDeprecated": false, 1219 | "deprecationReason": null 1220 | }, 1221 | { 1222 | "name": "ofType", 1223 | "description": null, 1224 | "args": [], 1225 | "type": { 1226 | "kind": "OBJECT", 1227 | "name": "__Type", 1228 | "ofType": null 1229 | }, 1230 | "isDeprecated": false, 1231 | "deprecationReason": null 1232 | } 1233 | ], 1234 | "inputFields": null, 1235 | "interfaces": [], 1236 | "enumValues": null, 1237 | "possibleTypes": null 1238 | }, 1239 | { 1240 | "kind": "ENUM", 1241 | "name": "__TypeKind", 1242 | "description": "An enum describing what kind of type a given `__Type` is.", 1243 | "fields": null, 1244 | "inputFields": null, 1245 | "interfaces": null, 1246 | "enumValues": [ 1247 | { 1248 | "name": "SCALAR", 1249 | "description": "Indicates this type is a scalar.", 1250 | "isDeprecated": false, 1251 | "deprecationReason": null 1252 | }, 1253 | { 1254 | "name": "OBJECT", 1255 | "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", 1256 | "isDeprecated": false, 1257 | "deprecationReason": null 1258 | }, 1259 | { 1260 | "name": "INTERFACE", 1261 | "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", 1262 | "isDeprecated": false, 1263 | "deprecationReason": null 1264 | }, 1265 | { 1266 | "name": "UNION", 1267 | "description": "Indicates this type is a union. `possibleTypes` is a valid field.", 1268 | "isDeprecated": false, 1269 | "deprecationReason": null 1270 | }, 1271 | { 1272 | "name": "ENUM", 1273 | "description": "Indicates this type is an enum. `enumValues` is a valid field.", 1274 | "isDeprecated": false, 1275 | "deprecationReason": null 1276 | }, 1277 | { 1278 | "name": "INPUT_OBJECT", 1279 | "description": "Indicates this type is an input object. `inputFields` is a valid field.", 1280 | "isDeprecated": false, 1281 | "deprecationReason": null 1282 | }, 1283 | { 1284 | "name": "LIST", 1285 | "description": "Indicates this type is a list. `ofType` is a valid field.", 1286 | "isDeprecated": false, 1287 | "deprecationReason": null 1288 | }, 1289 | { 1290 | "name": "NON_NULL", 1291 | "description": "Indicates this type is a non-null. `ofType` is a valid field.", 1292 | "isDeprecated": false, 1293 | "deprecationReason": null 1294 | } 1295 | ], 1296 | "possibleTypes": null 1297 | }, 1298 | { 1299 | "kind": "SCALAR", 1300 | "name": "Boolean", 1301 | "description": "The `Boolean` scalar type represents `true` or `false`.", 1302 | "fields": null, 1303 | "inputFields": null, 1304 | "interfaces": null, 1305 | "enumValues": null, 1306 | "possibleTypes": null 1307 | }, 1308 | { 1309 | "kind": "OBJECT", 1310 | "name": "__Field", 1311 | "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.", 1312 | "fields": [ 1313 | { 1314 | "name": "name", 1315 | "description": null, 1316 | "args": [], 1317 | "type": { 1318 | "kind": "NON_NULL", 1319 | "name": null, 1320 | "ofType": { 1321 | "kind": "SCALAR", 1322 | "name": "String", 1323 | "ofType": null 1324 | } 1325 | }, 1326 | "isDeprecated": false, 1327 | "deprecationReason": null 1328 | }, 1329 | { 1330 | "name": "description", 1331 | "description": null, 1332 | "args": [], 1333 | "type": { 1334 | "kind": "SCALAR", 1335 | "name": "String", 1336 | "ofType": null 1337 | }, 1338 | "isDeprecated": false, 1339 | "deprecationReason": null 1340 | }, 1341 | { 1342 | "name": "args", 1343 | "description": null, 1344 | "args": [], 1345 | "type": { 1346 | "kind": "NON_NULL", 1347 | "name": null, 1348 | "ofType": { 1349 | "kind": "LIST", 1350 | "name": null, 1351 | "ofType": { 1352 | "kind": "NON_NULL", 1353 | "name": null, 1354 | "ofType": { 1355 | "kind": "OBJECT", 1356 | "name": "__InputValue", 1357 | "ofType": null 1358 | } 1359 | } 1360 | } 1361 | }, 1362 | "isDeprecated": false, 1363 | "deprecationReason": null 1364 | }, 1365 | { 1366 | "name": "type", 1367 | "description": null, 1368 | "args": [], 1369 | "type": { 1370 | "kind": "NON_NULL", 1371 | "name": null, 1372 | "ofType": { 1373 | "kind": "OBJECT", 1374 | "name": "__Type", 1375 | "ofType": null 1376 | } 1377 | }, 1378 | "isDeprecated": false, 1379 | "deprecationReason": null 1380 | }, 1381 | { 1382 | "name": "isDeprecated", 1383 | "description": null, 1384 | "args": [], 1385 | "type": { 1386 | "kind": "NON_NULL", 1387 | "name": null, 1388 | "ofType": { 1389 | "kind": "SCALAR", 1390 | "name": "Boolean", 1391 | "ofType": null 1392 | } 1393 | }, 1394 | "isDeprecated": false, 1395 | "deprecationReason": null 1396 | }, 1397 | { 1398 | "name": "deprecationReason", 1399 | "description": null, 1400 | "args": [], 1401 | "type": { 1402 | "kind": "SCALAR", 1403 | "name": "String", 1404 | "ofType": null 1405 | }, 1406 | "isDeprecated": false, 1407 | "deprecationReason": null 1408 | } 1409 | ], 1410 | "inputFields": null, 1411 | "interfaces": [], 1412 | "enumValues": null, 1413 | "possibleTypes": null 1414 | }, 1415 | { 1416 | "kind": "OBJECT", 1417 | "name": "__InputValue", 1418 | "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.", 1419 | "fields": [ 1420 | { 1421 | "name": "name", 1422 | "description": null, 1423 | "args": [], 1424 | "type": { 1425 | "kind": "NON_NULL", 1426 | "name": null, 1427 | "ofType": { 1428 | "kind": "SCALAR", 1429 | "name": "String", 1430 | "ofType": null 1431 | } 1432 | }, 1433 | "isDeprecated": false, 1434 | "deprecationReason": null 1435 | }, 1436 | { 1437 | "name": "description", 1438 | "description": null, 1439 | "args": [], 1440 | "type": { 1441 | "kind": "SCALAR", 1442 | "name": "String", 1443 | "ofType": null 1444 | }, 1445 | "isDeprecated": false, 1446 | "deprecationReason": null 1447 | }, 1448 | { 1449 | "name": "type", 1450 | "description": null, 1451 | "args": [], 1452 | "type": { 1453 | "kind": "NON_NULL", 1454 | "name": null, 1455 | "ofType": { 1456 | "kind": "OBJECT", 1457 | "name": "__Type", 1458 | "ofType": null 1459 | } 1460 | }, 1461 | "isDeprecated": false, 1462 | "deprecationReason": null 1463 | }, 1464 | { 1465 | "name": "defaultValue", 1466 | "description": "A GraphQL-formatted string representing the default value for this input value.", 1467 | "args": [], 1468 | "type": { 1469 | "kind": "SCALAR", 1470 | "name": "String", 1471 | "ofType": null 1472 | }, 1473 | "isDeprecated": false, 1474 | "deprecationReason": null 1475 | } 1476 | ], 1477 | "inputFields": null, 1478 | "interfaces": [], 1479 | "enumValues": null, 1480 | "possibleTypes": null 1481 | }, 1482 | { 1483 | "kind": "OBJECT", 1484 | "name": "__EnumValue", 1485 | "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.", 1486 | "fields": [ 1487 | { 1488 | "name": "name", 1489 | "description": null, 1490 | "args": [], 1491 | "type": { 1492 | "kind": "NON_NULL", 1493 | "name": null, 1494 | "ofType": { 1495 | "kind": "SCALAR", 1496 | "name": "String", 1497 | "ofType": null 1498 | } 1499 | }, 1500 | "isDeprecated": false, 1501 | "deprecationReason": null 1502 | }, 1503 | { 1504 | "name": "description", 1505 | "description": null, 1506 | "args": [], 1507 | "type": { 1508 | "kind": "SCALAR", 1509 | "name": "String", 1510 | "ofType": null 1511 | }, 1512 | "isDeprecated": false, 1513 | "deprecationReason": null 1514 | }, 1515 | { 1516 | "name": "isDeprecated", 1517 | "description": null, 1518 | "args": [], 1519 | "type": { 1520 | "kind": "NON_NULL", 1521 | "name": null, 1522 | "ofType": { 1523 | "kind": "SCALAR", 1524 | "name": "Boolean", 1525 | "ofType": null 1526 | } 1527 | }, 1528 | "isDeprecated": false, 1529 | "deprecationReason": null 1530 | }, 1531 | { 1532 | "name": "deprecationReason", 1533 | "description": null, 1534 | "args": [], 1535 | "type": { 1536 | "kind": "SCALAR", 1537 | "name": "String", 1538 | "ofType": null 1539 | }, 1540 | "isDeprecated": false, 1541 | "deprecationReason": null 1542 | } 1543 | ], 1544 | "inputFields": null, 1545 | "interfaces": [], 1546 | "enumValues": null, 1547 | "possibleTypes": null 1548 | }, 1549 | { 1550 | "kind": "OBJECT", 1551 | "name": "__Directive", 1552 | "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", 1553 | "fields": [ 1554 | { 1555 | "name": "name", 1556 | "description": null, 1557 | "args": [], 1558 | "type": { 1559 | "kind": "NON_NULL", 1560 | "name": null, 1561 | "ofType": { 1562 | "kind": "SCALAR", 1563 | "name": "String", 1564 | "ofType": null 1565 | } 1566 | }, 1567 | "isDeprecated": false, 1568 | "deprecationReason": null 1569 | }, 1570 | { 1571 | "name": "description", 1572 | "description": null, 1573 | "args": [], 1574 | "type": { 1575 | "kind": "SCALAR", 1576 | "name": "String", 1577 | "ofType": null 1578 | }, 1579 | "isDeprecated": false, 1580 | "deprecationReason": null 1581 | }, 1582 | { 1583 | "name": "locations", 1584 | "description": null, 1585 | "args": [], 1586 | "type": { 1587 | "kind": "NON_NULL", 1588 | "name": null, 1589 | "ofType": { 1590 | "kind": "LIST", 1591 | "name": null, 1592 | "ofType": { 1593 | "kind": "NON_NULL", 1594 | "name": null, 1595 | "ofType": { 1596 | "kind": "ENUM", 1597 | "name": "__DirectiveLocation", 1598 | "ofType": null 1599 | } 1600 | } 1601 | } 1602 | }, 1603 | "isDeprecated": false, 1604 | "deprecationReason": null 1605 | }, 1606 | { 1607 | "name": "args", 1608 | "description": null, 1609 | "args": [], 1610 | "type": { 1611 | "kind": "NON_NULL", 1612 | "name": null, 1613 | "ofType": { 1614 | "kind": "LIST", 1615 | "name": null, 1616 | "ofType": { 1617 | "kind": "NON_NULL", 1618 | "name": null, 1619 | "ofType": { 1620 | "kind": "OBJECT", 1621 | "name": "__InputValue", 1622 | "ofType": null 1623 | } 1624 | } 1625 | } 1626 | }, 1627 | "isDeprecated": false, 1628 | "deprecationReason": null 1629 | }, 1630 | { 1631 | "name": "onOperation", 1632 | "description": null, 1633 | "args": [], 1634 | "type": { 1635 | "kind": "NON_NULL", 1636 | "name": null, 1637 | "ofType": { 1638 | "kind": "SCALAR", 1639 | "name": "Boolean", 1640 | "ofType": null 1641 | } 1642 | }, 1643 | "isDeprecated": true, 1644 | "deprecationReason": "Use `locations`." 1645 | }, 1646 | { 1647 | "name": "onFragment", 1648 | "description": null, 1649 | "args": [], 1650 | "type": { 1651 | "kind": "NON_NULL", 1652 | "name": null, 1653 | "ofType": { 1654 | "kind": "SCALAR", 1655 | "name": "Boolean", 1656 | "ofType": null 1657 | } 1658 | }, 1659 | "isDeprecated": true, 1660 | "deprecationReason": "Use `locations`." 1661 | }, 1662 | { 1663 | "name": "onField", 1664 | "description": null, 1665 | "args": [], 1666 | "type": { 1667 | "kind": "NON_NULL", 1668 | "name": null, 1669 | "ofType": { 1670 | "kind": "SCALAR", 1671 | "name": "Boolean", 1672 | "ofType": null 1673 | } 1674 | }, 1675 | "isDeprecated": true, 1676 | "deprecationReason": "Use `locations`." 1677 | } 1678 | ], 1679 | "inputFields": null, 1680 | "interfaces": [], 1681 | "enumValues": null, 1682 | "possibleTypes": null 1683 | }, 1684 | { 1685 | "kind": "ENUM", 1686 | "name": "__DirectiveLocation", 1687 | "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.", 1688 | "fields": null, 1689 | "inputFields": null, 1690 | "interfaces": null, 1691 | "enumValues": [ 1692 | { 1693 | "name": "QUERY", 1694 | "description": "Location adjacent to a query operation.", 1695 | "isDeprecated": false, 1696 | "deprecationReason": null 1697 | }, 1698 | { 1699 | "name": "MUTATION", 1700 | "description": "Location adjacent to a mutation operation.", 1701 | "isDeprecated": false, 1702 | "deprecationReason": null 1703 | }, 1704 | { 1705 | "name": "SUBSCRIPTION", 1706 | "description": "Location adjacent to a subscription operation.", 1707 | "isDeprecated": false, 1708 | "deprecationReason": null 1709 | }, 1710 | { 1711 | "name": "FIELD", 1712 | "description": "Location adjacent to a field.", 1713 | "isDeprecated": false, 1714 | "deprecationReason": null 1715 | }, 1716 | { 1717 | "name": "FRAGMENT_DEFINITION", 1718 | "description": "Location adjacent to a fragment definition.", 1719 | "isDeprecated": false, 1720 | "deprecationReason": null 1721 | }, 1722 | { 1723 | "name": "FRAGMENT_SPREAD", 1724 | "description": "Location adjacent to a fragment spread.", 1725 | "isDeprecated": false, 1726 | "deprecationReason": null 1727 | }, 1728 | { 1729 | "name": "INLINE_FRAGMENT", 1730 | "description": "Location adjacent to an inline fragment.", 1731 | "isDeprecated": false, 1732 | "deprecationReason": null 1733 | }, 1734 | { 1735 | "name": "SCHEMA", 1736 | "description": "Location adjacent to a schema definition.", 1737 | "isDeprecated": false, 1738 | "deprecationReason": null 1739 | }, 1740 | { 1741 | "name": "SCALAR", 1742 | "description": "Location adjacent to a scalar definition.", 1743 | "isDeprecated": false, 1744 | "deprecationReason": null 1745 | }, 1746 | { 1747 | "name": "OBJECT", 1748 | "description": "Location adjacent to an object type definition.", 1749 | "isDeprecated": false, 1750 | "deprecationReason": null 1751 | }, 1752 | { 1753 | "name": "FIELD_DEFINITION", 1754 | "description": "Location adjacent to a field definition.", 1755 | "isDeprecated": false, 1756 | "deprecationReason": null 1757 | }, 1758 | { 1759 | "name": "ARGUMENT_DEFINITION", 1760 | "description": "Location adjacent to an argument definition.", 1761 | "isDeprecated": false, 1762 | "deprecationReason": null 1763 | }, 1764 | { 1765 | "name": "INTERFACE", 1766 | "description": "Location adjacent to an interface definition.", 1767 | "isDeprecated": false, 1768 | "deprecationReason": null 1769 | }, 1770 | { 1771 | "name": "UNION", 1772 | "description": "Location adjacent to a union definition.", 1773 | "isDeprecated": false, 1774 | "deprecationReason": null 1775 | }, 1776 | { 1777 | "name": "ENUM", 1778 | "description": "Location adjacent to an enum definition.", 1779 | "isDeprecated": false, 1780 | "deprecationReason": null 1781 | }, 1782 | { 1783 | "name": "ENUM_VALUE", 1784 | "description": "Location adjacent to an enum value definition.", 1785 | "isDeprecated": false, 1786 | "deprecationReason": null 1787 | }, 1788 | { 1789 | "name": "INPUT_OBJECT", 1790 | "description": "Location adjacent to an input object type definition.", 1791 | "isDeprecated": false, 1792 | "deprecationReason": null 1793 | }, 1794 | { 1795 | "name": "INPUT_FIELD_DEFINITION", 1796 | "description": "Location adjacent to an input object field definition.", 1797 | "isDeprecated": false, 1798 | "deprecationReason": null 1799 | } 1800 | ], 1801 | "possibleTypes": null 1802 | } 1803 | ], 1804 | "directives": [ 1805 | { 1806 | "name": "include", 1807 | "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", 1808 | "locations": [ 1809 | "FIELD", 1810 | "FRAGMENT_SPREAD", 1811 | "INLINE_FRAGMENT" 1812 | ], 1813 | "args": [ 1814 | { 1815 | "name": "if", 1816 | "description": "Included when true.", 1817 | "type": { 1818 | "kind": "NON_NULL", 1819 | "name": null, 1820 | "ofType": { 1821 | "kind": "SCALAR", 1822 | "name": "Boolean", 1823 | "ofType": null 1824 | } 1825 | }, 1826 | "defaultValue": null 1827 | } 1828 | ] 1829 | }, 1830 | { 1831 | "name": "skip", 1832 | "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", 1833 | "locations": [ 1834 | "FIELD", 1835 | "FRAGMENT_SPREAD", 1836 | "INLINE_FRAGMENT" 1837 | ], 1838 | "args": [ 1839 | { 1840 | "name": "if", 1841 | "description": "Skipped when true.", 1842 | "type": { 1843 | "kind": "NON_NULL", 1844 | "name": null, 1845 | "ofType": { 1846 | "kind": "SCALAR", 1847 | "name": "Boolean", 1848 | "ofType": null 1849 | } 1850 | }, 1851 | "defaultValue": null 1852 | } 1853 | ] 1854 | }, 1855 | { 1856 | "name": "deprecated", 1857 | "description": "Marks an element of a GraphQL schema as no longer supported.", 1858 | "locations": [ 1859 | "FIELD_DEFINITION", 1860 | "ENUM_VALUE" 1861 | ], 1862 | "args": [ 1863 | { 1864 | "name": "reason", 1865 | "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).", 1866 | "type": { 1867 | "kind": "SCALAR", 1868 | "name": "String", 1869 | "ofType": null 1870 | }, 1871 | "defaultValue": "\"No longer supported\"" 1872 | } 1873 | ] 1874 | } 1875 | ] 1876 | } 1877 | } 1878 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-apollo-ssr-example", 3 | "version": "1.0.0", 4 | "description": "Example app with server-side rendering using react, react-router, react-apollo", 5 | "main": "dist/server.js", 6 | "scripts": { 7 | "build": "babel src --out-dir dist", 8 | "build:watch": "babel src --out-dir dist --watch --source-maps", 9 | "start": "node $NODE_DEBUG_OPTION dist/server.js", 10 | "start:watch": "nodemon $NODE_DEBUG_OPTION dist/server.js src --watch", 11 | "update-graphql-schema": "node ./scripts/updateGraphQLSchema", 12 | "lint": "eslint ./src", 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "author": "Vladimir Guguiev ", 16 | "license": "ISC", 17 | "dependencies": { 18 | "apollo-client": "^0.4.14", 19 | "express": "^4.14.0", 20 | "express-graphql": "^0.5.4", 21 | "graphql": "^0.7.0", 22 | "graphql-tag": "^0.1.13", 23 | "graphqlhub-schemas": "^0.1.0", 24 | "react": "^15.3.1", 25 | "react-apollo": "^0.5.2", 26 | "react-dom": "^15.3.1", 27 | "react-redux": "^4.4.5", 28 | "react-router": "^2.7.0", 29 | "react-router-redux": "^4.0.5", 30 | "redux": "^3.6.0" 31 | }, 32 | "devDependencies": { 33 | "babel-cli": "^6.8.0", 34 | "babel-core": "^6.8.0", 35 | "babel-loader": "^6.2.4", 36 | "babel-preset-es2015": "^6.6.0", 37 | "babel-preset-react": "^6.5.0", 38 | "colors": "^1.1.2", 39 | "eslint": "^3.6.1", 40 | "eslint-plugin-graphql": "^0.2.4", 41 | "nodemon": "^1.9.2", 42 | "webpack": "^1.13.0" 43 | }, 44 | "babel": { 45 | "presets": [ 46 | "es2015", 47 | "react" 48 | ] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /scripts/updateGraphQLSchema.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const colors = require('colors'); 5 | const { Reddit } = require('graphqlhub-schemas'); 6 | const { GraphQLSchema, graphql } = require('graphql'); 7 | const { introspectionQuery } = require('graphql/utilities/introspectionQuery'); 8 | 9 | const schemaFilePath = path.resolve('./graphql.schema.json'); 10 | const graphqlSchema = new GraphQLSchema({ 11 | query: Reddit.QueryObjectType 12 | }); 13 | 14 | console.log(colors.magenta('updating GraphQL schema...')); 15 | graphql( 16 | graphqlSchema, 17 | introspectionQuery 18 | ).then((response) => { 19 | fs.writeFileSync(schemaFilePath, JSON.stringify(response, null, ' ')); 20 | console.log(colors.green(`GraphQL schema updated and stored at ${schemaFilePath}`)); 21 | }).catch((err) => { 22 | console.error(colors.red(`There was an error updating GraphQL schema: ${err}`)); 23 | }); 24 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { graphql } from 'react-apollo'; 3 | import gql from 'graphql-tag'; 4 | 5 | class App extends React.Component { 6 | 7 | render() { 8 | const { loading, error, user } = this.props.data; 9 | 10 | if (loading) { 11 | return
Loading...
12 | } 13 | 14 | if (error) { 15 | return
{error}
; 16 | } 17 | 18 | return ( 19 |
20 |

Synth Music Subreddits followed by {user.username}

21 | {this.props.children} 22 |
23 | ); 24 | } 25 | 26 | } 27 | 28 | export default graphql( 29 | gql` 30 | query check { 31 | user(username: "wizardzloy") { 32 | username 33 | } 34 | } 35 | ` 36 | )(App); 37 | -------------------------------------------------------------------------------- /src/components/Details.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { graphql } from 'react-apollo'; 3 | import gql from 'graphql-tag'; 4 | 5 | class Details extends React.Component { 6 | 7 | render() { 8 | const { errors, loading, subreddit } = this.props.data; 9 | 10 | if (loading) { 11 | return
Loading...
; 12 | } 13 | 14 | return ( 15 |
16 |

{subreddit.title}

17 |
{subreddit.publicDescription}
18 |
19 | ); 20 | } 21 | 22 | } 23 | 24 | export default graphql( 25 | gql` 26 | query fetchSubreddit($id: String!) { 27 | subreddit(name: $id) { 28 | name 29 | title 30 | publicDescription 31 | } 32 | } 33 | `, 34 | { 35 | options: (ownProps) => ({ 36 | variables: { 37 | id: ownProps.params.id 38 | } 39 | }) 40 | } 41 | )(Details) 42 | -------------------------------------------------------------------------------- /src/components/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createFragment } from 'apollo-client'; 3 | import { graphql } from 'react-apollo'; 4 | import gql from 'graphql-tag'; 5 | 6 | const subredditInfoFragment = createFragment(gql` 7 | fragment subredditInfo on RedditSubreddit { 8 | name 9 | title 10 | publicDescription 11 | } 12 | `); 13 | 14 | const QUERY = gql` 15 | query { 16 | outrun: subreddit(name: "outrun") { 17 | ...subredditInfo 18 | } 19 | synthwave: subreddit(name: "synthwave") { 20 | ...subredditInfo 21 | } 22 | futuresynth: subreddit(name: "futuresynth") { 23 | ...subredditInfo 24 | } 25 | } 26 | `; 27 | 28 | class List extends React.Component { 29 | 30 | render() { 31 | const { errors, loading, outrun, synthwave, futuresynth } = this.props.data; 32 | 33 | if (loading) { 34 | return
Loading...
; 35 | } 36 | 37 | const listItems = [outrun, synthwave, futuresynth].map((subreddit) => ( 38 |
  • {subreddit.title}
  • 39 | )); 40 | 41 | return ( 42 | 45 | ); 46 | } 47 | 48 | } 49 | 50 | export default graphql(QUERY, { 51 | options: () => ({ 52 | fragments: subredditInfoFragment 53 | }) 54 | })(List); 55 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, IndexRoute } from 'react-router'; 3 | import App from './components/App'; 4 | import List from './components/List'; 5 | import Details from './components/Details'; 6 | 7 | export const getRoutes = () => { 8 | return ( 9 | 10 | 11 | 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const expressGraphql = require('express-graphql'); 3 | const React = require('react'); 4 | const { createMemoryHistory, match, RouterContext } = require('react-router'); 5 | const ApolloClient = require('apollo-client').default; 6 | const { ApolloProvider } = require('react-apollo'); 7 | const { renderToStringWithData } = require('react-apollo/server'); 8 | const { Reddit } = require('graphqlhub-schemas'); 9 | const { GraphQLSchema, graphql } = require('graphql'); 10 | const { print } = require('graphql-tag/printer'); 11 | 12 | const App = require('./components/App').default; 13 | const { getRoutes } = require('./routes'); 14 | 15 | const app = express(); 16 | 17 | // define GraphQL Schema 18 | const graphqlSchema = new GraphQLSchema({ 19 | query: Reddit.QueryObjectType 20 | }); 21 | 22 | // define ApolloClient's NetworkInterface, that will resolve the queries locally 23 | const localGraphQLNetworkInterface = { 24 | query: (graphqlRequest) => graphql( 25 | graphqlSchema, 26 | print(graphqlRequest.query), 27 | null, 28 | null, 29 | graphqlRequest.variables, 30 | graphqlRequest.operationName 31 | ) 32 | }; 33 | 34 | // initialize ApolloClient 35 | const apolloClient = new ApolloClient({ 36 | networkInterface: localGraphQLNetworkInterface 37 | }); 38 | 39 | app.use('/graphql', expressGraphql({ 40 | schema: graphqlSchema, 41 | graphiql: true 42 | })); 43 | 44 | // Server-Side-Rendering 45 | app.use((req, res) => { 46 | 47 | const history = createMemoryHistory(req.path); 48 | const routes = getRoutes(); 49 | 50 | // Find the correct route 51 | match({ history, routes, location: req.url }, (err, redirectLocation, renderProps) => { 52 | if (err) { 53 | return res.status(500).send(err.message); 54 | } 55 | 56 | if (redirectLocation) { 57 | return res.redirect(302, redirectLocation.pathname + redirectLocation.search); 58 | } 59 | 60 | if (!renderProps) { 61 | return res.status(404).send('Not found'); 62 | } 63 | 64 | const app = ( 65 | 66 | 67 | 68 | ); 69 | 70 | renderToStringWithData(app).then(({ markup, initialState }) => { 71 | res.send(markup); 72 | }); 73 | }); 74 | }); 75 | 76 | app.listen(8484, 'localhost', (err) => { 77 | if (err) { 78 | console.log(err); 79 | return; 80 | } 81 | console.log('listening on http://127.0.0.1:8484') 82 | }); 83 | --------------------------------------------------------------------------------