react-router-dom
Operate npm install react-router-dom
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
import Users from './components/Users';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import Landing from './components/Landing';
const client = new ApolloClient({
uri:"http://localhost:4000",
cache: new InMemoryCache()
})
function App() {
return (
<ApolloProvider client={client}>
1️⃣ <Router>
<Switch>
<Route path="/landing">
<Landing />
</Route>
<Route path="/">
<Users />
</Route>
</Switch>
</Router>
</ApolloProvider>
);
}
export default App;
📋Memo(App.tsx)
1️⃣ <Router>
1. Using `BrowserRouter` made `Router` that routes to "/" and "/landing"
import React from "react";
const Landing = () => {
return(
<div>
<h1>Landing</h1>
</div>
);
}
export default Landing;
npm install apollo-link-context
To create context that gets token and passes it along for each request
import { ApolloClient, InMemoryCache, ApolloProvider, HttpLink } from '@apollo/client';
import Users from './components/Users';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import Landing from './components/Landing';
import {setContext} from 'apollo-link-context';
import Signup from './pages/Signup';
import Login from './pages/Login';
// const client = new ApolloClient({
// uri:"http://localhost:4000",
// cache: new InMemoryCache()
// })
🚗 const httpLink = new HttpLink({uri:'http://localhost:4000'})
🚓 const authlink = setContext( async( req, { headers } ) => { // {headers : previous context}
// setContext((GraphQL request being executed, previous context) => {return object or a promise => returns an object to set the new context of a request})
// context is an object shared by all the resolvers of a specific execution.
// It's useful for keeping data such as authentication info, the current user, database connection, data sources and other things you need for running your business logic.
const token = localStorage.getItem('token')
return {
...headers,
headers:{
Authorization: token ? `Bearer ${token}` : null
}
}
})
const link = authlink.concat(httpLink as any)
// By default, Apollo Client uses Apollo Link's HttpLink to send GraphQL queries over HTTP.
// One of uri or link is required. If you provide both, link takes precedence.
const client = new ApolloClient(
{
🚕 link: (link as any),
cache: new InMemoryCache()}
)
function App() {
return (
<ApolloProvider client={client}>
<Router>
<Switch>
<Route path="/landing">
<Landing />
</Route>
<Route path="/signup">
<Signup />
</Route>
<Route path="/login">
<Login />
</Route>
<Route path="/">
<Users />
</Route>
</Switch>
</Router>
</ApolloProvider>
);
}
export default App;
In the case of my code, I have one ApolloLink
and one Terminating Link
1. ApolloLink
might add an HTTP header
to the outgoing operation request for authentication
purposes.
2. Terminating Link
sends the operation
to its destination
(usually a GraphQL server over HTTP
).
Context
is an object
shared by all the resolvers
of a specific execution.authentication info
, the current user
, database connection
, data sources
and other things you need for running your business logic.Context
may include network addresses, packets, fingerprints, or other types of data 🚗 const httpLink = new HttpLink({uri:'http://localhost:4000'})
1.HttpLink
is a termainating link
that sensd a GraphQL operation
to a remote endpoint over HTTP
.
2.Apollo Client
uses HttpLink
by default
when you provide the uri
option to the ApolloClient constructor
.
🚓 const authlink = setContext( async( req, { headers } )
(Context Setter)
1.setContext
((GraphQL request being executed
, previous context
) =>
{return object
or a promise
=> returns an object
to set the new context of a request
})
🚕 link: (link as any)
1. By default
, Apollo Client
uses Apollo Link's HttpLink
to send GraphQL queries
over HTTP
.
2. One of uri or link
is required. If you provide both, link
takes precedence
.
import {gql, useMutation} from '@apollo/client'
🚗 import {ErrorMessage, Field, Form, Formik} from 'formik';
🚓 import * as Yup from 'yup'
import {useHistory} from 'react-router-dom';
//npm install formik yup
//npm install -D types/yup
const SIGNUP_MUTATION = gql`
mutation signup($name: String, $email: String!, $password: String!){
signup(name: $name, email: $email, password: $password) {
token
}
}
`
interface SignupValues{
email: string
password: string
confirmPassword: string
name: string
}
const Signup = () => {
const history = useHistory();
const [signup, { data }] = useMutation(SIGNUP_MUTATION);
const initialValues: SignupValues= {
email:'',
password:'',
confirmPassword:'',
name:''
}
🚕 const validationSchema = Yup.object({
email: Yup.string()
.email("Invalid email address")
.required("Email Required"),
password: Yup.string()
.max(20, "Must be 20 charactres or less")
.required("Password Required"),
confirmPassword: Yup.string()
.oneOf([Yup.ref("password")], "Passwords must match"),
name: Yup.string()
.max(15, "Must be 15 characters or less")
.required("Name Required")
})
return(
<div>
<h1>Signup</h1>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
🛺 onSubmit={async( value, {setSubmitting}) => {
setSubmitting(true)
const response = await signup({
variables: value
})
localStorage.setItem("token", response.data.signup.token)
setSubmitting(false)
history.push('/users')
}}
>
<Form>
<Field name="email" type="text" placeholder="Email" />
<ErrorMessage name="email" component={'div'} />
<Field name="name" type="text" placeholder="Name" />
<ErrorMessage name="name" component={'div'} />
<Field name="password" type="text" placeholder="Password" />
<ErrorMessage name="password" component={'div'} />
<Field name="confirmPassword" type="text" placeholder="Confirm Password" />
<ErrorMessage name="confirmPassword" component={'div'} />
<button type="submit" >Signup</button>
</Form>
</Formik>
</div>
);
}
export default Signup;
🚓 About Yup
1. Yup
is a Javascript schema builder
for value parsing
and validation
.
🚕 onSubmit={async( values, { setSubmitting }) =>{...})
1. values
are values that are submitted from <Form>
tag
import {gql, useMutation} from '@apollo/client'
import {ErrorMessage, Field, Form, Formik} from 'formik';
import * as Yup from 'yup'
import {useHistory} from 'react-router-dom';
const LOGIN_MUTATION = gql`
mutation login($email: String!, $password: String!){
login(email: $email, password: $password) {
token
}
}
`
interface LoginValues{
email: string
password: string
}
const Login = () => {
const history = useHistory();
const [login, { data }] = useMutation(LOGIN_MUTATION);
const initialValues: LoginValues= {
email:'',
password: ''
}
const validationSchema = Yup.object({
email: Yup.string()
.email("Invalid email address")
.required("Email Required"),
password: Yup.string()
.max(20, "Must be 20 charactres or less")
.required("Password Required")
})
return(
<div>
<h1>Login</h1>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={async (value, { setSubmitting }) => {
setSubmitting(true)
const response = await login({
variables: value
})
localStorage.setItem("token", response.data.login.token)
setSubmitting(false)
history.push('/users')
}}
>
<Form>
<;Field name="email" type="text" placeholder="Email" />
<ErrorMessage name="email" component={'div'} />
<Field name="password" type="password" placeholder="Password" />
<ErrorMessage name="password" component={'div'} />
<button type="submit" >Login</button>
</Form>
</Formik>
</div>
);
}
export default Login;