├── .gitignore ├── README.md ├── codegen.yml ├── graphql.schema.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── Products.tsx ├── ProductsGenerated.tsx ├── ProductsHooks.tsx ├── generated │ └── graphql.tsx ├── index.tsx ├── products.graphql ├── react-app-env.d.ts ├── serviceWorker.ts └── setupTests.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ts-react-grapqhl 2 | 3 | This demo shows how to consume data from a GraphQL Server with React and TypeScript, types are generated automatically from the GraphQL server usin [graphql-codegen](https://graphql-code-generator.com/) 🚀. 4 | 5 | ## Getting Started 6 | 7 | Open [this](https://codesandbox.io/s/50m91p16rp) CodeSandbox containing the GraphQL server and wait untill it's initialised (or use this url). **This server needs to run in order for the demo to work!**. 8 | 9 | Clone this repository and from the root directory run the command: 10 | 11 | ```sh 12 | yarn 13 | ``` 14 | 15 | This will install the dependencies needed to run this project. After the installation is complete you need to run the following command that creates the types from the schema on the GraphQL server, to make sure your TypeScript types match the schema: 16 | 17 | ```sh 18 | yarn generate 19 | ``` 20 | 21 | ### Run Project 22 | 23 | ```sh 24 | yarn 25 | yarn start 26 | ``` 27 | 28 | View in the browser at http://localhost:3000. Running in this environment provides hot reloading; just edit and save the file and the browser will automatically refresh. 29 | 30 | ## Use Your Own GraphQL Server 31 | In order to use your own GraphQL server for this demo, you'd need to change the definition of the GraphQL server in `codegen.yml` to: 32 | 33 | ``` 34 | overwrite: true 35 | schema: "[YOUR_GRAPHQL_SERVER]" 36 | documents: "src/**/*.graphql" 37 | generates: 38 | src/generated/graphql.tsx: 39 | plugins: 40 | - "typescript" 41 | - "typescript-operations" 42 | - "typescript-react-apollo" 43 | ./graphql.schema.json: 44 | plugins: 45 | - "introspection" 46 | 47 | ``` 48 | 49 | *If your GraphQL server will have a different schema than the example server, everything (in the UI) will break! But you should be getting pointers from TypeScript in the terminal to help you fix any compile errors.* 50 | -------------------------------------------------------------------------------- /codegen.yml: -------------------------------------------------------------------------------- 1 | overwrite: true 2 | schema: "https://50m91p16rp.sse.codesandbox.io/" 3 | documents: "src/**/*.graphql" 4 | generates: 5 | src/generated/graphql.tsx: 6 | plugins: 7 | - "typescript" 8 | - "typescript-operations" 9 | - "typescript-react-apollo" 10 | ./graphql.schema.json: 11 | plugins: 12 | - "introspection" 13 | -------------------------------------------------------------------------------- /graphql.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "__schema": { 3 | "queryType": { 4 | "name": "Query" 5 | }, 6 | "mutationType": { 7 | "name": "Mutation" 8 | }, 9 | "subscriptionType": null, 10 | "types": [ 11 | { 12 | "kind": "OBJECT", 13 | "name": "Query", 14 | "description": null, 15 | "fields": [ 16 | { 17 | "name": "products", 18 | "description": null, 19 | "args": [], 20 | "type": { 21 | "kind": "NON_NULL", 22 | "name": null, 23 | "ofType": { 24 | "kind": "LIST", 25 | "name": null, 26 | "ofType": { 27 | "kind": "NON_NULL", 28 | "name": null, 29 | "ofType": { 30 | "kind": "OBJECT", 31 | "name": "Product", 32 | "ofType": null 33 | } 34 | } 35 | } 36 | }, 37 | "isDeprecated": false, 38 | "deprecationReason": null 39 | }, 40 | { 41 | "name": "product", 42 | "description": null, 43 | "args": [ 44 | { 45 | "name": "id", 46 | "description": null, 47 | "type": { 48 | "kind": "NON_NULL", 49 | "name": null, 50 | "ofType": { 51 | "kind": "SCALAR", 52 | "name": "Int", 53 | "ofType": null 54 | } 55 | }, 56 | "defaultValue": null 57 | } 58 | ], 59 | "type": { 60 | "kind": "NON_NULL", 61 | "name": null, 62 | "ofType": { 63 | "kind": "OBJECT", 64 | "name": "Product", 65 | "ofType": null 66 | } 67 | }, 68 | "isDeprecated": false, 69 | "deprecationReason": null 70 | } 71 | ], 72 | "inputFields": null, 73 | "interfaces": [], 74 | "enumValues": null, 75 | "possibleTypes": null 76 | }, 77 | { 78 | "kind": "OBJECT", 79 | "name": "Product", 80 | "description": null, 81 | "fields": [ 82 | { 83 | "name": "id", 84 | "description": null, 85 | "args": [], 86 | "type": { 87 | "kind": "NON_NULL", 88 | "name": null, 89 | "ofType": { 90 | "kind": "SCALAR", 91 | "name": "Int", 92 | "ofType": null 93 | } 94 | }, 95 | "isDeprecated": false, 96 | "deprecationReason": null 97 | }, 98 | { 99 | "name": "title", 100 | "description": null, 101 | "args": [], 102 | "type": { 103 | "kind": "NON_NULL", 104 | "name": null, 105 | "ofType": { 106 | "kind": "SCALAR", 107 | "name": "String", 108 | "ofType": null 109 | } 110 | }, 111 | "isDeprecated": false, 112 | "deprecationReason": null 113 | }, 114 | { 115 | "name": "thumbnail", 116 | "description": null, 117 | "args": [], 118 | "type": { 119 | "kind": "NON_NULL", 120 | "name": null, 121 | "ofType": { 122 | "kind": "SCALAR", 123 | "name": "String", 124 | "ofType": null 125 | } 126 | }, 127 | "isDeprecated": false, 128 | "deprecationReason": null 129 | }, 130 | { 131 | "name": "thumbnailName", 132 | "description": null, 133 | "args": [], 134 | "type": { 135 | "kind": "NON_NULL", 136 | "name": null, 137 | "ofType": { 138 | "kind": "SCALAR", 139 | "name": "String", 140 | "ofType": null 141 | } 142 | }, 143 | "isDeprecated": true, 144 | "deprecationReason": "No longer supported" 145 | }, 146 | { 147 | "name": "reviews", 148 | "description": null, 149 | "args": [], 150 | "type": { 151 | "kind": "NON_NULL", 152 | "name": null, 153 | "ofType": { 154 | "kind": "OBJECT", 155 | "name": "Review", 156 | "ofType": null 157 | } 158 | }, 159 | "isDeprecated": false, 160 | "deprecationReason": null 161 | }, 162 | { 163 | "name": "offers", 164 | "description": null, 165 | "args": [], 166 | "type": { 167 | "kind": "NON_NULL", 168 | "name": null, 169 | "ofType": { 170 | "kind": "LIST", 171 | "name": null, 172 | "ofType": { 173 | "kind": "NON_NULL", 174 | "name": null, 175 | "ofType": { 176 | "kind": "OBJECT", 177 | "name": "Offer", 178 | "ofType": null 179 | } 180 | } 181 | } 182 | }, 183 | "isDeprecated": false, 184 | "deprecationReason": null 185 | } 186 | ], 187 | "inputFields": null, 188 | "interfaces": [], 189 | "enumValues": null, 190 | "possibleTypes": null 191 | }, 192 | { 193 | "kind": "SCALAR", 194 | "name": "Int", 195 | "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.", 196 | "fields": null, 197 | "inputFields": null, 198 | "interfaces": null, 199 | "enumValues": null, 200 | "possibleTypes": null 201 | }, 202 | { 203 | "kind": "SCALAR", 204 | "name": "String", 205 | "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.", 206 | "fields": null, 207 | "inputFields": null, 208 | "interfaces": null, 209 | "enumValues": null, 210 | "possibleTypes": null 211 | }, 212 | { 213 | "kind": "OBJECT", 214 | "name": "Review", 215 | "description": null, 216 | "fields": [ 217 | { 218 | "name": "productId", 219 | "description": null, 220 | "args": [], 221 | "type": { 222 | "kind": "NON_NULL", 223 | "name": null, 224 | "ofType": { 225 | "kind": "SCALAR", 226 | "name": "Int", 227 | "ofType": null 228 | } 229 | }, 230 | "isDeprecated": false, 231 | "deprecationReason": null 232 | }, 233 | { 234 | "name": "count", 235 | "description": null, 236 | "args": [], 237 | "type": { 238 | "kind": "SCALAR", 239 | "name": "Int", 240 | "ofType": null 241 | }, 242 | "isDeprecated": false, 243 | "deprecationReason": null 244 | }, 245 | { 246 | "name": "average", 247 | "description": null, 248 | "args": [], 249 | "type": { 250 | "kind": "SCALAR", 251 | "name": "Float", 252 | "ofType": null 253 | }, 254 | "isDeprecated": false, 255 | "deprecationReason": null 256 | } 257 | ], 258 | "inputFields": null, 259 | "interfaces": [], 260 | "enumValues": null, 261 | "possibleTypes": null 262 | }, 263 | { 264 | "kind": "SCALAR", 265 | "name": "Float", 266 | "description": "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).", 267 | "fields": null, 268 | "inputFields": null, 269 | "interfaces": null, 270 | "enumValues": null, 271 | "possibleTypes": null 272 | }, 273 | { 274 | "kind": "OBJECT", 275 | "name": "Offer", 276 | "description": null, 277 | "fields": [ 278 | { 279 | "name": "productId", 280 | "description": null, 281 | "args": [], 282 | "type": { 283 | "kind": "NON_NULL", 284 | "name": null, 285 | "ofType": { 286 | "kind": "SCALAR", 287 | "name": "Int", 288 | "ofType": null 289 | } 290 | }, 291 | "isDeprecated": false, 292 | "deprecationReason": null 293 | }, 294 | { 295 | "name": "id", 296 | "description": null, 297 | "args": [], 298 | "type": { 299 | "kind": "NON_NULL", 300 | "name": null, 301 | "ofType": { 302 | "kind": "SCALAR", 303 | "name": "Int", 304 | "ofType": null 305 | } 306 | }, 307 | "isDeprecated": false, 308 | "deprecationReason": null 309 | }, 310 | { 311 | "name": "reseller", 312 | "description": null, 313 | "args": [], 314 | "type": { 315 | "kind": "NON_NULL", 316 | "name": null, 317 | "ofType": { 318 | "kind": "SCALAR", 319 | "name": "String", 320 | "ofType": null 321 | } 322 | }, 323 | "isDeprecated": false, 324 | "deprecationReason": null 325 | }, 326 | { 327 | "name": "price", 328 | "description": null, 329 | "args": [], 330 | "type": { 331 | "kind": "NON_NULL", 332 | "name": null, 333 | "ofType": { 334 | "kind": "SCALAR", 335 | "name": "String", 336 | "ofType": null 337 | } 338 | }, 339 | "isDeprecated": false, 340 | "deprecationReason": null 341 | } 342 | ], 343 | "inputFields": null, 344 | "interfaces": [], 345 | "enumValues": null, 346 | "possibleTypes": null 347 | }, 348 | { 349 | "kind": "OBJECT", 350 | "name": "Mutation", 351 | "description": null, 352 | "fields": [ 353 | { 354 | "name": "addOffer", 355 | "description": null, 356 | "args": [ 357 | { 358 | "name": "input", 359 | "description": null, 360 | "type": { 361 | "kind": "NON_NULL", 362 | "name": null, 363 | "ofType": { 364 | "kind": "INPUT_OBJECT", 365 | "name": "OfferInput", 366 | "ofType": null 367 | } 368 | }, 369 | "defaultValue": null 370 | } 371 | ], 372 | "type": { 373 | "kind": "NON_NULL", 374 | "name": null, 375 | "ofType": { 376 | "kind": "OBJECT", 377 | "name": "Offer", 378 | "ofType": null 379 | } 380 | }, 381 | "isDeprecated": false, 382 | "deprecationReason": null 383 | }, 384 | { 385 | "name": "addProductOffer", 386 | "description": null, 387 | "args": [ 388 | { 389 | "name": "productId", 390 | "description": null, 391 | "type": { 392 | "kind": "NON_NULL", 393 | "name": null, 394 | "ofType": { 395 | "kind": "SCALAR", 396 | "name": "Int", 397 | "ofType": null 398 | } 399 | }, 400 | "defaultValue": null 401 | }, 402 | { 403 | "name": "reseller", 404 | "description": null, 405 | "type": { 406 | "kind": "NON_NULL", 407 | "name": null, 408 | "ofType": { 409 | "kind": "SCALAR", 410 | "name": "String", 411 | "ofType": null 412 | } 413 | }, 414 | "defaultValue": null 415 | }, 416 | { 417 | "name": "price", 418 | "description": null, 419 | "type": { 420 | "kind": "NON_NULL", 421 | "name": null, 422 | "ofType": { 423 | "kind": "SCALAR", 424 | "name": "String", 425 | "ofType": null 426 | } 427 | }, 428 | "defaultValue": null 429 | } 430 | ], 431 | "type": { 432 | "kind": "NON_NULL", 433 | "name": null, 434 | "ofType": { 435 | "kind": "OBJECT", 436 | "name": "Offer", 437 | "ofType": null 438 | } 439 | }, 440 | "isDeprecated": false, 441 | "deprecationReason": null 442 | } 443 | ], 444 | "inputFields": null, 445 | "interfaces": [], 446 | "enumValues": null, 447 | "possibleTypes": null 448 | }, 449 | { 450 | "kind": "INPUT_OBJECT", 451 | "name": "OfferInput", 452 | "description": null, 453 | "fields": null, 454 | "inputFields": [ 455 | { 456 | "name": "productId", 457 | "description": null, 458 | "type": { 459 | "kind": "NON_NULL", 460 | "name": null, 461 | "ofType": { 462 | "kind": "SCALAR", 463 | "name": "Int", 464 | "ofType": null 465 | } 466 | }, 467 | "defaultValue": null 468 | }, 469 | { 470 | "name": "reseller", 471 | "description": null, 472 | "type": { 473 | "kind": "NON_NULL", 474 | "name": null, 475 | "ofType": { 476 | "kind": "SCALAR", 477 | "name": "String", 478 | "ofType": null 479 | } 480 | }, 481 | "defaultValue": null 482 | }, 483 | { 484 | "name": "price", 485 | "description": null, 486 | "type": { 487 | "kind": "NON_NULL", 488 | "name": null, 489 | "ofType": { 490 | "kind": "SCALAR", 491 | "name": "String", 492 | "ofType": null 493 | } 494 | }, 495 | "defaultValue": null 496 | } 497 | ], 498 | "interfaces": null, 499 | "enumValues": null, 500 | "possibleTypes": null 501 | }, 502 | { 503 | "kind": "OBJECT", 504 | "name": "__Schema", 505 | "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.", 506 | "fields": [ 507 | { 508 | "name": "types", 509 | "description": "A list of all types supported by this server.", 510 | "args": [], 511 | "type": { 512 | "kind": "NON_NULL", 513 | "name": null, 514 | "ofType": { 515 | "kind": "LIST", 516 | "name": null, 517 | "ofType": { 518 | "kind": "NON_NULL", 519 | "name": null, 520 | "ofType": { 521 | "kind": "OBJECT", 522 | "name": "__Type", 523 | "ofType": null 524 | } 525 | } 526 | } 527 | }, 528 | "isDeprecated": false, 529 | "deprecationReason": null 530 | }, 531 | { 532 | "name": "queryType", 533 | "description": "The type that query operations will be rooted at.", 534 | "args": [], 535 | "type": { 536 | "kind": "NON_NULL", 537 | "name": null, 538 | "ofType": { 539 | "kind": "OBJECT", 540 | "name": "__Type", 541 | "ofType": null 542 | } 543 | }, 544 | "isDeprecated": false, 545 | "deprecationReason": null 546 | }, 547 | { 548 | "name": "mutationType", 549 | "description": "If this server supports mutation, the type that mutation operations will be rooted at.", 550 | "args": [], 551 | "type": { 552 | "kind": "OBJECT", 553 | "name": "__Type", 554 | "ofType": null 555 | }, 556 | "isDeprecated": false, 557 | "deprecationReason": null 558 | }, 559 | { 560 | "name": "subscriptionType", 561 | "description": "If this server support subscription, the type that subscription operations will be rooted at.", 562 | "args": [], 563 | "type": { 564 | "kind": "OBJECT", 565 | "name": "__Type", 566 | "ofType": null 567 | }, 568 | "isDeprecated": false, 569 | "deprecationReason": null 570 | }, 571 | { 572 | "name": "directives", 573 | "description": "A list of all directives supported by this server.", 574 | "args": [], 575 | "type": { 576 | "kind": "NON_NULL", 577 | "name": null, 578 | "ofType": { 579 | "kind": "LIST", 580 | "name": null, 581 | "ofType": { 582 | "kind": "NON_NULL", 583 | "name": null, 584 | "ofType": { 585 | "kind": "OBJECT", 586 | "name": "__Directive", 587 | "ofType": null 588 | } 589 | } 590 | } 591 | }, 592 | "isDeprecated": false, 593 | "deprecationReason": null 594 | } 595 | ], 596 | "inputFields": null, 597 | "interfaces": [], 598 | "enumValues": null, 599 | "possibleTypes": null 600 | }, 601 | { 602 | "kind": "OBJECT", 603 | "name": "__Type", 604 | "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.", 605 | "fields": [ 606 | { 607 | "name": "kind", 608 | "description": null, 609 | "args": [], 610 | "type": { 611 | "kind": "NON_NULL", 612 | "name": null, 613 | "ofType": { 614 | "kind": "ENUM", 615 | "name": "__TypeKind", 616 | "ofType": null 617 | } 618 | }, 619 | "isDeprecated": false, 620 | "deprecationReason": null 621 | }, 622 | { 623 | "name": "name", 624 | "description": null, 625 | "args": [], 626 | "type": { 627 | "kind": "SCALAR", 628 | "name": "String", 629 | "ofType": null 630 | }, 631 | "isDeprecated": false, 632 | "deprecationReason": null 633 | }, 634 | { 635 | "name": "description", 636 | "description": null, 637 | "args": [], 638 | "type": { 639 | "kind": "SCALAR", 640 | "name": "String", 641 | "ofType": null 642 | }, 643 | "isDeprecated": false, 644 | "deprecationReason": null 645 | }, 646 | { 647 | "name": "fields", 648 | "description": null, 649 | "args": [ 650 | { 651 | "name": "includeDeprecated", 652 | "description": null, 653 | "type": { 654 | "kind": "SCALAR", 655 | "name": "Boolean", 656 | "ofType": null 657 | }, 658 | "defaultValue": "false" 659 | } 660 | ], 661 | "type": { 662 | "kind": "LIST", 663 | "name": null, 664 | "ofType": { 665 | "kind": "NON_NULL", 666 | "name": null, 667 | "ofType": { 668 | "kind": "OBJECT", 669 | "name": "__Field", 670 | "ofType": null 671 | } 672 | } 673 | }, 674 | "isDeprecated": false, 675 | "deprecationReason": null 676 | }, 677 | { 678 | "name": "interfaces", 679 | "description": null, 680 | "args": [], 681 | "type": { 682 | "kind": "LIST", 683 | "name": null, 684 | "ofType": { 685 | "kind": "NON_NULL", 686 | "name": null, 687 | "ofType": { 688 | "kind": "OBJECT", 689 | "name": "__Type", 690 | "ofType": null 691 | } 692 | } 693 | }, 694 | "isDeprecated": false, 695 | "deprecationReason": null 696 | }, 697 | { 698 | "name": "possibleTypes", 699 | "description": null, 700 | "args": [], 701 | "type": { 702 | "kind": "LIST", 703 | "name": null, 704 | "ofType": { 705 | "kind": "NON_NULL", 706 | "name": null, 707 | "ofType": { 708 | "kind": "OBJECT", 709 | "name": "__Type", 710 | "ofType": null 711 | } 712 | } 713 | }, 714 | "isDeprecated": false, 715 | "deprecationReason": null 716 | }, 717 | { 718 | "name": "enumValues", 719 | "description": null, 720 | "args": [ 721 | { 722 | "name": "includeDeprecated", 723 | "description": null, 724 | "type": { 725 | "kind": "SCALAR", 726 | "name": "Boolean", 727 | "ofType": null 728 | }, 729 | "defaultValue": "false" 730 | } 731 | ], 732 | "type": { 733 | "kind": "LIST", 734 | "name": null, 735 | "ofType": { 736 | "kind": "NON_NULL", 737 | "name": null, 738 | "ofType": { 739 | "kind": "OBJECT", 740 | "name": "__EnumValue", 741 | "ofType": null 742 | } 743 | } 744 | }, 745 | "isDeprecated": false, 746 | "deprecationReason": null 747 | }, 748 | { 749 | "name": "inputFields", 750 | "description": null, 751 | "args": [], 752 | "type": { 753 | "kind": "LIST", 754 | "name": null, 755 | "ofType": { 756 | "kind": "NON_NULL", 757 | "name": null, 758 | "ofType": { 759 | "kind": "OBJECT", 760 | "name": "__InputValue", 761 | "ofType": null 762 | } 763 | } 764 | }, 765 | "isDeprecated": false, 766 | "deprecationReason": null 767 | }, 768 | { 769 | "name": "ofType", 770 | "description": null, 771 | "args": [], 772 | "type": { 773 | "kind": "OBJECT", 774 | "name": "__Type", 775 | "ofType": null 776 | }, 777 | "isDeprecated": false, 778 | "deprecationReason": null 779 | } 780 | ], 781 | "inputFields": null, 782 | "interfaces": [], 783 | "enumValues": null, 784 | "possibleTypes": null 785 | }, 786 | { 787 | "kind": "ENUM", 788 | "name": "__TypeKind", 789 | "description": "An enum describing what kind of type a given `__Type` is.", 790 | "fields": null, 791 | "inputFields": null, 792 | "interfaces": null, 793 | "enumValues": [ 794 | { 795 | "name": "SCALAR", 796 | "description": "Indicates this type is a scalar.", 797 | "isDeprecated": false, 798 | "deprecationReason": null 799 | }, 800 | { 801 | "name": "OBJECT", 802 | "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", 803 | "isDeprecated": false, 804 | "deprecationReason": null 805 | }, 806 | { 807 | "name": "INTERFACE", 808 | "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", 809 | "isDeprecated": false, 810 | "deprecationReason": null 811 | }, 812 | { 813 | "name": "UNION", 814 | "description": "Indicates this type is a union. `possibleTypes` is a valid field.", 815 | "isDeprecated": false, 816 | "deprecationReason": null 817 | }, 818 | { 819 | "name": "ENUM", 820 | "description": "Indicates this type is an enum. `enumValues` is a valid field.", 821 | "isDeprecated": false, 822 | "deprecationReason": null 823 | }, 824 | { 825 | "name": "INPUT_OBJECT", 826 | "description": "Indicates this type is an input object. `inputFields` is a valid field.", 827 | "isDeprecated": false, 828 | "deprecationReason": null 829 | }, 830 | { 831 | "name": "LIST", 832 | "description": "Indicates this type is a list. `ofType` is a valid field.", 833 | "isDeprecated": false, 834 | "deprecationReason": null 835 | }, 836 | { 837 | "name": "NON_NULL", 838 | "description": "Indicates this type is a non-null. `ofType` is a valid field.", 839 | "isDeprecated": false, 840 | "deprecationReason": null 841 | } 842 | ], 843 | "possibleTypes": null 844 | }, 845 | { 846 | "kind": "SCALAR", 847 | "name": "Boolean", 848 | "description": "The `Boolean` scalar type represents `true` or `false`.", 849 | "fields": null, 850 | "inputFields": null, 851 | "interfaces": null, 852 | "enumValues": null, 853 | "possibleTypes": null 854 | }, 855 | { 856 | "kind": "OBJECT", 857 | "name": "__Field", 858 | "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.", 859 | "fields": [ 860 | { 861 | "name": "name", 862 | "description": null, 863 | "args": [], 864 | "type": { 865 | "kind": "NON_NULL", 866 | "name": null, 867 | "ofType": { 868 | "kind": "SCALAR", 869 | "name": "String", 870 | "ofType": null 871 | } 872 | }, 873 | "isDeprecated": false, 874 | "deprecationReason": null 875 | }, 876 | { 877 | "name": "description", 878 | "description": null, 879 | "args": [], 880 | "type": { 881 | "kind": "SCALAR", 882 | "name": "String", 883 | "ofType": null 884 | }, 885 | "isDeprecated": false, 886 | "deprecationReason": null 887 | }, 888 | { 889 | "name": "args", 890 | "description": null, 891 | "args": [], 892 | "type": { 893 | "kind": "NON_NULL", 894 | "name": null, 895 | "ofType": { 896 | "kind": "LIST", 897 | "name": null, 898 | "ofType": { 899 | "kind": "NON_NULL", 900 | "name": null, 901 | "ofType": { 902 | "kind": "OBJECT", 903 | "name": "__InputValue", 904 | "ofType": null 905 | } 906 | } 907 | } 908 | }, 909 | "isDeprecated": false, 910 | "deprecationReason": null 911 | }, 912 | { 913 | "name": "type", 914 | "description": null, 915 | "args": [], 916 | "type": { 917 | "kind": "NON_NULL", 918 | "name": null, 919 | "ofType": { 920 | "kind": "OBJECT", 921 | "name": "__Type", 922 | "ofType": null 923 | } 924 | }, 925 | "isDeprecated": false, 926 | "deprecationReason": null 927 | }, 928 | { 929 | "name": "isDeprecated", 930 | "description": null, 931 | "args": [], 932 | "type": { 933 | "kind": "NON_NULL", 934 | "name": null, 935 | "ofType": { 936 | "kind": "SCALAR", 937 | "name": "Boolean", 938 | "ofType": null 939 | } 940 | }, 941 | "isDeprecated": false, 942 | "deprecationReason": null 943 | }, 944 | { 945 | "name": "deprecationReason", 946 | "description": null, 947 | "args": [], 948 | "type": { 949 | "kind": "SCALAR", 950 | "name": "String", 951 | "ofType": null 952 | }, 953 | "isDeprecated": false, 954 | "deprecationReason": null 955 | } 956 | ], 957 | "inputFields": null, 958 | "interfaces": [], 959 | "enumValues": null, 960 | "possibleTypes": null 961 | }, 962 | { 963 | "kind": "OBJECT", 964 | "name": "__InputValue", 965 | "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.", 966 | "fields": [ 967 | { 968 | "name": "name", 969 | "description": null, 970 | "args": [], 971 | "type": { 972 | "kind": "NON_NULL", 973 | "name": null, 974 | "ofType": { 975 | "kind": "SCALAR", 976 | "name": "String", 977 | "ofType": null 978 | } 979 | }, 980 | "isDeprecated": false, 981 | "deprecationReason": null 982 | }, 983 | { 984 | "name": "description", 985 | "description": null, 986 | "args": [], 987 | "type": { 988 | "kind": "SCALAR", 989 | "name": "String", 990 | "ofType": null 991 | }, 992 | "isDeprecated": false, 993 | "deprecationReason": null 994 | }, 995 | { 996 | "name": "type", 997 | "description": null, 998 | "args": [], 999 | "type": { 1000 | "kind": "NON_NULL", 1001 | "name": null, 1002 | "ofType": { 1003 | "kind": "OBJECT", 1004 | "name": "__Type", 1005 | "ofType": null 1006 | } 1007 | }, 1008 | "isDeprecated": false, 1009 | "deprecationReason": null 1010 | }, 1011 | { 1012 | "name": "defaultValue", 1013 | "description": "A GraphQL-formatted string representing the default value for this input value.", 1014 | "args": [], 1015 | "type": { 1016 | "kind": "SCALAR", 1017 | "name": "String", 1018 | "ofType": null 1019 | }, 1020 | "isDeprecated": false, 1021 | "deprecationReason": null 1022 | } 1023 | ], 1024 | "inputFields": null, 1025 | "interfaces": [], 1026 | "enumValues": null, 1027 | "possibleTypes": null 1028 | }, 1029 | { 1030 | "kind": "OBJECT", 1031 | "name": "__EnumValue", 1032 | "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.", 1033 | "fields": [ 1034 | { 1035 | "name": "name", 1036 | "description": null, 1037 | "args": [], 1038 | "type": { 1039 | "kind": "NON_NULL", 1040 | "name": null, 1041 | "ofType": { 1042 | "kind": "SCALAR", 1043 | "name": "String", 1044 | "ofType": null 1045 | } 1046 | }, 1047 | "isDeprecated": false, 1048 | "deprecationReason": null 1049 | }, 1050 | { 1051 | "name": "description", 1052 | "description": null, 1053 | "args": [], 1054 | "type": { 1055 | "kind": "SCALAR", 1056 | "name": "String", 1057 | "ofType": null 1058 | }, 1059 | "isDeprecated": false, 1060 | "deprecationReason": null 1061 | }, 1062 | { 1063 | "name": "isDeprecated", 1064 | "description": null, 1065 | "args": [], 1066 | "type": { 1067 | "kind": "NON_NULL", 1068 | "name": null, 1069 | "ofType": { 1070 | "kind": "SCALAR", 1071 | "name": "Boolean", 1072 | "ofType": null 1073 | } 1074 | }, 1075 | "isDeprecated": false, 1076 | "deprecationReason": null 1077 | }, 1078 | { 1079 | "name": "deprecationReason", 1080 | "description": null, 1081 | "args": [], 1082 | "type": { 1083 | "kind": "SCALAR", 1084 | "name": "String", 1085 | "ofType": null 1086 | }, 1087 | "isDeprecated": false, 1088 | "deprecationReason": null 1089 | } 1090 | ], 1091 | "inputFields": null, 1092 | "interfaces": [], 1093 | "enumValues": null, 1094 | "possibleTypes": null 1095 | }, 1096 | { 1097 | "kind": "OBJECT", 1098 | "name": "__Directive", 1099 | "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.", 1100 | "fields": [ 1101 | { 1102 | "name": "name", 1103 | "description": null, 1104 | "args": [], 1105 | "type": { 1106 | "kind": "NON_NULL", 1107 | "name": null, 1108 | "ofType": { 1109 | "kind": "SCALAR", 1110 | "name": "String", 1111 | "ofType": null 1112 | } 1113 | }, 1114 | "isDeprecated": false, 1115 | "deprecationReason": null 1116 | }, 1117 | { 1118 | "name": "description", 1119 | "description": null, 1120 | "args": [], 1121 | "type": { 1122 | "kind": "SCALAR", 1123 | "name": "String", 1124 | "ofType": null 1125 | }, 1126 | "isDeprecated": false, 1127 | "deprecationReason": null 1128 | }, 1129 | { 1130 | "name": "locations", 1131 | "description": null, 1132 | "args": [], 1133 | "type": { 1134 | "kind": "NON_NULL", 1135 | "name": null, 1136 | "ofType": { 1137 | "kind": "LIST", 1138 | "name": null, 1139 | "ofType": { 1140 | "kind": "NON_NULL", 1141 | "name": null, 1142 | "ofType": { 1143 | "kind": "ENUM", 1144 | "name": "__DirectiveLocation", 1145 | "ofType": null 1146 | } 1147 | } 1148 | } 1149 | }, 1150 | "isDeprecated": false, 1151 | "deprecationReason": null 1152 | }, 1153 | { 1154 | "name": "args", 1155 | "description": null, 1156 | "args": [], 1157 | "type": { 1158 | "kind": "NON_NULL", 1159 | "name": null, 1160 | "ofType": { 1161 | "kind": "LIST", 1162 | "name": null, 1163 | "ofType": { 1164 | "kind": "NON_NULL", 1165 | "name": null, 1166 | "ofType": { 1167 | "kind": "OBJECT", 1168 | "name": "__InputValue", 1169 | "ofType": null 1170 | } 1171 | } 1172 | } 1173 | }, 1174 | "isDeprecated": false, 1175 | "deprecationReason": null 1176 | } 1177 | ], 1178 | "inputFields": null, 1179 | "interfaces": [], 1180 | "enumValues": null, 1181 | "possibleTypes": null 1182 | }, 1183 | { 1184 | "kind": "ENUM", 1185 | "name": "__DirectiveLocation", 1186 | "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.", 1187 | "fields": null, 1188 | "inputFields": null, 1189 | "interfaces": null, 1190 | "enumValues": [ 1191 | { 1192 | "name": "QUERY", 1193 | "description": "Location adjacent to a query operation.", 1194 | "isDeprecated": false, 1195 | "deprecationReason": null 1196 | }, 1197 | { 1198 | "name": "MUTATION", 1199 | "description": "Location adjacent to a mutation operation.", 1200 | "isDeprecated": false, 1201 | "deprecationReason": null 1202 | }, 1203 | { 1204 | "name": "SUBSCRIPTION", 1205 | "description": "Location adjacent to a subscription operation.", 1206 | "isDeprecated": false, 1207 | "deprecationReason": null 1208 | }, 1209 | { 1210 | "name": "FIELD", 1211 | "description": "Location adjacent to a field.", 1212 | "isDeprecated": false, 1213 | "deprecationReason": null 1214 | }, 1215 | { 1216 | "name": "FRAGMENT_DEFINITION", 1217 | "description": "Location adjacent to a fragment definition.", 1218 | "isDeprecated": false, 1219 | "deprecationReason": null 1220 | }, 1221 | { 1222 | "name": "FRAGMENT_SPREAD", 1223 | "description": "Location adjacent to a fragment spread.", 1224 | "isDeprecated": false, 1225 | "deprecationReason": null 1226 | }, 1227 | { 1228 | "name": "INLINE_FRAGMENT", 1229 | "description": "Location adjacent to an inline fragment.", 1230 | "isDeprecated": false, 1231 | "deprecationReason": null 1232 | }, 1233 | { 1234 | "name": "VARIABLE_DEFINITION", 1235 | "description": "Location adjacent to a variable definition.", 1236 | "isDeprecated": false, 1237 | "deprecationReason": null 1238 | }, 1239 | { 1240 | "name": "SCHEMA", 1241 | "description": "Location adjacent to a schema definition.", 1242 | "isDeprecated": false, 1243 | "deprecationReason": null 1244 | }, 1245 | { 1246 | "name": "SCALAR", 1247 | "description": "Location adjacent to a scalar definition.", 1248 | "isDeprecated": false, 1249 | "deprecationReason": null 1250 | }, 1251 | { 1252 | "name": "OBJECT", 1253 | "description": "Location adjacent to an object type definition.", 1254 | "isDeprecated": false, 1255 | "deprecationReason": null 1256 | }, 1257 | { 1258 | "name": "FIELD_DEFINITION", 1259 | "description": "Location adjacent to a field definition.", 1260 | "isDeprecated": false, 1261 | "deprecationReason": null 1262 | }, 1263 | { 1264 | "name": "ARGUMENT_DEFINITION", 1265 | "description": "Location adjacent to an argument definition.", 1266 | "isDeprecated": false, 1267 | "deprecationReason": null 1268 | }, 1269 | { 1270 | "name": "INTERFACE", 1271 | "description": "Location adjacent to an interface definition.", 1272 | "isDeprecated": false, 1273 | "deprecationReason": null 1274 | }, 1275 | { 1276 | "name": "UNION", 1277 | "description": "Location adjacent to a union definition.", 1278 | "isDeprecated": false, 1279 | "deprecationReason": null 1280 | }, 1281 | { 1282 | "name": "ENUM", 1283 | "description": "Location adjacent to an enum definition.", 1284 | "isDeprecated": false, 1285 | "deprecationReason": null 1286 | }, 1287 | { 1288 | "name": "ENUM_VALUE", 1289 | "description": "Location adjacent to an enum value definition.", 1290 | "isDeprecated": false, 1291 | "deprecationReason": null 1292 | }, 1293 | { 1294 | "name": "INPUT_OBJECT", 1295 | "description": "Location adjacent to an input object type definition.", 1296 | "isDeprecated": false, 1297 | "deprecationReason": null 1298 | }, 1299 | { 1300 | "name": "INPUT_FIELD_DEFINITION", 1301 | "description": "Location adjacent to an input object field definition.", 1302 | "isDeprecated": false, 1303 | "deprecationReason": null 1304 | } 1305 | ], 1306 | "possibleTypes": null 1307 | }, 1308 | { 1309 | "kind": "ENUM", 1310 | "name": "CacheControlScope", 1311 | "description": null, 1312 | "fields": null, 1313 | "inputFields": null, 1314 | "interfaces": null, 1315 | "enumValues": [ 1316 | { 1317 | "name": "PUBLIC", 1318 | "description": null, 1319 | "isDeprecated": false, 1320 | "deprecationReason": null 1321 | }, 1322 | { 1323 | "name": "PRIVATE", 1324 | "description": null, 1325 | "isDeprecated": false, 1326 | "deprecationReason": null 1327 | } 1328 | ], 1329 | "possibleTypes": null 1330 | }, 1331 | { 1332 | "kind": "SCALAR", 1333 | "name": "Upload", 1334 | "description": "The `Upload` scalar type represents a file upload promise that resolves an\nobject containing `stream`, `filename`, `mimetype` and `encoding`.", 1335 | "fields": null, 1336 | "inputFields": null, 1337 | "interfaces": null, 1338 | "enumValues": null, 1339 | "possibleTypes": null 1340 | } 1341 | ], 1342 | "directives": [ 1343 | { 1344 | "name": "skip", 1345 | "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", 1346 | "locations": [ 1347 | "FIELD", 1348 | "FRAGMENT_SPREAD", 1349 | "INLINE_FRAGMENT" 1350 | ], 1351 | "args": [ 1352 | { 1353 | "name": "if", 1354 | "description": "Skipped when true.", 1355 | "type": { 1356 | "kind": "NON_NULL", 1357 | "name": null, 1358 | "ofType": { 1359 | "kind": "SCALAR", 1360 | "name": "Boolean", 1361 | "ofType": null 1362 | } 1363 | }, 1364 | "defaultValue": null 1365 | } 1366 | ] 1367 | }, 1368 | { 1369 | "name": "include", 1370 | "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", 1371 | "locations": [ 1372 | "FIELD", 1373 | "FRAGMENT_SPREAD", 1374 | "INLINE_FRAGMENT" 1375 | ], 1376 | "args": [ 1377 | { 1378 | "name": "if", 1379 | "description": "Included when true.", 1380 | "type": { 1381 | "kind": "NON_NULL", 1382 | "name": null, 1383 | "ofType": { 1384 | "kind": "SCALAR", 1385 | "name": "Boolean", 1386 | "ofType": null 1387 | } 1388 | }, 1389 | "defaultValue": null 1390 | } 1391 | ] 1392 | }, 1393 | { 1394 | "name": "deprecated", 1395 | "description": "Marks an element of a GraphQL schema as no longer supported.", 1396 | "locations": [ 1397 | "FIELD_DEFINITION", 1398 | "ENUM_VALUE" 1399 | ], 1400 | "args": [ 1401 | { 1402 | "name": "reason", 1403 | "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax (as specified by [CommonMark](https://commonmark.org/).", 1404 | "type": { 1405 | "kind": "SCALAR", 1406 | "name": "String", 1407 | "ofType": null 1408 | }, 1409 | "defaultValue": "\"No longer supported\"" 1410 | } 1411 | ] 1412 | } 1413 | ] 1414 | } 1415 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-graphql", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "@types/jest": "^24.0.0", 10 | "@types/node": "^12.0.0", 11 | "@types/react": "^16.9.15", 12 | "@types/react-dom": "^16.9.4", 13 | "apollo-boost": "^0.4.4", 14 | "react": "^16.12.0", 15 | "react-apollo": "^3.1.3", 16 | "react-dom": "^16.12.0", 17 | "react-scripts": "3.3.0", 18 | "typescript": "~3.7.2" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject", 25 | "generate": "graphql-codegen --config codegen.yml" 26 | }, 27 | "eslintConfig": { 28 | "extends": "react-app" 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "devDependencies": { 43 | "@graphql-codegen/cli": "^1.13.2", 44 | "@graphql-codegen/introspection": "^0.1.0", 45 | "@graphql-codegen/typescript": "^0.1.0", 46 | "@graphql-codegen/typescript-operations": "^0.1.0", 47 | "@graphql-codegen/typescript-react-apollo": "^0.1.0", 48 | "graphql": "^14.5.8" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/royderks/ts-react-graphql/6cabc2592fd47d13e6a029b233383d7f295727be/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/royderks/ts-react-graphql/6cabc2592fd47d13e6a029b233383d7f295727be/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/royderks/ts-react-graphql/6cabc2592fd47d13e6a029b233383d7f295727be/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /src/Products.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Query } from "react-apollo"; 3 | import gql from "graphql-tag"; 4 | 5 | interface GetProducts { 6 | products: any; 7 | data: { 8 | products: Product[]; 9 | }; 10 | } 11 | 12 | interface Product { 13 | id: string; 14 | title: string; 15 | offers: Offer[]; 16 | } 17 | 18 | interface Offer { 19 | id: string; 20 | reseller: string; 21 | price: string; 22 | } 23 | 24 | export const GET_PRODUCTS_QUERY = gql` 25 | query getProducts { 26 | products { 27 | id 28 | title 29 | offers { 30 | reseller 31 | price 32 | } 33 | } 34 | } 35 | `; 36 | 37 | export default () => ( 38 | query={GET_PRODUCTS_QUERY}> 39 | {({ loading, data }) => { 40 | if (loading) return

Loading...

; 41 | 42 | return ( 43 | 60 | ); 61 | }} 62 | 63 | ); 64 | -------------------------------------------------------------------------------- /src/ProductsGenerated.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GetProductsComponent } from './generated/graphql'; 3 | 4 | export default () => ( 5 | 6 | {({ loading, data }) => { 7 | if (loading) return

Loading...

; 8 | 9 | return ( 10 | 27 | ); 28 | }} 29 |
30 | ); 31 | -------------------------------------------------------------------------------- /src/ProductsHooks.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useQuery } from 'react-apollo'; 3 | import { GetProductsDocument, Product, Offer } from './generated/graphql'; 4 | 5 | export default () => { 6 | const { loading, data } = useQuery(GetProductsDocument); 7 | 8 | if (loading) return

Loading...

; 9 | 10 | return ( 11 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /src/generated/graphql.tsx: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | import * as React from 'react'; 3 | import * as ApolloReactCommon from '@apollo/react-common'; 4 | import * as ApolloReactComponents from '@apollo/react-components'; 5 | import * as ApolloReactHoc from '@apollo/react-hoc'; 6 | export type Maybe = T | null; 7 | export type Omit = Pick>; 8 | /** All built-in and custom scalars, mapped to their actual values */ 9 | export type Scalars = { 10 | ID: string; 11 | String: string; 12 | Boolean: boolean; 13 | Int: number; 14 | Float: number; 15 | /** 16 | * The `Upload` scalar type represents a file upload promise that resolves an 17 | * object containing `stream`, `filename`, `mimetype` and `encoding`. 18 | */ 19 | Upload: any; 20 | }; 21 | 22 | export enum CacheControlScope { 23 | Public = 'PUBLIC', 24 | Private = 'PRIVATE' 25 | } 26 | 27 | export type Mutation = { 28 | __typename?: 'Mutation'; 29 | addOffer: Offer; 30 | addProductOffer: Offer; 31 | }; 32 | 33 | 34 | export type MutationAddOfferArgs = { 35 | input: OfferInput; 36 | }; 37 | 38 | 39 | export type MutationAddProductOfferArgs = { 40 | productId: Scalars['Int']; 41 | reseller: Scalars['String']; 42 | price: Scalars['String']; 43 | }; 44 | 45 | export type Offer = { 46 | __typename?: 'Offer'; 47 | productId: Scalars['Int']; 48 | id: Scalars['Int']; 49 | reseller: Scalars['String']; 50 | price: Scalars['String']; 51 | }; 52 | 53 | export type OfferInput = { 54 | productId: Scalars['Int']; 55 | reseller: Scalars['String']; 56 | price: Scalars['String']; 57 | }; 58 | 59 | export type Product = { 60 | __typename?: 'Product'; 61 | id: Scalars['Int']; 62 | title: Scalars['String']; 63 | thumbnail: Scalars['String']; 64 | /** @deprecated Field no longer supported */ 65 | thumbnailName: Scalars['String']; 66 | reviews: Review; 67 | offers: Array; 68 | }; 69 | 70 | export type Query = { 71 | __typename?: 'Query'; 72 | products: Array; 73 | product: Product; 74 | }; 75 | 76 | 77 | export type QueryProductArgs = { 78 | id: Scalars['Int']; 79 | }; 80 | 81 | export type Review = { 82 | __typename?: 'Review'; 83 | productId: Scalars['Int']; 84 | count?: Maybe; 85 | average?: Maybe; 86 | }; 87 | 88 | 89 | export type GetProductsQueryVariables = {}; 90 | 91 | 92 | export type GetProductsQuery = ( 93 | { __typename?: 'Query' } 94 | & { products: Array<( 95 | { __typename?: 'Product' } 96 | & Pick 97 | & { offers: Array<( 98 | { __typename?: 'Offer' } 99 | & Pick 100 | )> } 101 | )> } 102 | ); 103 | 104 | 105 | export const GetProductsDocument = gql` 106 | query GetProducts { 107 | products { 108 | id 109 | title 110 | thumbnail 111 | offers { 112 | id 113 | reseller 114 | price 115 | } 116 | } 117 | } 118 | `; 119 | export type GetProductsComponentProps = Omit, 'query'>; 120 | 121 | export const GetProductsComponent = (props: GetProductsComponentProps) => ( 122 | query={GetProductsDocument} {...props} /> 123 | ); 124 | 125 | export type GetProductsProps = { 126 | [key in TDataName]: ApolloReactHoc.DataValue 127 | } & TChildProps; 128 | export function withGetProducts(operationOptions?: ApolloReactHoc.OperationOption< 129 | TProps, 130 | GetProductsQuery, 131 | GetProductsQueryVariables, 132 | GetProductsProps>) { 133 | return ApolloReactHoc.withQuery>(GetProductsDocument, { 134 | alias: 'getProducts', 135 | ...operationOptions 136 | }); 137 | }; 138 | export type GetProductsQueryResult = ApolloReactCommon.QueryResult; -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "react-dom"; 3 | import ApolloClient from "apollo-boost"; 4 | import { ApolloProvider } from "react-apollo"; 5 | 6 | import Products from "./Products"; 7 | // import ProductsGenerated from "./ProductsGenerated"; 8 | // import ProductsHooks from "./ProductsHooks"; 9 | 10 | const client = new ApolloClient({ 11 | uri: "https://50m91p16rp.sse.codesandbox.io/" 12 | }); 13 | 14 | render( 15 | 16 | 17 | {/* */} 18 | {/* */} 19 | , 20 | document.getElementById("root") 21 | ); 22 | -------------------------------------------------------------------------------- /src/products.graphql: -------------------------------------------------------------------------------- 1 | query GetProducts { 2 | products { 3 | id 4 | title 5 | thumbnail 6 | offers { 7 | id 8 | reseller 9 | price 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/serviceWorker.ts: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | type Config = { 24 | onSuccess?: (registration: ServiceWorkerRegistration) => void; 25 | onUpdate?: (registration: ServiceWorkerRegistration) => void; 26 | }; 27 | 28 | export function register(config?: Config) { 29 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 30 | // The URL constructor is available in all browsers that support SW. 31 | const publicUrl = new URL( 32 | process.env.PUBLIC_URL, 33 | window.location.href 34 | ); 35 | if (publicUrl.origin !== window.location.origin) { 36 | // Our service worker won't work if PUBLIC_URL is on a different origin 37 | // from what our page is served on. This might happen if a CDN is used to 38 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 39 | return; 40 | } 41 | 42 | window.addEventListener('load', () => { 43 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 44 | 45 | if (isLocalhost) { 46 | // This is running on localhost. Let's check if a service worker still exists or not. 47 | checkValidServiceWorker(swUrl, config); 48 | 49 | // Add some additional logging to localhost, pointing developers to the 50 | // service worker/PWA documentation. 51 | navigator.serviceWorker.ready.then(() => { 52 | console.log( 53 | 'This web app is being served cache-first by a service ' + 54 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 55 | ); 56 | }); 57 | } else { 58 | // Is not localhost. Just register service worker 59 | registerValidSW(swUrl, config); 60 | } 61 | }); 62 | } 63 | } 64 | 65 | function registerValidSW(swUrl: string, config?: Config) { 66 | navigator.serviceWorker 67 | .register(swUrl) 68 | .then(registration => { 69 | registration.onupdatefound = () => { 70 | const installingWorker = registration.installing; 71 | if (installingWorker == null) { 72 | return; 73 | } 74 | installingWorker.onstatechange = () => { 75 | if (installingWorker.state === 'installed') { 76 | if (navigator.serviceWorker.controller) { 77 | // At this point, the updated precached content has been fetched, 78 | // but the previous service worker will still serve the older 79 | // content until all client tabs are closed. 80 | console.log( 81 | 'New content is available and will be used when all ' + 82 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 83 | ); 84 | 85 | // Execute callback 86 | if (config && config.onUpdate) { 87 | config.onUpdate(registration); 88 | } 89 | } else { 90 | // At this point, everything has been precached. 91 | // It's the perfect time to display a 92 | // "Content is cached for offline use." message. 93 | console.log('Content is cached for offline use.'); 94 | 95 | // Execute callback 96 | if (config && config.onSuccess) { 97 | config.onSuccess(registration); 98 | } 99 | } 100 | } 101 | }; 102 | }; 103 | }) 104 | .catch(error => { 105 | console.error('Error during service worker registration:', error); 106 | }); 107 | } 108 | 109 | function checkValidServiceWorker(swUrl: string, config?: Config) { 110 | // Check if the service worker can be found. If it can't reload the page. 111 | fetch(swUrl, { 112 | headers: { 'Service-Worker': 'script' } 113 | }) 114 | .then(response => { 115 | // Ensure service worker exists, and that we really are getting a JS file. 116 | const contentType = response.headers.get('content-type'); 117 | if ( 118 | response.status === 404 || 119 | (contentType != null && contentType.indexOf('javascript') === -1) 120 | ) { 121 | // No service worker found. Probably a different app. Reload the page. 122 | navigator.serviceWorker.ready.then(registration => { 123 | registration.unregister().then(() => { 124 | window.location.reload(); 125 | }); 126 | }); 127 | } else { 128 | // Service worker found. Proceed as normal. 129 | registerValidSW(swUrl, config); 130 | } 131 | }) 132 | .catch(() => { 133 | console.log( 134 | 'No internet connection found. App is running in offline mode.' 135 | ); 136 | }); 137 | } 138 | 139 | export function unregister() { 140 | if ('serviceWorker' in navigator) { 141 | navigator.serviceWorker.ready.then(registration => { 142 | registration.unregister(); 143 | }); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | --------------------------------------------------------------------------------