├── .circleci
└── config.yml
├── .github
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .npmignore
├── .nvmrc
├── .prettierrc
├── .prettierrc.yml
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── codecov.yml
├── dangerfile.ts
├── designs
├── initial.md
└── meetings
│ └── 17-12-14.md
├── docs
├── README.md
└── rest.md
├── examples
├── advanced
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── SearchShow.js
│ │ ├── index.css
│ │ └── index.js
├── simple
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── Person.js
│ │ ├── index.css
│ │ └── index.js
└── typescript
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ └── index.html
│ ├── src
│ ├── Repo.tsx
│ ├── RepoSearch.tsx
│ ├── index.css
│ └── index.tsx
│ └── tsconfig.json
├── jest.config.js
├── package.json
├── renovate.json
├── rollup.config.js
├── schema.graphql
├── scripts
├── deploy.sh
├── docs_check.sh
├── docs_pull.sh
├── docs_push.sh
└── jest.js
├── src
├── __tests__
│ └── restLink.ts
├── index.ts
├── restLink.ts
└── utils
│ └── graphql.ts
├── tsconfig.json
└── tsconfig.tests.json
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | orbs:
4 | secops: apollo/circleci-secops-orb@2.0.1
5 |
6 | workflows:
7 | security-scans:
8 | jobs:
9 | - secops/gitleaks:
10 | context:
11 | - platform-docker-ro
12 | - github-orb
13 | - secops-oidc
14 | git-base-revision: <<#pipeline.git.base_revision>><><>
15 | git-revision: << pipeline.git.revision >>
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 | *.pid.lock
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # nyc test coverage
19 | .nyc_output
20 |
21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
22 | .grunt
23 |
24 | # node-waf configuration
25 | .lock-wscript
26 |
27 | # Compiled binary addons (http://nodejs.org/api/addons.html)
28 | build/Release
29 |
30 | # Dependency directories
31 | node_modules
32 | jspm_packages
33 |
34 | # Optional npm cache directory
35 | .npm
36 |
37 | # Optional eslint cache
38 | .eslintcache
39 |
40 | # Optional REPL history
41 | .node_repl_history
42 |
43 | # Output of 'npm pack'
44 | *.tgz
45 |
46 | # Yarn Integrity file
47 | .yarn-integrity
48 |
49 | # lock files
50 | yarn.lock
51 | package-lock.json
52 |
53 | # Compiled
54 | dist
55 | lib
56 |
57 | .idea
58 | .vscode
59 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | dist/tests/
2 | src/
3 | tests/
4 | .travis.yml
5 | tsconfig.json
6 | tslint.json
7 | typings.d.ts
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "singleQuote": true
4 | }
5 |
--------------------------------------------------------------------------------
/.prettierrc.yml:
--------------------------------------------------------------------------------
1 | singleQuote: true
2 | trailingComma: all
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '16'
4 |
5 | script:
6 | # - npm run danger (disable until it's fixed)
7 |
8 | # using jest --coverage also runs the tests so this will cut down CI time
9 | - npm run coverage
10 |
11 | # run coverage and file size checks
12 | - npm run coverage:upload || true #ignore failures
13 |
14 | # make sure files don't get too large
15 | - npm run filesize
16 |
17 | # make sure there are no type errors
18 | - npm run check-types
19 |
20 | # Allow Travis tests to run in containers.
21 | sudo: false
22 |
23 | notifications:
24 | email: false
25 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change log
2 |
3 | ## Versions
4 |
5 | ### v0.next
6 |
7 | ### v0.9.0
8 |
9 | * Feature: Pass context to typePatcher to make it possible to extract args from the URL for use when patching [#260](https://github.com/apollographql/apollo-link-rest/pull/260) [#261](https://github.com/apollographql/apollo-link-rest/pull/261)
10 | * Feature: Allow per-request field-name normalization for symmetry with de-normalization [#253](https://github.com/apollographql/apollo-link-rest/pull/253)
11 | * Improvement: Use globalThis instead of global [#293](https://github.com/apollographql/apollo-link-rest/pull/293)
12 | * Fix: fieldNameNormalizer mangling ArrayBuffer/Blob types [#247](https://github.com/apollographql/apollo-link-rest/pull/247)
13 | * Drop dependency on `graphql-anywhere`! [#301](https://github.com/apollographql/apollo-link-rest/pull/301)
14 |
15 | ### v0.8.0
16 |
17 | In beta for a unreasonably long time, this release has been stable (in-beta) for 2 years now!! It's about time we officially tag the stable version.
18 |
19 | The main breaking change is that you need to be running Apollo Client >= 3.
20 |
21 | A list of some specific relevant PRs:
22 |
23 | * [#241](https://github.com/apollographql/apollo-link-rest/pull/241)
24 | * [#239](https://github.com/apollographql/apollo-link-rest/pull/239)
25 | * [#228](https://github.com/apollographql/apollo-link-rest/pull/228)
26 | * [#209](https://github.com/apollographql/apollo-link-rest/pull/209)
27 |
28 | ### v0.7.3
29 |
30 | * Fix: Nested `@rest(…)` calls with nested `@export(as:…)` directives should keep their contexts distinct in order to work. [#204](https://github.com/apollographql/apollo-link-rest/pull/204)
31 |
32 | ### v0.7.2
33 |
34 | * Fix: FileList/File aren't available in react-native causing crashes. [#200](https://github.com/apollographql/apollo-link-rest/pull/200)
35 |
36 | ### v0.7.1
37 |
38 | * Fix: Duplicated Content Type Header [#188](https://github.com/apollographql/apollo-link-rest/pull/188)
39 | * Fix: FileList Support [#183](https://github.com/apollographql/apollo-link-rest/pull/183)
40 | * Fix: Default Empty object when creating headers [#178](https://github.com/apollographql/apollo-link-rest/pull/178)
41 | * Body-containing Queries [#173](https://github.com/apollographql/apollo-link-rest/pull/173)
42 |
43 | ### v0.7.0 - Breaking!
44 |
45 | #### Breaking changes around `responseTransformer!`
46 |
47 | In this [PR #165](https://github.com/apollographql/apollo-link-rest/pull/165), we realized that the `responseTransformer` feature added last release wasn't broad enough, `responseTransformer`s now receive the raw response stream instead of just the `json()`-promise.
48 |
49 | Code which relies on this feature will break, however the fix should be very simple:
50 |
51 | Either the responseTransformer function is made `async` and to `await response.json()`, *or* if this syntax is not available, the existing code needs to be wrapped in `response.json().then(data => {/* existing implementation */})`.
52 |
53 | #### Other Changes
54 |
55 | * Remove restriction that only allows request bodies to be built for Mutation operations. [#154](https://github.com/apollographql/apollo-link-rest/issues/154) & [#173](https://github.com/apollographql/apollo-link-rest/pull/173)
56 | * Fix code sandbox examples [#177](https://github.com/apollographql/apollo-link-rest/pull/177)
57 | * Bug-fix: default to empty headers instead of undefined for IE [#178](https://github.com/apollographql/apollo-link-rest/pull/178)
58 | * Various docs typo fixes
59 |
60 |
61 | ### v0.6.0
62 |
63 | * Feature: responseTransformers allow you to restructure & erase "wrapper" objects from your responses. [#146](https://github.com/apollographql/apollo-link-rest/pull/146)
64 | * Tweaks to config for prettier [#153](https://github.com/apollographql/apollo-link-rest/pull/153) & jest [#158](https://github.com/apollographql/apollo-link-rest/pull/158)
65 | * Tests for No-Content responses [#157](https://github.com/apollographql/apollo-link-rest/pull/157) & [#161](https://github.com/apollographql/apollo-link-rest/pull/161)
66 | * Bundle Size-Limit Increased [#162](https://github.com/apollographql/apollo-link-rest/pull/162)
67 | * Restructure Code for preferring `await` over Promise-chains [#159](https://github.com/apollographql/apollo-link-rest/pull/159)
68 |
69 | ### v0.5.0
70 |
71 | * Breaking Change: 404s now no longer throw an error! It's just null data! [#142](https://github.com/apollographql/apollo-link-rest/pull/142)
72 | * Default Accept header if no header configured for `Accept:` [#143](https://github.com/apollographql/apollo-link-rest/pull/143)
73 | * Improve/enable Support for Nested Queries from different apollo-links [#138](https://github.com/apollographql/apollo-link-rest/pull/138)
74 | * Remove Restriction that Mutation must not contain GET queries & vice versa [#140](https://github.com/apollographql/apollo-link-rest/issues/140)
75 |
76 | ### v0.4.3, v0.4.4
77 |
78 | * Expose an internal helper class (PathBuilder) for experimentation
79 |
80 | ### v0.4.2
81 |
82 | * Fix: Bad regexp causes path-replacements with multiple replacement slots to fail [#135](https://github.com/apollographql/apollo-link-rest/issues/135)
83 |
84 | ### v0.4.1
85 |
86 | * Fix: Correctly slicing nested key-paths [#134](https://github.com/apollographql/apollo-link-rest/issues/134)
87 | * Fix: Improve Types for ServerError [#133](https://github.com/apollographql/apollo-link-rest/pull/133)
88 |
89 | ### v0.4.0
90 |
91 | Breaking changes around `path`-variable replacement and `pathBuilder` (previously undocumented, [#132](https://github.com/apollographql/apollo-link-rest/issues/132)).
92 |
93 | * Breaking Change: paths now have a new style for variable replacement. (Old style is marked as deprecated, but will still work until v0.5.0). The migration should be easy in most cases `/path/:foo` => `/path/{args.foo}`
94 | * Breaking Change: `pathBuilder` signature changes to give them access to context & other data [#131](https://github.com/apollographql/apollo-link-rest/issues/131) and support optional Values [#130](https://github.com/apollographql/apollo-link-rest/issues/130)
95 | * Breaking Change: `bodyBuilder` signature changes to give them access to context & other data (for consistency with `pathBuilder`)
96 | * Fix/Feature: Queries that fetch Scalar values or Arrays of scalar values should now work! [#129](https://github.com/apollographql/apollo-link-rest/issues/129)
97 |
98 | ### v0.3.1
99 |
100 | * Fix: Fetch Response bodies can only be "read" once after which they throw "Already Read" -- this prevented us from properly speculatively parsing the error bodies outside of a test environment. [#122](https://github.com/apollographql/apollo-link-rest/issues/122)
101 | * Fix: Some browsers explode when you send null to them! [#121](https://github.com/apollographql/apollo-link-rest/issues/121#issuecomment-396049677)
102 |
103 | ### v0.3.0
104 |
105 | * Feature: Expose Headers from REST responses to the apollo-link chain via context. [#106](https://github.com/apollographql/apollo-link-rest/issues/106)
106 | * Feature: Expose HTTP-error REST responses as JSON if available! [#94](https://github.com/apollographql/apollo-link-rest/issues/94)
107 | * Feature: Add `@type(name: )` as an alternative, lighter-weight system for tagging Nested objects with \_\_typenames! [#72](https://github.com/apollographql/apollo-link-rest/issues/72)
108 | * Feature: Support "No-Content" responses! [#107](https://github.com/apollographql/apollo-link-rest/pull/107) [#111](https://github.com/apollographql/apollo-link-rest/pull/111)
109 | * Feature: Support serializing the body of REST calls with formats other than JSON [#103](https://github.com/apollographql/apollo-link-rest/pull/103)
110 | * Fix: Bundle-size / Tree Shaking issues [#99](https://github.com/apollographql/apollo-link-rest/issues/99)
111 | * Fix: Dependency tweaks to prevent multiple versions of deps [#105](https://github.com/apollographql/apollo-link-rest/issues/105)
112 | * Fix: GraphQL Nested Aliases - [#113](https://github.com/apollographql/apollo-link-rest/pull/113) [#7](https://github.com/apollographql/apollo-link-rest/issues/7)
113 |
114 | ### v0.2.4
115 |
116 | * Enable JSDoc comments for TypeScript!
117 | * Add in-repo copy of docs so PRs can make changes to docs in sync with implementation changes.
118 | * Fixed a bug with recursive type-patching around arrays.
119 | * Fixed a bug in default URI assignment! [#91](https://github.com/apollographql/apollo-link-rest/pull/91)
120 |
121 | ### v0.2.3
122 |
123 | * Fix: react-native: Android boolean responses being iterated by fieldNameNormalizer throws an error [#89](https://github.com/apollographql/apollo-link-rest/issues/89)
124 |
125 | ### v0.2.2
126 |
127 | * Fix: Queries with Arrays & omitted fields would treat those fields as required (and fail) [#85](https://github.com/apollographql/apollo-link-rest/issues/85)
128 |
129 | ### v0.2.1
130 |
131 | * Fix: Query throws an error when path-parameter is falsy [#82](https://github.com/apollographql/apollo-link-rest/issues/82)
132 | * Fix: Concurrency bug when multiple requests are in flight and both use `@export(as:)` [#81](https://github.com/apollographql/apollo-link-rest/issues/81)
133 | * Fix: fieldNameNormalizer/fieldNameDenormalizer should now be working! [#80](https://github.com/apollographql/apollo-link-rest/issues/80)
134 | * Improvement: Jest should now report code-coverage correctly for Unit Tests on PRs!
135 |
136 | ### v0.2.0
137 |
138 | * Feature: Support Handling Non-success HTTP Status Codes
139 | * Feature: Dynamic Paths & Query building using `pathBuilder`
140 | * Improvement: Sourcemaps should now be more TypeScript aware (via rollup changes) see [#76](https://github.com/apollographql/apollo-link-rest/issues/76) for more up-to-date info.
141 |
142 | ### v0.1.0
143 |
144 | Dropping the alpha tag, but keeping the pre-1.0 nature of this project!
145 |
146 | Recent changes:
147 |
148 | * Fix/Feature: Ability to have deeply nested responses that have \_\_typename set correctly. See `typePatcher` for more details
149 | * Fix: Real-world mutations need their bodies serialized. Mock-fetch allowed incorrect tests to be written. thanks @fabien0102
150 | * Feature: Bodies for mutations can be custom-built.
151 |
152 | ### v0.0.1-alpha.1
153 |
154 | ### v0.0.1-alpha.0
155 |
156 | * First publish
157 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Apollo Contributor Guide
2 |
3 | Excited about Apollo and want to make it better? We’re excited too!
4 |
5 | Apollo is a community of developers just like you, striving to create the best tools and libraries around GraphQL. We welcome anyone who wants to contribute or provide constructive feedback, no matter the age or level of experience. If you want to help but don't know where to start, let us know, and we'll find something for you.
6 |
7 | Oh, and if you haven't already, sign up for the [Apollo Slack](http://www.apollodata.com/#slack).
8 |
9 | Here are some ways to contribute to the project, from easiest to most difficult:
10 |
11 | * [Reporting bugs](#reporting-bugs)
12 | * [Improving the documentation](#improving-the-documentation)
13 | * [Responding to issues](#responding-to-issues)
14 | * [Small bug fixes](#small-bug-fixes)
15 | * [Suggesting features](#feature-requests)
16 | * [Big pull requests](#big-prs)
17 |
18 | ## Issues
19 |
20 | ### Reporting bugs
21 |
22 | If you encounter a bug, please file an issue on GitHub via the repository of the sub-project you think contains the bug. If an issue you have is already reported, please add additional information or add a 👍 reaction to indicate your agreement.
23 |
24 | While we will try to be as helpful as we can on any issue reported, please include the following to maximize the chances of a quick fix:
25 |
26 | 1. **Intended outcome:** What you were trying to accomplish when the bug occurred, and as much code as possible related to the source of the problem.
27 | 2. **Actual outcome:** A description of what actually happened, including a screenshot or copy-paste of any related error messages, logs, or other output that might be related. Places to look for information include your browser console, server console, and network logs. Please avoid non-specific phrases like “didn’t work” or “broke”.
28 | 3. **How to reproduce the issue:** Instructions for how the issue can be reproduced by a maintainer or contributor. Be as specific as possible, and only mention what is necessary to reproduce the bug. If possible, build a reproduction with our [error template](https://github.com/apollographql/react-apollo-error-template) to isolate the exact circumstances in which the bug occurs. Avoid speculation over what the cause might be.
29 |
30 | Creating a good reproduction really helps contributors investigate and resolve your issue quickly. In many cases, the act of creating a minimal reproduction illuminates that the source of the bug was somewhere outside the library in question, saving time and effort for everyone.
31 |
32 | ### Improving the documentation
33 |
34 | Improving the documentation, examples, and other open source content can be the easiest way to contribute to the library. If you see a piece of content that can be better, open a PR with an improvement, no matter how small! If you would like to suggest a big change or major rewrite, we’d love to hear your ideas but please open an issue for discussion before writing the PR.
35 |
36 | ### Responding to issues
37 |
38 | In addition to reporting issues, a great way to contribute to Apollo is to respond to other peoples' issues and try to identify the problem or help them work around it. If you’re interested in taking a more active role in this process, please go ahead and respond to issues. And don't forget to say "Hi" on Apollo Slack!
39 |
40 | ### Small bug fixes
41 |
42 | For a small bug fix change (less than 20 lines of code changed), feel free to open a pull request. We’ll try to merge it as fast as possible and ideally publish a new release on the same day. The only requirement is, make sure you also add a test that verifies the bug you are trying to fix.
43 |
44 | ### Suggesting features
45 |
46 | Most of the features in Apollo came from suggestions by you, the community! We welcome any ideas about how to make Apollo better for your use case. Unless there is overwhelming demand for a feature, it might not get implemented immediately, but please include as much information as possible that will help people have a discussion about your proposal:
47 |
48 | 1. **Use case:** What are you trying to accomplish, in specific terms? Often, there might already be a good way to do what you need and a new feature is unnecessary, but it’s hard to know without information about the specific use case.
49 | 2. **Could this be a plugin?** In many cases, a feature might be too niche to be included in the core of a library, and is better implemented as a companion package. If there isn’t a way to extend the library to do what you want, could we add additional plugin APIs? It’s important to make the case for why a feature should be part of the core functionality of the library.
50 | 3. **Is there a workaround?** Is this a more convenient way to do something that is already possible, or is there some blocker that makes a workaround unfeasible?
51 |
52 | Feature requests will be labeled as such, and we encourage using GitHub issues as a place to discuss new features and possible implementation designs. Please refrain from submitting a pull request to implement a proposed feature until there is consensus that it should be included. This way, you can avoid putting in work that can’t be merged in.
53 |
54 | Once there is a consensus on the need for a new feature, proceed as listed below under “Big PRs”.
55 |
56 | ## Big PRs
57 |
58 | This includes:
59 |
60 | - Big bug fixes
61 | - New features
62 |
63 | For significant changes to a repository, it’s important to settle on a design before starting on the implementation. This way, we can make sure that major improvements get the care and attention they deserve. Since big changes can be risky and might not always get merged, it’s good to reduce the amount of possible wasted effort by agreeing on an implementation design/plan first.
64 |
65 | 1. **Open an issue.** Open an issue about your bug or feature, as described above.
66 | 2. **Reach consensus.** Some contributors and community members should reach an agreement that this feature or bug is important, and that someone should work on implementing or fixing it.
67 | 3. **Agree on intended behavior.** On the issue, reach an agreement about the desired behavior. In the case of a bug fix, it should be clear what it means for the bug to be fixed, and in the case of a feature, it should be clear what it will be like for developers to use the new feature.
68 | 4. **Agree on implementation plan.** Write a plan for how this feature or bug fix should be implemented. What modules need to be added or rewritten? Should this be one pull request or multiple incremental improvements? Who is going to do each part?
69 | 5. **Submit PR.** In the case where multiple dependent patches need to be made to implement the change, only submit one at a time. Otherwise, the others might get stale while the first is reviewed and merged. Make sure to avoid “while we’re here” type changes - if something isn’t relevant to the improvement at hand, it should be in a separate PR; this especially includes code style changes of unrelated code.
70 | 6. **Review.** At least one core contributor should sign off on the change before it’s merged. Look at the “code review” section below to learn about factors are important in the code review. If you want to expedite the code being merged, try to review your own code first!
71 | 7. **Merge and release!**
72 |
73 | ### Code review guidelines
74 |
75 | It’s important that every piece of code in Apollo packages is reviewed by at least one core contributor familiar with that codebase. Here are some things we look for:
76 |
77 | 1. **Required CI checks pass.** This is a prerequisite for the review, and it is the PR author's responsibility. As long as the tests don’t pass, the PR won't get reviewed.
78 | 2. **Simplicity.** Is this the simplest way to achieve the intended goal? If there are too many files, redundant functions, or complex lines of code, suggest a simpler way to do the same thing. In particular, avoid implementing an overly general solution when a simple, small, and pragmatic fix will do.
79 | 3. **Testing.** Do the tests ensure this code won’t break when other stuff changes around it? When it does break, will the tests added help us identify which part of the library has the problem? Did we cover an appropriate set of edge cases? Look at the test coverage report if there is one. Are all significant code paths in the new code exercised at least once?
80 | 4. **No unnecessary or unrelated changes.** PRs shouldn’t come with random formatting changes, especially in unrelated parts of the code. If there is some refactoring that needs to be done, it should be in a separate PR from a bug fix or feature, if possible.
81 | 5. **Code has appropriate comments.** Code should be commented, or written in a clear “self-documenting” way.
82 | 6. **Idiomatic use of the language.** In TypeScript, make sure the typings are specific and correct. In ES2015, make sure to use imports rather than require and const instead of var, etc. Ideally a linter enforces a lot of this, but use your common sense and follow the style of the surrounding code.
83 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 - 2017 Meteor Development Group, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: REST Link
3 | ---
4 |
5 | ## ⚠️ This library is under active development ⚠️
6 |
7 | > The [Apollo Link Rest](https://github.com/apollographql/apollo-link-rest) library is maintained by Apollo community members and not an Apollo GraphQL maintained library. For information on progress check out [the issues](https://github.com/apollographql/apollo-link-rest/issues) or [the design](./designs/initial.md). We would love your help with writing docs, testing, anything! We would love for you, yes you, to be a part of the Apollo community!
8 |
9 | ## Purpose
10 | An Apollo Link to easily try out GraphQL without a full server. It can be used to prototype, with third-party services that don't have a GraphQL endpoint or in a transition from REST to GraphQL.
11 |
12 | ## Installation
13 |
14 | ```bash
15 | npm install apollo-link-rest @apollo/client graphql qs --save
16 | or
17 | yarn add apollo-link-rest @apollo/client graphql qs
18 | ```
19 |
20 | `@apollo/client`, `graphql`, and `qs` are peer dependencies needed by `apollo-link-rest`.
21 |
22 | ## Usage
23 |
24 | ### Basics
25 |
26 | ```js
27 | import { RestLink } from "apollo-link-rest";
28 | // Other necessary imports...
29 |
30 | // Create a RestLink for the REST API
31 | // If you are using multiple link types, restLink should go before httpLink,
32 | // as httpLink will swallow any calls that should be routed through rest!
33 | const restLink = new RestLink({
34 | uri: 'https://swapi.co/api/',
35 | });
36 |
37 | // Configure the ApolloClient with the default cache and RestLink
38 | const client = new ApolloClient({
39 | link: restLink,
40 | cache: new InMemoryCache(),
41 | });
42 |
43 | // A simple query to retrieve data about the first person
44 | const query = gql`
45 | query luke {
46 | person @rest(type: "Person", path: "people/1/") {
47 | name
48 | }
49 | }
50 | `;
51 |
52 | // Invoke the query and log the person's name
53 | client.query({ query }).then(response => {
54 | console.log(response.data.person.name);
55 | });
56 | ```
57 |
58 | [](https://codesandbox.io/s/github/apollographql/apollo-link-rest/tree/master/examples/simple)
59 |
60 | ### Apollo Client & React Apollo
61 |
62 | For an example of using REST Link with Apollo Client and React Apollo view this CodeSandbox:
63 |
64 | [](https://codesandbox.io/s/github/apollographql/apollo-link-rest/tree/master/examples/advanced)
65 |
66 | ### TypeScript
67 |
68 | For an example of using REST Link with Apollo Client, React Apollo and TypeScript view this CodeSandbox:
69 |
70 | [](https://codesandbox.io/s/github/apollographql/apollo-link-rest/tree/master/examples/typescript)
71 |
72 | ## Options
73 |
74 | REST Link takes an object with some options on it to customize the behavior of the link. The options you can pass are outlined below:
75 |
76 | - `uri`: the URI key is a string endpoint (optional when `endpoints` provides a default)
77 | - `endpoints`: root endpoint (uri) to apply paths to or a map of endpoints
78 | - `customFetch`: a custom `fetch` to handle REST calls
79 | - `headers`: an object representing values to be sent as headers on the request
80 | - `credentials`: a string representing the credentials policy you want for the fetch call
81 | - `fieldNameNormalizer`: function that takes the response field name and converts it into a GraphQL compliant name
82 |
83 | ## Context
84 |
85 | REST Link uses the `headers` field on the context to allow passing headers to the HTTP request. It also supports the `credentials` field for defining credentials policy.
86 |
87 | - `headers`: an object representing values to be sent as headers on the request
88 | - `credentials`: a string representing the credentials policy you want for the fetch call
89 |
90 | ## Documentation
91 |
92 | For a complete `apollo-link-rest` reference visit the documentation website at: https://www.apollographql.com/docs/link/links/rest.html
93 |
94 | ## Contributing
95 |
96 | This project uses TypeScript to bring static types to JavaScript and uses Jest for testing. To get started, clone the repo and run the following commands:
97 |
98 | ```bash
99 | npm install # or `yarn`
100 |
101 | npm test # or `yarn test` to run tests
102 | npm test -- --watch # run tests in watch mode
103 |
104 | npm run check-types # or `yarn check-types` to check TypeScript types
105 | ```
106 |
107 | To run the library locally in another project, you can do the following:
108 |
109 | ```bash
110 | npm link
111 |
112 | # in the project you want to run this in
113 | npm link apollo-link-rest
114 | ```
115 |
116 | ## Related Libraries
117 |
118 | - [JSON API Link](https://github.com/Rsullivan00/apollo-link-json-api/) provides
119 | tooling for using GraphQL with JSON API compliant APIs.
120 | - [apollo-type-patcher](https://github.com/mpgon/apollo-type-patcher) declarative type definitions for your REST API with zero dependencies.
121 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | parsers:
3 | javascript:
4 | enable_partials: yes
5 | status:
6 | project:
7 | default:
8 | target: "83%"
9 | patch:
10 | enabled: false
11 |
--------------------------------------------------------------------------------
/dangerfile.ts:
--------------------------------------------------------------------------------
1 | // Removed import
2 | import danger from 'danger';
3 | import { includes } from 'lodash';
4 | import * as fs from 'fs';
5 |
6 | // Setup
7 | const pr = danger.github.pr;
8 | const commits = danger.github.commits;
9 | const modified = danger.git.modified_files;
10 | const bodyAndTitle = (pr.body + pr.title).toLowerCase();
11 |
12 | // Custom modifiers for people submitting PRs to be able to say "skip this"
13 | const trivialPR = bodyAndTitle.includes('trivial');
14 | const acceptedNoTests = bodyAndTitle.includes('skip new tests');
15 |
16 | const typescriptOnly = (file: string) => includes(file, '.ts');
17 | const filesOnly = (file: string) =>
18 | fs.existsSync(file) && fs.lstatSync(file).isFile();
19 |
20 | // Custom subsets of known files
21 | const modifiedAppFiles = modified
22 | .filter(p => includes(p, 'src') || includes(p, '__tests__'))
23 | .filter(p => filesOnly(p) && typescriptOnly(p));
24 |
25 | // Takes a list of file paths, and converts it into clickable links
26 | const linkableFiles = paths => {
27 | const repoURL = danger.github.pr.head.repo.html_url;
28 | const ref = danger.github.pr.head.ref;
29 | const links = paths.map(path => {
30 | return createLink(`${repoURL}/blob/${ref}/${path}`, path);
31 | });
32 | return toSentence(links);
33 | };
34 |
35 | // ["1", "2", "3"] to "1, 2 and 3"
36 | const toSentence = (array: Array): string => {
37 | if (array.length === 1) {
38 | return array[0];
39 | }
40 | return array.slice(0, array.length - 1).join(', ') + ' and ' + array.pop();
41 | };
42 |
43 | // ("/href/thing", "name") to "name"
44 | const createLink = (href: string, text: string): string =>
45 | `${text}`;
46 |
47 | // Raise about missing code inside files
48 | const raiseIssueAboutPaths = (
49 | type: Function,
50 | paths: string[],
51 | codeToInclude: string,
52 | ) => {
53 | if (paths.length > 0) {
54 | const files = linkableFiles(paths);
55 | const strict = '' + codeToInclude + '';
56 | type(`Please ensure that ${strict} is enabled on: ${files}`);
57 | }
58 | };
59 |
60 | const authors = commits.map(x => x.author.login);
61 | const isBot = authors.some(
62 | x => ['greenkeeper', 'renovate', 'dependabot'].indexOf(x) > -1,
63 | );
64 |
65 | // Rules
66 | if (!isBot) {
67 | // make sure someone else reviews these changes
68 | // const someoneAssigned = danger.github.pr.assignee;
69 | // if (someoneAssigned === null) {
70 | // warn(
71 | // 'Please assign someone to merge this PR, and optionally include people who should review.'
72 | // );
73 | // }
74 |
75 | // When there are app-changes and it's not a PR marked as trivial, expect
76 | // there to be CHANGELOG changes.
77 | // const changelogChanges = modified.some(x => x.indexOf('CHANGELOG') > -1);
78 | // if (modifiedAppFiles.length > 0 && !trivialPR && !changelogChanges) {
79 | // fail('No CHANGELOG added.');
80 | // }
81 |
82 | // No PR is too small to warrant a paragraph or two of summary
83 | if (pr.body.length === 0) {
84 | fail('Please add a description to your PR.');
85 | }
86 |
87 | const hasAppChanges = modifiedAppFiles.length > 0;
88 |
89 | const testChanges = modifiedAppFiles.filter(filepath =>
90 | filepath.includes('test'),
91 | );
92 | const hasTestChanges = testChanges.length > 0;
93 |
94 | // Warn when there is a big PR
95 | const bigPRThreshold = 500;
96 | if (
97 | danger.github.pr.additions + danger.github.pr.deletions >
98 | bigPRThreshold
99 | ) {
100 | warn(':exclamation: Big PR');
101 | }
102 |
103 | // Warn if there are library changes, but not tests
104 | if (hasAppChanges && !hasTestChanges) {
105 | warn(
106 | "There are library changes, but not tests. That's OK as long as you're refactoring existing code",
107 | );
108 | }
109 |
110 | // Be careful of leaving testing shortcuts in the codebase
111 | const onlyTestFiles = testChanges.filter(x => {
112 | const content = fs.readFileSync(x).toString();
113 | return (
114 | content.includes('it.only') ||
115 | content.includes('describe.only') ||
116 | content.includes('fdescribe') ||
117 | content.includes('fit(')
118 | );
119 | });
120 | raiseIssueAboutPaths(fail, onlyTestFiles, 'an `only` was left in the test');
121 |
122 | // Politely ask for their name in the authors file
123 | message('Please add your name and email to the AUTHORS file (optional)');
124 | message(
125 | 'If this was a change that affects the external API, please update the docs and post a link to the PR in the discussion',
126 | );
127 | }
128 |
--------------------------------------------------------------------------------
/designs/initial.md:
--------------------------------------------------------------------------------
1 | ## `@rest` directive
2 |
3 | ### Arguments
4 |
5 | - path: path to rest endpoint. This could be a path or a full url. If a path, add to the endpoint given on link creation or from the context is concatenated to it.
6 | - params: a map of variables to url params
7 | - method: the HTTP method to send the request via (i.e GET, PUT, POST)
8 | - type: The GraphQL type this will return
9 | - endpoint: which endpoint (if using a map of endpoints) to use for the request
10 |
11 | ### Notes
12 |
13 | It's important that the rest directive could be used at any depth in a query, but once it is used, nothing nested in it can be graphql data, it has to be from the rest link or other resource (like a @client directive)
14 |
15 | ## `@export` directive
16 |
17 | The export directive re-exposes a field for use in a later (nested) query. An example use-case would be getting a list of users, and hitting a different endpoint to fetch more data using the exported field in the REST query args.
18 |
19 | ### Arguments
20 | - as: the string name to create this as a variable to be used down the selection set
21 |
22 | ### Notes
23 |
24 | These are the same semantics that will be supported on the server, but when used in a rest link you can use the exported variables for futher calls (i.e. waterfall requests from nested fields)
25 |
26 | ```js
27 | const QUERY = gql`
28 | query RestData($email: String!) {
29 | users @rest(path: '/users/email/:email', params: { email: $email }, method: 'GET', type: 'User') {
30 | id @export(as: "id")
31 | firstName
32 | lastName
33 | friends @rest(path: '/friends/:id', params: { id: $id }, type: '[User]') {
34 | firstName
35 | lastName
36 | }
37 | }
38 | }
39 | `;
40 | ```
41 |
42 |
43 | ## `createRestLink`
44 |
45 | ### Arguments
46 |
47 | - `fetch`: an optional implementation of `fetch` (see the http-link for api / warnings). Will use global if found
48 | - `fieldNameNormalizer`: a function that takes the response field name and turns into a GraphQL compliant name,for instance "MyFieldName:IsGreat" => myFieldNameIsGreat
49 | - `endpoint`: a root endpoint (uri) to apply paths to: i.e. http[s]://api.example.com/v1 or a map of endpoints with a key to choose in the directive
50 | - `batch`: a boolean to batch possible calls together (not initial version requirement!)
51 | - `headers`: an object representing values to be sent as headers on each request
52 | - `credentials`: a string representing the credentials policy you want for the fetch call
53 | - `fetchOptions`: any overrides of the fetch options argument to pass to the fetch call
54 |
55 | ### Notes
56 |
57 | It would be great to support batching of calls to /users if they are sent at the same time (i.e. dataloader) but definitely not something for the first round. Most of the tools around directives could be pulled from the apollo-link-state project to do the same work. For a mixed graphql + rest query, rest should be executed second as it may be nested and need the data (for instance with @export usage)
58 |
59 | ```js
60 | const link = createRestLink({
61 | endpoint: "https://api.example.com/v1",
62 | // endpoint: { "version1": "https://api.example.com/v1", "version2": "https://api.example.com/v2" },
63 | fetch: nodeFetch,
64 | fieldNameNormalizer: name => camelCase(name),
65 | // batch: false
66 | });
67 | ```
68 |
--------------------------------------------------------------------------------
/designs/meetings/17-12-14.md:
--------------------------------------------------------------------------------
1 | # Launch meeting
2 |
3 | ## Attendees
4 | - Peggy Rayzis (Apollo)
5 | - Victor Sabatier (Reactivic)
6 | - Frederic Barthelemy (TaskRabbit)
7 |
8 | ## Notes
9 | - Features left to complete
10 | - https://github.com/apollographql/apollo-link-rest/issues/3
11 | - Action: Close this issue and open up a new one for mixed directives since it's not a release blocker
12 | - Launch blockers
13 | - Documentation
14 | - Apollo Link docs & Apollo Client docs
15 | - Symlink Apollo Link docs page to README
16 | - Action: Peggy will open up issues w/ links to the process so Victor & Frederic can collaborate
17 | - https://github.com/apollographql/apollo-link-rest/issues/22
18 | - Example app
19 | - Next week, Victor & Frederic will collaborate on this
20 | - Start w/ a simple example: REST endpoint
21 | - Host on CodeSandbox
22 | - Git integration allows you to store the examples in the REST link repo
23 | - https://github.com/apollographql/apollo-link-rest/issues/23
24 | - Blog post
25 | - Frederic & Victor will divide up the sections
26 | - Target date: Send to Apollo team the week between Christmas & New Years
27 | - https://github.com/apollographql/apollo-link-rest/issues/21
28 | - Launch plan: Target date (first week of the new year)
29 | - Future (v1.0)
30 | - What's our (REST-response) typechecking story?
31 | - Start thinking of best practices (query components vs. HOC)
32 | - Query components are very tempting for people trying to migrate, but schema-stitching & GraphQL fragments are valuable GraphQL features that a naïve implementation of query-components would fail to support. We don't want people "learning" GraphQL using Apollo-link-rest, but avoiding these features. [Frederic will be prototyping some query-components with fragment support in the next few weeks]
33 |
34 | - Organisation
35 | - Who is willing to maintain/contribute on this project ?
36 | - Code Process
37 | - Continue iterating quickly, with PRs opened for major changes, but don't let one's self get blocked. -- It's fine if a PR is only open for a few hours, or it gets rapidly merged.
38 | - Do prioritize small PRs
39 | - All attendees & Apollo are happy to provide code-reviews & should be tagged in if more feedback is needed.
40 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # RestLink Docs
2 |
3 | The purpose of this directory is to have an in-repo copy of the `RestLink` documentation. It however truly lives in [`apollo-link`](https://www.apollographql.com/docs/link/links/rest.html)
4 |
5 | If you make changes in this directory, once the PR lands, please make sure it gets copied/merged into [apollographql/apollo-link](https://github.com/apollographql/apollo-link/blob/master/docs/source/links/rest.md)
6 |
7 | To help with this, we have these commands:
8 |
9 | ```shell
10 | # This will check to see if we're out of date
11 | $ yarn docs:check
12 |
13 | # Pushes documentation into a checkout of apollo-link for you to commit
14 | $ yarn docs:push
15 |
16 | # Pulls docs from apollo-link into your current checkout of apollo-link-rest
17 | $ yarn docs:pull
18 | ```
--------------------------------------------------------------------------------
/docs/rest.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: apollo-link-rest
3 | description: Call your REST APIs inside your GraphQL queries.
4 | ---
5 |
6 | Calling REST APIs from a GraphQL client opens the benefits of GraphQL for more people, whether:
7 |
8 | * You are in a front-end developer team that wants to try GraphQL without asking for the backend team to implement a GraphQL server.
9 | * You have no access to change the backend because it's an existing set of APIs, potentially managed by a 3rd party.
10 | * You have an existing codebase, but you're looking to evaluate whether GraphQL can work for your needs.
11 | * You have a large codebase, and the GraphQL migration is happening on the backend, but you want to use GraphQL *now* without waiting!
12 |
13 | With `apollo-link-rest`, you can now call your endpoints inside your GraphQL queries and have all your data managed by [`ApolloClient`](https://www.apollographql.com/react/basics/setup/#ApolloClient). `apollo-link-rest` is suitable for just dipping your toes in the water, or doing a full-steam ahead integration, and then later on migrating to a backend-driven GraphQL experience. `apollo-link-rest` combines well with other links such as [`apollo-link-context`][], [`apollo-link-state`](/links/state/), and others! _For complex back-ends, you may want to consider using [`apollo-server`](https://www.apollographql.com/docs/apollo-server/) which you can try out at [launchpad.graphql.com](https://launchpad.graphql.com/)_
14 |
15 | You can start using ApolloClient in your app today, let's see how!
16 |
17 | ## Quick start
18 |
19 | To get started, you need first to install @apollo/client:
20 |
21 | ```bash
22 | npm install --save @apollo/client
23 | ```
24 |
25 | Then it is time to install our link and its `peerDependencies`:
26 |
27 | ```bash
28 | npm install --save apollo-link-rest graphql qs
29 | ```
30 |
31 | After this, you are ready to setup your apollo client:
32 |
33 | ```js
34 | import { ApolloClient, InMemoryCache } from '@apollo/client';
35 | import { RestLink } from 'apollo-link-rest';
36 |
37 | // setup your `RestLink` with your endpoint
38 | const restLink = new RestLink({ uri: "https://swapi.co/api/" });
39 |
40 | // setup your client
41 | const client = new ApolloClient({
42 | link: restLink,
43 | cache: new InMemoryCache(),
44 | });
45 | ```
46 |
47 | From the [Apollo Client "Get started" docs](https://www.apollographql.com/docs/react/get-started#step-3-initialize-apolloclient):
48 |
49 | > `cache` is an instance of `InMemoryCache`, which Apollo Client uses to cache query results after fetching them.
50 |
51 | Now it is time to write our first query, for this you need to install the `graphql-tag` package:
52 |
53 | ```bash
54 | npm install --save graphql-tag
55 | ```
56 |
57 | Defining a query is straightforward:
58 |
59 | ```js
60 | const query = gql`
61 | query luke {
62 | person @rest(type: "Person", path: "people/1/") {
63 | name
64 | }
65 | }
66 | `;
67 | ```
68 |
69 | You can then fetch your data:
70 |
71 | ```js
72 | // Invoke the query and log the person's name
73 | client.query({ query }).then(response => {
74 | console.log(response.data.name);
75 | });
76 | ```
77 |
78 | ## Options
79 |
80 | Construction of `RestLink` takes an options object to customize the behavior of the link. The options you can pass are outlined below:
81 |
82 | * `uri: string`: the URI key is a string endpoint/domain for your requests to hit (_optional_ when `endpoints` provides a default)
83 | * `endpoints: /map-of-endpoints/`: _optional_ map of endpoints -- If you use this, you need to provide `endpoint` to the `@rest(...)` directives.
84 | * `customFetch?`: _optional_ a custom `fetch` to handle `REST` calls
85 | * `headers?: Headers`: _optional_ an object representing values to be sent as headers with all requests. [Documented here](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers)
86 | * `credentials?`: _optional_ a string representing the credentials policy the fetch call should operate with. [Documented here](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)
87 | * `fieldNameNormalizer?: /function/`: _optional_ function that takes the response field name and converts it into a GraphQL compliant name. -- This is useful if your `REST` API returns fields that aren't representable as GraphQL, or if you want to convert between `snake_case` field names in JSON to `camelCase` keyed fields.
88 | * `fieldNameDenormalizer?: /function/`: _optional_ function that takes a GraphQL-compliant field name and converts it back into an endpoint-specific name.
89 | * `typePatcher: /map-of-functions/`: _optional_ Structure to allow you to specify the `__typename` when you have nested objects in your REST response!
90 | * `defaultSerializer /function/`: _optional_ function that will be used by the `RestLink` as the default serializer when no `bodySerializer` is defined for a `@rest` call. The function will also be passed the current `Header` set, which can be updated before the request is sent to `fetch`. Default method uses `JSON.stringify` and sets the `Content-Type` to `application/json`.
91 | * `bodySerializers: /map-of-functions/`: _optional_ Structure to allow the definition of alternative serializers, which can then be specified by their key.
92 | * `responseTransformer?: /function/`: _optional_ Apollo expects a record response to return a root object, and a collection of records response to return an array of objects. Use this function to structure the response into the format Apollo expects if your response data is structured differently.
93 |
94 | ### Multiple endpoints
95 |
96 | If you want to be able to use multiple endpoints, you should create your link like so:
97 |
98 | ```js
99 | const link = new RestLink({ endpoints: { v1: 'api.com/v1', v2: 'api.com/v2' } });
100 | ```
101 |
102 | Then you need to specify in the rest directive the endpoint you want to use:
103 |
104 | ```js
105 | const postTitleQuery1 = gql`
106 | query postTitle {
107 | post @rest(type: "Post", path: "/post", endpoint: "v1") {
108 | id
109 | title
110 | }
111 | }
112 | `;
113 | const postTitleQuery2 = gql`
114 | query postTitle {
115 | post @rest(type: "[Tag]", path: "/tags", endpoint: "v2") {
116 | id
117 | tags
118 | }
119 | }
120 | `;
121 | ```
122 |
123 | If you have a default endpoint, you can create your link like so:
124 |
125 | ```js
126 | const link = new RestLink({
127 | endpoints: { github: 'github.com' },
128 | uri: 'api.com',
129 | });
130 | ```
131 |
132 | Then if you do not specify an endpoint in your query the default endpoint (the one you specify in the `uri` option.) will be used.
133 |
134 | ### Typename patching
135 |
136 | When sending such a query:
137 |
138 | ```graphql
139 | query MyQuery {
140 | planets @rest(type: "PlanetPayload", path: "planets/") {
141 | count
142 | next
143 | results {
144 | name
145 | }
146 | }
147 | }
148 | ```
149 |
150 | The outer response object (`data.planets`) gets its `__typename: "PlanetPayload"` from the [`@rest(...)` directive's `type` parameter](#rest-directive). You, however, need to have a way to set the typename of `PlanetPayload.results`.
151 |
152 | One way you can do this is by providing a `typePatcher`:
153 |
154 | ```typescript
155 | const restLink = new RestLink({
156 | uri: '/api',
157 | typePatcher: {
158 | PlanetPayload: (
159 | data: any,
160 | outerType: string,
161 | patchDeeper: RestLink.FunctionalTypePatcher,
162 | context: RestLink.TypePatcherContext
163 | ): any => {
164 | if (data.results != null) {
165 | data.results = data.results.map( planet => ({ __typename: "Planet", ...planet }));
166 | }
167 | return data;
168 | },
169 | /* … other nested type patchers … */
170 | },
171 | })
172 | ```
173 |
174 | If you have a very lightweight REST integration, you can use the `@type(name: ...)` directive.
175 |
176 | ```graphql
177 | query MyQuery {
178 | planets @rest(type: "PlanetPayload", path: "planets/") {
179 | count
180 | next
181 | results @type(name: "Planet") {
182 | name
183 | }
184 | }
185 | }
186 | ```
187 |
188 | This is appropriate if you have a small list of nested objects. The cost of this strategy is every query that deals with these objects needs to also include `@type(name: ...)` and this could be verbose and error prone.
189 |
190 | You can also use both of these approaches in tandem:
191 |
192 | ```graphql
193 | query MyQuery {
194 | planets @rest(type: "PlanetPayload", path: "planets/") {
195 | count
196 | next
197 | results @type(name: "Results") {
198 | name
199 | }
200 | typePatchedResults {
201 | name
202 | }
203 | }
204 | }
205 | ```
206 |
207 | ```typescript
208 | const restLink = new RestLink({
209 | uri: '/api',
210 | typePatcher: {
211 | PlanetPayload: (
212 | data: any,
213 | outerType: string,
214 | patchDeeper: RestLink.FunctionalTypePatcher,
215 | context: RestLink.TypePatcherContext
216 | ): any => {
217 | if (data.typePatchedResults != null) {
218 | data.typePatchedResults = data.typePatchedResults.map( planet => { __typename: "Planet", ...planet });
219 | }
220 | return data;
221 | },
222 | /* … other nested type patchers … */
223 | },
224 | })
225 | ```
226 |
227 | If you want to take advantage of Apollo's built-in client caching, you can provide a unique id field for your types if one is not present by default in the response from the REST API. This assumes you are using a default `id` field as your unique identifier key. If you have changed this using `dataIdFromObject`, you will need to use a different identifier in the code. [See More](https://www.apollographql.com/docs/react/v2.5/advanced/caching/#configuration)
228 |
229 | ```typescript
230 | const restLink = new RestLink({
231 | uri: '/api',
232 | typePatcher: {
233 | UserPayload: (
234 | data: any,
235 | outerType: string,
236 | patchDeeper: RestLink.FunctionalTypePatcher,
237 | context: RestLink.TypePatcherContext
238 | ): any => {
239 | if (!data.id) {
240 | const { directives } = context.resolverParams.info;
241 | const { path, endpoint } = directives.rest as RestLink.DirectiveOptions;
242 | // path = 23483/summary
243 | // endpoint = user/
244 | // So we are requesting /user/23483/summary
245 | data.id = path.substr(0, path.indexOf('/'));
246 | }
247 | return data;
248 | },
249 | /* … other nested type patchers … */
250 | },
251 | })
252 | ```
253 |
254 | #### Warning
255 |
256 | However, you should know that at the moment the `typePatcher` is not able to act on nested objects within annotated `@type` objects. For instance, `failingResults` will not be patched if you define it on the `typePatcher`.
257 |
258 | ```graphql
259 | query MyQuery {
260 | planets @rest(type: "PlanetPayload", path: "planets/") {
261 | count
262 | next
263 | results @type(name: "Planet") {
264 | name
265 | failingResults {
266 | name
267 | }
268 | }
269 | typePatchedResults {
270 | name
271 | }
272 | }
273 | }
274 | ```
275 |
276 | To make this work you should try to pick one strategy, and stick with it -- either all `typePatcher` or all `@type` directives.
277 |
278 | This is tracked in [Issue #112](https://github.com/apollographql/apollo-link-rest/issues/112)
279 |
280 | ### Response transforming
281 |
282 | By default, Apollo expects an object at the root for record requests, and an array of objects at the root for collection requests. For example, if fetching a user by ID (`/users/1`), the following response is expected.
283 |
284 | ```json
285 | {
286 | "id": 1,
287 | "name": "Apollo"
288 | }
289 | ```
290 |
291 | And when fetching for a list of users (`/users`), the following response is expected.
292 |
293 | ```json
294 | [
295 | {
296 | "id": 1,
297 | "name": "Apollo"
298 | },
299 | {
300 | "id": 2,
301 | "name": "Starman"
302 | }
303 | ]
304 | ```
305 |
306 | If the structure of your API responses differs than what Apollo expects, you can define a `responseTransformer` in the client. This function receives the response object as the 1st argument, and the current `typeName` as the 2nd argument. It should return a `Promise` as it will be responsible for reading the response stream by calling one of `json()`, `text()` etc.
307 |
308 | For instance if the record is not at the root level:
309 |
310 | ```json
311 | {
312 | "meta": {},
313 | "data": [
314 | {
315 | "id": 1,
316 | "name": "Apollo"
317 | },
318 | {
319 | "id": 2,
320 | "name": "Starman"
321 | }
322 | ]
323 | }
324 | ```
325 |
326 | The following transformer could be used to support it:
327 |
328 |
329 | ```js
330 | const link = new RestLink({
331 | uri: '/api',
332 | responseTransformer: async response => response.json().then(({data}) => data),
333 | });
334 | ```
335 |
336 | Plaintext, or XML, or otherwise-encoded responses can be handled by manually parsing and converting them to JSON (using the previously described format that Apollo expects):
337 |
338 | ```js
339 | const link = new RestLink({
340 | uri: '/xmlApi',
341 | responseTransformer: async response => response.text().then(text => parseXmlResponseToJson(text)),
342 | });
343 |
344 | ```
345 |
346 | ### Custom endpoint responses
347 |
348 | The client level `responseTransformer` applies for all responses, across all URIs and endpoints. If you need a custom `responseTransformer` per endpoint, you can define an object of options for that specific endpoint.
349 |
350 | ```js
351 | const link = new RestLink({
352 | endpoints: {
353 | v1: {
354 | uri: '/v1',
355 | responseTransformer: async response => response.data,
356 | },
357 | v2: {
358 | uri: '/v2',
359 | responseTransformer: async (response, typeName) => response[typeName],
360 | },
361 | },
362 | });
363 | ```
364 |
365 | > When using the object form, the `uri` field is required.
366 |
367 | ### Custom Fetch
368 |
369 | By default, Apollo uses the browsers `fetch` method to handle `REST` requests to your domain/endpoint. The `customFetch` option allows you to specify _your own_ request handler by defining a function that returns a `Promise` with a fetch-response-like object:
370 | ```js
371 | const link = new RestLink({
372 | endpoints: "/api",
373 | customFetch: (uri, options) => new Promise((resolve, reject) => {
374 | // Your own (asynchronous) request handler
375 | resolve(responseObject)
376 | }),
377 | });
378 | ```
379 |
380 | To resolve your GraphQL queries quickly, Apollo will issue requests to relevant endpoints as soon as possible. This is generally ok, but can lead to large numbers of `REST` requests to be fired at once; especially for deeply nested queries [(see `@export` directive)](#export-directive).
381 |
382 | > Some endpoints (like public APIs) might enforce _rate limits_, leading to failed responses and unresolved queries in such cases.
383 |
384 | By example, `customFetch` is a good place to manage your apps fetch operations. The following implementation makes sure to only issue 2 requests at a time (concurrency) while waiting at least 500ms until the next batch of requests is fired.
385 |
386 | ```js
387 | import pThrottle from "p-throttle";
388 |
389 | const link = new RestLink({
390 | endpoints: "/api",
391 | customFetch: pThrottle((uri, config) => {
392 | return fetch(uri, config);
393 | },
394 | 2, // Max. concurrent Requests
395 | 500 // Min. delay between calls
396 | ),
397 | });
398 | ```
399 | > Since Apollo issues `Promise` based requests, we can resolve them as we see fit. This example uses [`pThrottle`](https://github.com/sindresorhus/p-throttle); part of the popular [promise-fun](https://github.com/sindresorhus/promise-fun) collection.
400 |
401 | ### Complete options
402 |
403 | Here is one way you might customize `RestLink`:
404 |
405 | ```js
406 | import fetch from 'node-fetch';
407 | import * as camelCase from 'camelcase';
408 | import * as snake_case from 'snake-case';
409 |
410 | const link = new RestLink({
411 | endpoints: { github: 'github.com' },
412 | uri: 'api.com',
413 | customFetch: fetch,
414 | headers: {
415 | "Content-Type": "application/json"
416 | },
417 | credentials: "same-origin",
418 | fieldNameNormalizer: (key: string) => camelCase(key),
419 | fieldNameDenormalizer: (key: string) => snake_case(key),
420 | typePatcher: {
421 | Post: ()=> {
422 | bodySnippet...
423 | }
424 | },
425 | defaultSerializer: (data: any, headers: Headers) => {
426 | const formData = new FormData();
427 | for (let key in body) {
428 | formData.append(key, body[key]);
429 | }
430 | headers.set("Content-Type", "x-www-form-encoded")
431 | return {body: formData, headers};
432 | }
433 | });
434 | ```
435 |
436 | ## Link Context
437 |
438 | `RestLink` has an [interface `LinkChainContext`](https://github.com/apollographql/apollo-link-rest/blob/1824da47d5db77a2259f770d9c9dd60054c4bb1c/src/restLink.ts#L557-L570) which it uses as the structure of things that it will look for in the `context`, as it decides how to fulfill a specific `RestLink` request. (Please see the [`apollo-link-context`][] page for a discussion of why you might want this).
439 |
440 | * `credentials?: RequestCredentials`: overrides the `RestLink`-level setting for `credentials`. [Values documented here](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers)
441 | * `headers?: Headers`: Additional headers provided in this `context-link` [Values documented here](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers)
442 | * `headersToOverride?: string[]` If you provide this array, we will merge the headers you provide in this link, by replacing any matching headers that exist in the root `RestLink` configuration. Alternatively you can use `headersMergePolicy` for more fine-grained customization of the merging behavior.
443 | * `headersMergePolicy?: RestLink.HeadersMergePolicy`: This is a function that decide how the headers returned in this `contextLink` are merged with headers defined at the `RestLink`-level. If you don't provide this, the headers will be simply appended. To use this option, you can provide your own function that decides how to process the headers. [Code references](https://github.com/apollographql/apollo-link-rest/blob/8e57cabb5344209d9cfa391c1614fe8880efa5d9/src/restLink.ts#L462-L510)
444 | * `restResponses?: Response[]`: This will be populated after the operation has completed with the [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response) of every REST url fetched during the operation. This can be useful if you need to access the response headers to grab an authorization token for example.
445 |
446 | ### Example
447 |
448 | `RestLink` uses the `headers` field on the [`apollo-link-context`][] so you can compose other links that provide additional & dynamic headers to a given query.
449 |
450 | [`apollo-link-context`]: /links/context/
451 |
452 | Here is one way to add request `headers` to the context and retrieve the response headers of the operation:
453 |
454 | ```js
455 | const authRestLink = new ApolloLink((operation, forward) => {
456 | operation.setContext(({headers}) => {
457 | const token = localStorage.getItem("token");
458 | return {
459 | headers: {
460 | ...headers,
461 | Accept: "application/json",
462 | Authorization: token
463 | }
464 | };
465 | });
466 | return forward(operation).map(result => {
467 | const { restResponses } = operation.getContext();
468 | const authTokenResponse = restResponses.find(res => res.headers.has("Authorization"));
469 | // You might also filter on res.url to find the response of a specific API call
470 | if (authTokenResponse) {
471 | localStorage.setItem("token", authTokenResponse.headers.get("Authorization"));
472 | }
473 | return result;
474 | });
475 | });
476 |
477 | const restLink = new RestLink({ uri: "uri" });
478 |
479 | const client = new ApolloClient({
480 | link: ApolloLink.from([authRestLink, restLink]),
481 | cache: new InMemoryCache(),
482 | });
483 | ```
484 |
485 | ## Link order
486 |
487 | If you are using multiple link types, `restLink` should go before `httpLink`, as `httpLink` will swallow any calls that should be routed through `apollo-link-rest`!
488 |
489 | For example:
490 |
491 | ```js
492 | const httpLink = createHttpLink({ uri: "server.com/graphql" });
493 | const restLink = new RestLink({ uri: "api.server.com" });
494 |
495 | const client = new ApolloClient({
496 | link: ApolloLink.from([authLink, restLink, errorLink, retryLink, httpLink]),
497 | // Note: httpLink is terminating so must be last, while retry & error wrap the links to their right
498 | // state & context links should happen before (to the left of) restLink.
499 | cache: new InMemoryCache()
500 | });
501 | ```
502 |
503 | _Note: you should also consider this if you're using [`apollo-link-context`](#link-context) to set `Headers`, you need that link to be before `restLink` as well._
504 |
505 | ## @rest directive
506 |
507 | This is where you setup the endpoint you want to fetch.
508 | The rest directive could be used at any depth in a query, but once it is used, nothing nested in it can be GraphQL data, it has to be from the `RestLink` or other resource (like the [`@client` directive](/links/state/))
509 |
510 | ### Arguments
511 |
512 | An `@rest(…)` directive takes two required and several optional arguments:
513 |
514 | * `type: string`: The GraphQL type this will return
515 | * `path: string`: uri-path to the REST API. This could be a path or a full url. If a path, the endpoint given on link creation or from the context is concatenated with it to produce a full `URI`. See also: `pathBuilder`
516 | * _optional_ `method?: "GET" | "PUT" | "POST" | "DELETE"`: the HTTP method to send the request via (i.e GET, PUT, POST)
517 | * _optional_ `endpoint?: string` key to use when looking up the endpoint in the (optional) `endpoints` table if provided to RestLink at creation time.
518 | * _optional_ `pathBuilder?: /function/`: If provided, this function gets to control what path is produced for this request.
519 | * _optional_ `bodyKey?: string = "input"`: This is the name of the `variable` to use when looking to build a REST request-body for a `PUT` or `POST` request. It defaults to `input` if not supplied.
520 | * _optional_ `bodyBuilder?: /function/`: If provided, this is the name a `function` that you provided to `variables`, that is called when a request-body needs to be built. This lets you combine arguments or encode the body in some format other than JSON.
521 | * _optional_ `bodySerializer?: /string | function/`: string key to look up a function in `bodySerializers` or a custom serialization function for the body/headers of this request before it is passed to the fetch call. Defaults to `JSON.stringify` and setting `Content-Type: application-json`.
522 |
523 | ### Variables
524 |
525 | You can use query `variables` inside nested queries, or in the the path argument of your directive:
526 |
527 | ```graphql
528 | query postTitle {
529 | post(id: "1") @rest(type: "Post", path: "/post/{args.id}") {
530 | id
531 | title
532 | }
533 | }
534 | ```
535 |
536 | *Warning*: Variables in the main path will not automatically have `encodeURIComponent` called on them
537 |
538 | Additionally, you can also control the query-string:
539 |
540 | ```graphql
541 | query postTitle {
542 | postSearch(query: "some key words", page_size: 5)
543 | @rest(type: "Post", path: "/search?{args}&{context.language}") {
544 | id
545 | title
546 | }
547 | }
548 | ```
549 |
550 | Things to note:
551 |
552 | 1. This will be converted into `/search?query=some%20key%20words&page_size=5&lang=en`
553 | 2. The `context.language / lang=en` is extracting an object from the Apollo Context, that was added via an `apollo-link-context` Link.
554 | 3. The query string arguments are assembled by npm:qs and have `encodeURIComponent` called on them.
555 |
556 | The available variable sources are:
557 |
558 | * `args` these are the things passed directly to this field parameters. In the above example `postSearch` had `query` and `page_size` in args.
559 | * `exportVariables` these are the things in the parent context that were tagged as `@export(as: ...)`
560 | * `context` these are the apollo-context, so you can have globals set up via `apollo-link-context`
561 | * `@rest` these include any other parameters you pass to the `@rest()` directive. This is probably more useful when working with `pathBuilder`, documented below.
562 |
563 | #### `pathBuilder`
564 |
565 | If the variable-replacement options described above aren't enough, you can provide a `pathBuilder` to your query. This will be called to dynamically construct the path. This is considered an advanced feature, and is documented in the source -- it also should be considered syntactically unstable, and we're looking for feedback!
566 |
567 | #### `bodyKey` / `bodyBuilder`
568 |
569 | When making a `POST` or `PUT` HTTP request, you often need to provide a request body. By [convention](https://graphql.org/graphql-js/mutations-and-input-types/), GraphQL recommends you name your input-types as `input`, so by default that's where we'll look to find a JSON object for your body.
570 |
571 | ##### `bodyKey`
572 |
573 | If you need/want to name it something different, you can pass `bodyKey`, and we'll look at that variable instead.
574 |
575 | In this example the publish API accepts a body in the variable `body` instead of input:
576 |
577 | ```graphql
578 | mutation publishPost(
579 | $someApiWithACustomBodyKey: PublishablePostInput!
580 | ) {
581 | publishedPost: publish(input: "Foo", body: $someApiWithACustomBodyKey)
582 | @rest(
583 | type: "Post"
584 | path: "/posts/{args.input}/new"
585 | method: "POST"
586 | bodyKey: "body"
587 | ) {
588 | id
589 | title
590 | }
591 | }
592 | ```
593 |
594 | [Unit Test](https://github.com/apollographql/apollo-link-rest/blob/c9d81ae308e5f61b5ae992061de7abc6cb2f78e0/src/__tests__/restLink.ts#L1803-L1846)
595 |
596 | ##### `bodyBuilder`
597 |
598 | If you need to structure your data differently, or you need to custom encode your body (say as form-encoded), you can instead provide `bodyBuilder`
599 |
600 | ```graphql
601 | mutation encryptedPost(
602 | $input: PublishablePostInput!
603 | $encryptor: any
604 | ) {
605 | publishedPost: publish(input: $input)
606 | @rest(
607 | type: "Post"
608 | path: "/posts/new"
609 | method: "POST"
610 | bodyBuilder: $encryptor
611 | ) {
612 | id
613 | title
614 | }
615 | }
616 | ```
617 |
618 | [Unit Test](https://github.com/apollographql/apollo-link-rest/blob/c9d81ae308e5f61b5ae992061de7abc6cb2f78e0/src/__tests__/restLink.ts#L1847-L1904)
619 |
620 | ##### `bodySerializer`
621 |
622 | If you need to serialize your data differently (say as form-encoded), you can provide a `bodySerializer` instead of relying on the default JSON serialization.
623 | `bodySerializer` can be either a function of the form `(data: any, headers: Headers) => {body: any, headers: Headers}` or a string key. When using the string key
624 | `RestLink` will instead use the corresponding serializer from the `bodySerializers` object that can optionally be passed in during initialization.
625 |
626 | ```graphql
627 | mutation encryptedForm(
628 | $input: PublishablePostInput!,
629 | $formSerializer: any
630 | ) {
631 | publishedPost: publish(input: $input)
632 | @rest(
633 | type: "Post",
634 | path: "/posts/new",
635 | method: "POST",
636 | bodySerializer: $formSerializer
637 | ) {
638 | id
639 | title
640 | }
641 |
642 | publishRSS(input: $input)
643 | @rest(
644 | type: "Post",
645 | path: "/feed",
646 | method: "POST",
647 | bodySerializer: "xml"
648 | )
649 | }
650 | ```
651 |
652 | Where `formSerializer` could be defined as
653 |
654 | ```typescript
655 | const formSerializer = (data: any, headers: Headers) => {
656 | const formData = new FormData();
657 | for (let key in data) {
658 | if (data.hasOwnProperty(key)) {
659 | formData.append(key, data[key]);
660 | }
661 | }
662 |
663 | headers.set('Content-Type', 'application/x-www-form-urlencoded');
664 |
665 | return {body: formData, headers};
666 | }
667 |
668 | ```
669 |
670 | And `"xml"` would have been defined on the `RestLink` directly
671 |
672 | ```typescript
673 | const restLink = new RestLink({
674 | ...otherOptions,
675 | bodySerializers: {
676 | xml: xmlSerializer
677 | }
678 | })
679 | ```
680 |
681 | ## @export directive
682 |
683 | The export directive re-exposes a field for use in a later (nested) query. These are the same semantics that will be supported on the server, but when used in a `RestLink` you can use the exported variables for further calls (i.e. waterfall requests from nested fields)
684 |
685 | _Note: If you're constantly using @export you may prefer to take a look at [`apollo-server`](https://www.apollographql.com/docs/apollo-server/) which you can try out at [launchpad.graphql.com](https://launchpad.graphql.com/)_
686 |
687 | ### Arguments
688 |
689 | * `as: string`: name to create this as a variable to be used down the selection set
690 |
691 | ### Example
692 |
693 | An example use-case would be getting a list of users, and hitting a different endpoint to fetch more data using the exported field in the REST query args.
694 |
695 | ```graphql
696 | const QUERY = gql`
697 | query RestData($email: String!) {
698 | users @rest(path: '/users/email?{args.email}', method: 'GET', type: 'User') {
699 | id @export(as: "id")
700 | firstName
701 | lastName
702 | friends @rest(path: '/friends/{exportVariables.id}', type: '[User]') {
703 | firstName
704 | lastName
705 | }
706 | }
707 | }
708 | `;
709 | ```
710 |
711 | ## Mutations
712 |
713 | You can write also mutations with the apollo-link-rest, for example:
714 |
715 | ```graphql
716 | mutation deletePost($id: ID!) {
717 | deletePostResponse(id: $id)
718 | @rest(type: "Post", path: "/posts/{args.id}", method: "DELETE") {
719 | NoResponse
720 | }
721 | }
722 | ```
723 |
724 | ## Troubleshooting
725 |
726 | As you start using `apollo-link-rest` you may run into some standard issues that we thought we could help you solve.
727 |
728 | * `Missing field __typename in ...` -- If you see this, it's possible you haven't provided `type:` to the [`@rest(...)`](#rest-directive)-directive. Alternately you need to set up a [`typePatcher`](#typename-patching)
729 | * `Headers is undefined` -- If you see something like this, you're running in a browser or other Javascript environment that does not yet support the full specification for the `Headers` API.
730 |
731 | ## Example apps
732 |
733 | To get you started, here are some example apps:
734 |
735 | * [Simple](https://github.com/apollographql/apollo-link-rest/tree/master/examples/simple):
736 | A very simple app with a single query that reflect the setup section.
737 | * [Advanced](https://github.com/apollographql/apollo-link-rest/tree/master/examples/advanced):
738 | A more complex app that demonstrate how to use an export directive.
739 |
740 | ## Contributing
741 |
742 | Please join us on github: [apollographql/apollo-link-rest](https://github.com/apollographql/apollo-link-rest/) and in the ApolloGraphQL Slack in the `#apollo-link-rest` chat room.
743 |
744 | If you have an example app that you'd like to be featured, please send us a PR! 😊 We'd love to hear how you're using `apollo-link-rest`.
745 |
--------------------------------------------------------------------------------
/examples/advanced/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/examples/advanced/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
2 |
3 |
--------------------------------------------------------------------------------
/examples/advanced/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "advanced",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@apollo/client": "3.7.1",
7 | "apollo-link-rest": "0.x",
8 | "graphql": "16.6.0",
9 | "graphql-tag": "2.12.6",
10 | "qs": "6.11.0",
11 | "react": "18.2.0",
12 | "react-dom": "18.2.0",
13 | "react-scripts": "5.0.1"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | },
21 | "browserslist": {
22 | "production": [
23 | ">0.2%",
24 | "not dead",
25 | "not op_mini all"
26 | ],
27 | "development": [
28 | "last 1 chrome version",
29 | "last 1 firefox version",
30 | "last 1 safari version"
31 | ]
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/advanced/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apollographql/apollo-link-rest/bd0e244b9826e8e9053296e87bbb081a4b4cd394/examples/advanced/public/favicon.ico
--------------------------------------------------------------------------------
/examples/advanced/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/examples/advanced/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-header {
6 | background-color: #222;
7 | padding: 20px;
8 | color: white;
9 | }
10 |
11 | .App-title {
12 | font-size: 1.5em;
13 | }
14 |
15 | .App-intro {
16 | font-size: large;
17 | }
18 |
19 | @keyframes App-logo-spin {
20 | from { transform: rotate(0deg); }
21 | to { transform: rotate(360deg); }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/advanced/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
3 | import { RestLink } from 'apollo-link-rest';
4 | import SearchShow from './SearchShow';
5 | import './App.css';
6 |
7 | const restLink = new RestLink({
8 | uri: 'https://api.tvmaze.com/',
9 | });
10 |
11 | const client = new ApolloClient({
12 | link: restLink,
13 | cache: new InMemoryCache(),
14 | });
15 |
16 | class App extends Component {
17 | render() {
18 | return (
19 |