Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

(examples) - With-refresh-auth (#1605)

* Add With-refresh-auth example

authored by

Yanko Valera and committed by
GitHub
1788b3b2 b501be8a

+335 -4
+2 -1
examples/with-graphcache-pagination/README.md
··· 1 - # Integrating with React 1 + # Integrating with `@urql/exchange-graphcache`'s cacheExchange pagination 2 2 3 3 Integrating urql is as simple as: 4 4 ··· 11 11 ``` 12 12 13 13 2. Install [graphcache](https://formidable.com/open-source/urql/docs/graphcache/) 14 + 14 15 ```sh 15 16 yarn add @urql/exchange-graphcache 16 17 # or
+1 -1
examples/with-multipart/README.md
··· 1 - # Integrating with React 1 + # Integrating with `@urql/exchange-multipart-fetch`'s multipartFetchExchange 2 2 3 3 Integrating urql is as simple as: 4 4
+21
examples/with-refresh-auth/README.md
··· 1 + # Integrating with `@urql/exchange-auth`'s authExchange 2 + 3 + Integrating urql is as simple as: 4 + 5 + 1. Install packages [getting started](https://formidable.com/open-source/urql/docs/basics/react-preact/) 6 + 7 + ```sh 8 + yarn add urql graphql 9 + # or 10 + npm install --save urql graphql 11 + ``` 12 + 13 + 2. Install [authExchange](https://formidable.com/open-source/urql/docs/advanced/authentication/) 14 + 15 + ```sh 16 + yarn add @urql/exchange-auth 17 + # or 18 + npm install --save @urql/exchange-auth 19 + ``` 20 + 21 + 3. Setting up the Client and setup the Auth flow [here](src/App.js)
+39
examples/with-refresh-auth/package.json
··· 1 + { 2 + "name": "react-urql-query", 3 + "version": "1.0.0", 4 + "private": true, 5 + "dependencies": { 6 + "@urql/exchange-auth": "^0.1.2", 7 + "graphql": "^15.5.0", 8 + "react": "^17.0.2", 9 + "react-dom": "^17.0.2", 10 + "urql": "^2.0.2" 11 + }, 12 + "devDependencies": { 13 + "react-scripts": "4.0.3" 14 + }, 15 + "scripts": { 16 + "start": "react-scripts start", 17 + "build": "react-scripts build", 18 + "test": "react-scripts test", 19 + "eject": "react-scripts eject" 20 + }, 21 + "eslintConfig": { 22 + "extends": [ 23 + "react-app", 24 + "react-app/jest" 25 + ] 26 + }, 27 + "browserslist": { 28 + "production": [ 29 + ">0.2%", 30 + "not dead", 31 + "not op_mini all" 32 + ], 33 + "development": [ 34 + "last 1 chrome version", 35 + "last 1 firefox version", 36 + "last 1 safari version" 37 + ] 38 + } 39 + }
+15
examples/with-refresh-auth/src/App.js
··· 1 + import React from 'react'; 2 + import { Provider } from 'urql'; 3 + 4 + import client from './client'; 5 + import Home from './pages/Home'; 6 + 7 + function App() { 8 + return ( 9 + <Provider value={client}> 10 + <Home /> 11 + </Provider> 12 + ); 13 + } 14 + 15 + export default App;
+19
examples/with-refresh-auth/src/auth/Store.js
··· 1 + const TOKEN_KEY = 'token'; 2 + const REFRESH_TOKEN_KEY = 'refresh_token'; 3 + 4 + export const saveAuthData = ({ token, refreshToken }) => { 5 + localStorage.setItem(TOKEN_KEY, token); 6 + localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken); 7 + }; 8 + 9 + export const getToken = () => { 10 + return localStorage.getItem(TOKEN_KEY); 11 + }; 12 + 13 + export const getRefreshToken = () => { 14 + return localStorage.getItem(REFRESH_TOKEN_KEY); 15 + }; 16 + 17 + export const clearStorage = () => { 18 + localStorage.clear(); 19 + };
+91
examples/with-refresh-auth/src/client/index.js
··· 1 + import { 2 + createClient, 3 + dedupExchange, 4 + fetchExchange, 5 + cacheExchange, 6 + gql, 7 + } from 'urql'; 8 + import { makeOperation } from '@urql/core'; 9 + import { authExchange } from '@urql/exchange-auth'; 10 + import { 11 + getRefreshToken, 12 + getToken, 13 + saveAuthData, 14 + clearStorage, 15 + } from '../auth/Store'; 16 + 17 + const REFRESH_TOKEN_MUTATION = gql` 18 + mutation RefreshCredentials($refreshToken: String!) { 19 + refreshCredentials(refreshToken: $refreshToken) { 20 + refreshToken 21 + token 22 + } 23 + } 24 + `; 25 + 26 + const getAuth = async ({ authState, mutate }) => { 27 + if (!authState) { 28 + const token = getToken(); 29 + const refreshToken = getRefreshToken(); 30 + 31 + if (token && refreshToken) { 32 + return { token, refreshToken }; 33 + } 34 + 35 + return null; 36 + } 37 + 38 + const result = await mutate(REFRESH_TOKEN_MUTATION, { 39 + refreshToken: authState.refreshToken, 40 + }); 41 + 42 + if (result.data?.refreshCredentials) { 43 + saveAuthData(result.data.refreshCredentials); 44 + 45 + return result.data.refreshCredentials; 46 + } 47 + 48 + // This is where auth has gone wrong and we need to clean up and redirect to a login page 49 + clearStorage(); 50 + window.location.reload(); 51 + 52 + return null; 53 + }; 54 + 55 + const addAuthToOperation = ({ authState, operation }) => { 56 + if (!authState || !authState.token) { 57 + return operation; 58 + } 59 + 60 + const fetchOptions = 61 + typeof operation.context.fetchOptions === 'function' 62 + ? operation.context.fetchOptions() 63 + : operation.context.fetchOptions || {}; 64 + 65 + return makeOperation(operation.kind, operation, { 66 + ...operation.context, 67 + fetchOptions: { 68 + ...fetchOptions, 69 + headers: { 70 + ...fetchOptions.headers, 71 + Authorization: `Bearer ${authState.token}`, 72 + }, 73 + }, 74 + }); 75 + }; 76 + 77 + const didAuthError = ({ error }) => { 78 + return error.graphQLErrors.some(e => e.extensions?.code === 'UNAUTHORIZED'); 79 + }; 80 + 81 + const client = createClient({ 82 + url: 'https://trygql.dev/graphql/web-collections', 83 + exchanges: [ 84 + dedupExchange, 85 + cacheExchange({}), 86 + authExchange({ getAuth, addAuthToOperation, didAuthError }), 87 + fetchExchange, 88 + ], 89 + }); 90 + 91 + export default client;
+13
examples/with-refresh-auth/src/index.css
··· 1 + body { 2 + margin: 0; 3 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 + sans-serif; 6 + -webkit-font-smoothing: antialiased; 7 + -moz-osx-font-smoothing: grayscale; 8 + } 9 + 10 + code { 11 + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 + monospace; 13 + }
+12
examples/with-refresh-auth/src/index.js
··· 1 + import React from 'react'; 2 + import ReactDOM from 'react-dom'; 3 + import './index.css'; 4 + import App from './App'; 5 + 6 + ReactDOM.render( 7 + <React.StrictMode> 8 + <App /> 9 + </React.StrictMode>, 10 + document.getElementById('root') 11 + ); 12 +
+28
examples/with-refresh-auth/src/pages/Home.js
··· 1 + import React, { useEffect, useState } from 'react'; 2 + 3 + import { getToken, saveAuthData } from '../auth/Store'; 4 + import Profile from './Profile'; 5 + import LoginForm from './LoginForm'; 6 + 7 + const Home = () => { 8 + const [isLoggedIn, setIsLoggedIn] = useState(false); 9 + 10 + const onLoginSuccess = auth => { 11 + setIsLoggedIn(true); 12 + saveAuthData(auth); 13 + }; 14 + 15 + useEffect(() => { 16 + if (getToken()) { 17 + setIsLoggedIn(true); 18 + } 19 + }, []); 20 + 21 + return isLoggedIn ? ( 22 + <Profile /> 23 + ) : ( 24 + <LoginForm onLoginSuccess={onLoginSuccess} /> 25 + ); 26 + }; 27 + 28 + export default Home;
+57
examples/with-refresh-auth/src/pages/LoginForm.js
··· 1 + import React, { useEffect, useState } from 'react'; 2 + import { gql, useMutation } from 'urql'; 3 + 4 + const LOGIN_MUTATION = gql` 5 + mutation Signin($input: LoginInput!) { 6 + signin(input: $input) { 7 + refreshToken 8 + token 9 + } 10 + } 11 + `; 12 + 13 + const LoginForm = ({ onLoginSuccess }) => { 14 + const [password, setPassword] = useState(''); 15 + const [username, setUsername] = useState(''); 16 + 17 + const [loginResult, login] = useMutation(LOGIN_MUTATION); 18 + 19 + const { data, fetching, error } = loginResult; 20 + 21 + const onUsernameChange = evt => { 22 + setUsername(evt.target.value); 23 + }; 24 + 25 + const onPasswordChange = evt => { 26 + setPassword(evt.target.value); 27 + }; 28 + 29 + const onSubmit = evt => { 30 + evt.preventDefault(); 31 + login({ input: { username, password } }); 32 + }; 33 + 34 + useEffect(() => { 35 + if (data && data.signin) { 36 + onLoginSuccess(data.signin); 37 + } 38 + }, [onLoginSuccess, data]); 39 + 40 + if (fetching) { 41 + return <p>loading...</p>; 42 + } 43 + 44 + return ( 45 + <form onSubmit={onSubmit}> 46 + {error && <p>Oh no... {error.message}</p>} 47 + 48 + <input type="text" onChange={onUsernameChange} /> 49 + 50 + <input type="password" onChange={onPasswordChange} /> 51 + 52 + <input type="submit" title="login" /> 53 + </form> 54 + ); 55 + }; 56 + 57 + export default LoginForm;
+36
examples/with-refresh-auth/src/pages/Profile.js
··· 1 + import React from 'react'; 2 + import { gql, useQuery } from 'urql'; 3 + 4 + const PROFILE_QUERY = gql` 5 + query Profile { 6 + me { 7 + id 8 + username 9 + createdAt 10 + } 11 + } 12 + `; 13 + 14 + const Profile = () => { 15 + const [result] = useQuery({ query: PROFILE_QUERY }); 16 + 17 + const { data, fetching, error } = result; 18 + 19 + return ( 20 + <div> 21 + {fetching && <p>Loading...</p>} 22 + 23 + {error && <p>Oh no... {error.message}</p>} 24 + 25 + {data && ( 26 + <> 27 + <p>profile data</p> 28 + <p>id: {data.me.id}</p> 29 + <p>username: {data.me.username}</p> 30 + </> 31 + )} 32 + </div> 33 + ); 34 + }; 35 + 36 + export default Profile;
+1 -2
package.json
··· 25 25 }, 26 26 "eslintIgnore": [ 27 27 "packages/site/dist-prod", 28 - "docs", 29 - "examples" 28 + "docs" 30 29 ], 31 30 "prettier": { 32 31 "singleQuote": true,