In this section, you will learn how to do user authentication from your client application.
You can find the completed code for this section in this Github link.
Add some basic styling to your application. You can add UIKit library for styling. Make the following changes to your pages/_app.js
file. Feel free to use other CSS library of your choice or write your own CSS.
/* eslint-disable @next/next/no-sync-scripts */
import Head from 'next/head';
import { ApolloProvider } from '@apollo/client';
import client from '../apollo-client';
import '../styles/globals.css';
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.7.6/dist/css/uikit.min.css" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.7.6/dist/js/uikit.min.js" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.7.6/dist/js/uikit-icons.min.js" />
</Head>
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
</>
);
}
export default MyApp;
First, create a new folder named components
in the root of your application. In the components folder, create a new file named Signup.js
and add the following code. This creates a React component containing a signup form.
import { useState, useEffect } from 'react'
const INITAL_STATE = {
name: '',
email: '',
password: '',
}
export default function Signup() {
const [state, setState] = useState(INITAL_STATE);
const handleChange = e => {
setState({
...state,
[e.target.name]: e.target.value
})
}
const doSignup = e => {
e.preventDefault();
}
return (
<div uk-grid="true">
<div>
<div className="uk-card uk-card-default uk-card-body">
<h3 className="uk-card-title">Sign up</h3>
<form onSubmit={doSignup}>
<div className="uk-margin">
<input
className="uk-input"
type="text"
placeholder="Username"
name="name"
onChange={handleChange}
value={state.name}
autoComplete="off"
/>
</div>
<div className="uk-margin">
<input
className="uk-input"
type="text"
placeholder="Email"
name="email"
onChange={handleChange}
value={state.email}
/>
</div>
<div className="uk-margin">
<input
className="uk-input"
type="password"
placeholder="Password"
name="password"
onChange={handleChange}
value={state.password}
/>
</div>
<div className="uk-margin">
<input className="uk-input" type="submit" />
</div>
</form>
</div>
</div>
</div>
)
}
Next, create a new /signup route in your application by creating a new file called signup.js
in the pages directory. Add the following code to your pages/signup.js file.
import Signup from '../components/Signup'
import styles from '../styles/Home.module.css'
export default function SignUpPage() {
return (
<div className={styles.container}>
<Signup />
</div>
)
}
You plug in the Signup component to signup page. You do this because it is a good practice not to have API logic in your page level component.
Run the application with npm run dev command and visit localhost:3000/signup. Ensure that the signup page is loading.
In the previous section, you created a signup mutation in GraphQL. On the signup page on form submit, you call this signup
mutation using the apollo-client library. Make the following changes to your Signup component.
First, import the useMutation
and gql
functions from apollo-client library. Define the signup
mutation as a JavaScript query string constant.
...
import { useMutation, gql } from '@apollo/client';
const SIGN_UP = gql`
mutation OwnerSignUp($email: String!, $name: String!, $password: String! ) {
registerOwner(email: $email, name: $name, password: $password) {
_id
name
email
}
}
`;
Next, attach the mutation with a button. So when the button is selected the mutation fires. Create a useEffect
hook to listen on the signup response.
export default function Signup() {
const [signupUserFunc, { data, loading, error }] = useMutation(SIGN_UP);
...
useEffect(() => {
if(data) {
alert('Signup Complete')
setState(INITAL_STATE);
console.log(data);
}
}, [data])
...
const doSignup = e => {
e.preventDefault();
signupUserFunc({
variables: {
...state,
},
})
}
if (loading) return 'Submitting...';
if (error) return 'Something went wrong...'
return (
<div>....</div>
)
}
With all the updates applied your components/Signup.js
will be simmilar to the following code snippet.
// components/Signup.js
import { useState, useEffect } from 'react'
import { useMutation, gql } from "@apollo/client";
const SIGN_UP = gql`
mutation OwnerSignUp($email: String!, $name: String!, $password: String! ) {
registerOwner(email: $email, name: $name, password: $password) {
_id
name
email
}
}
`;
const INITAL_STATE = {
name: '',
email: '',
password: '',
}
export default function Signup() {
const [signupUserFunc, { data, loading, error }] = useMutation(SIGN_UP);
const [state, setState] = useState(INITAL_STATE);
useEffect(() => {
if(data) {
alert('Signup Complete')
setState(INITAL_STATE);
console.log(data);
}
}, [data])
const handleChange = e => {
setState({
...state,
[e.target.name]: e.target.value
})
}
const doSignup = e => {
e.preventDefault();
signupUserFunc({
variables: {
...state,
},
})
}
if (loading) return 'Submitting...';
if (error) return 'Something went wrong...'
return (
<div uk-grid>
<div>
<div className="uk-card uk-card-default uk-card-body">
<h3 className="uk-card-title">Sign up</h3>
<form onSubmit={doSignup}>
<div className="uk-margin">
<input
className="uk-input"
type="text"
placeholder="Username"
name="name"
onChange={handleChange}
value={state.name}
autoComplete="off"
/>
</div>
<div className="uk-margin">
<input
className="uk-input"
type="text"
placeholder="Email"
name="email"
onChange={handleChange}
value={state.email}
/>
</div>
<div className="uk-margin">
<input
className="uk-input"
type="password"
placeholder="Password"
name="password"
onChange={handleChange}
value={state.password}
/>
</div>
<div className="uk-margin">
<input className="uk-input" type="submit" />
</div>
</form>
</div>
</div>
</div>
)
}
After you update the Signup component, try registering a user. Navigate to Collections in your Fauna dashboard and review the Owner collection. Your newly registered users will appear in this collection.
Next, create a simple login component. Create a new file components/Login.js
. Add the following code to your file.
// components/Login.js
import { useState, useEffect } from 'react'
import { useMutation, gql } from '@apollo/client'
const LOGIN = gql`
mutation OwnerLogin($email: String!, $password: String! ) {
login(email: $email, password: $password) {
ttl
secret
}
}
`;
export default function Login() {
const [loginFunc, { data, loading, error }] = useMutation(LOGIN)
const [state, setState] = useState({
email: '',
password: ''
})
useEffect(() => {
if(data) {
// TODO: Save User Session
alert('User Logged in')
console.log(data);
}
}, [data])
const doLogin = e => {
e.preventDefault();
loginFunc({
variables: {
...state
}
}).catch(e => console.log(e))
}
const handleChange = (e) => {
setState({
...state,
[e.target.name]: e.target.value
})
}
if (loading) return 'Submitting...';
return (
<div>
<div>
<div className="uk-card uk-card-default uk-card-body">
<h3 className="uk-card-title">Login</h3>
{error ?
<div className="uk-alert-danger" uk-alert style={{ maxWidth: '300px', padding: '10px'}}>
Incorrect email and password
</div> : null
}
<form onSubmit={doLogin}>
<div className="uk-margin">
<input
className="uk-input"
type="text"
placeholder="Email"
name="email"
onChange={handleChange}
value={state.email}
/>
</div>
<div className="uk-margin">
<input
className="uk-input"
type="password"
placeholder="Password"
name="password"
onChange={handleChange}
value={state.password}
/>
</div>
<div className="uk-margin">
<input className="uk-input" type="submit" />
</div>
</form>
</div>
</div>
</div>
)
}
Create a new page pages/login.js
. Add the following code to this file. You plug your Login component into your login page component.
// pages/login.js
import Login from '../components/Login'
import styles from '../styles/Home.module.css'
export default function LoginPage() {
return (
<div className={styles.container}>
<Login />
</div>
)
}
To ensure everything is working as intended, run the application with npm run dev command
, and visit localhost:3000/login. Verify the login function is working. Log in with a user you have registered before. Observe the console tab in your browser.
If you are getting a secret back from your GraphQL request, that means everything is working as intended. You can now use this secret to interact with other resources in Fauna. In the next section, you learn how to manage your user sessions with the client.
📙 Get the final code for this section here