How to build a serverless app with Gatsby, Netlify and FaunaDB - Part 3
Every story has an end
Finally, we are at the end of the series, Go Serverless. Thank you for reading and liking previous articles of the series. Truly appreciate.
If you are new to the series, here is a quick recap of what we have covered so far:
👉 How to build a serverless app with Gatsby, Netlify and FaunaDB - Part 1
- Created a database, schema, data-set and the server key with
FaunaDB
. - Tested the testimonial data-set with, create and fetch operations.
- Written
serverless
functions usingNetlify Function
capability to fetch, create, update and delete testimonials. - Tested these functions like APIs.
👉 How to build a serverless app with Gatsby, Netlify and FaunaDB - Part 2
- Explored
Gatsby
to write the client side testimonial app. - Fetched the testimonials in the app using the
serverless
function. - Deployed the app with
Netlify
.
In this article, we will continue the app development to add an authentication layer. We will allow our users to create a testimonial only after they authenticate to our app.
Our Testimonial app
Our plan is to,
- Add a
Login
button to the user interface. - Allow users to create an account using email id and password.
- Allow users to enter credentials when they try to log-in.
- Show the
Create Testimonial
option on successful login and allow users to create testimonials. - Add the newly created testimonial to the list.
- Allow the logged-in user to log-out.
At the end, the testimonial app may behave like this:
But, we are Serverless!
So how about the Authentication module? Do we need to implement one? Who is going to manage identities(i,e, account creation, role provisioning etc.)?
70% - 80% of the features that once required a custom back-end can now be done entirely on the front-end. More details from here.
Authentication and Authorization modules are among them. It powers going serverless
too. We are not going to implement any of these by ourselves. Rather, we are going to use one.
Netlify Identity
We are going to explore Netlify Identity
.
Netlify Identity service brings a full suite of authentication functionality, backed by the GoTrue API. This allows you to manage and authenticate users on your site or app, without requiring them to be users of Netlify or any other service.
- Login to your
Netlify
account and browse to thetestimonial
app we have created in the previous article. - Go to the
identity
tab and click on theEnable Identity
button.
That's all. You have enabled the identity services for the testimonial app successfully. Let us now make use of it by modifying the testimonial
app a bit.
Install dependencies
We have to install few sets of dependencies to use the Netlify Identity in our app.
react-netlify-identity-widget
: A React component used to authenticate with Netlify's Identity service. It also has some peer dependencies like,@reach/dialog
,@reach/tabs
,@reach/visually-hidden
.gatsby-plugin-netlify-identity
: A Gatsby plugin which adds a React Netlify Identity Widget Provider for us.
Open a command prompt at the root of the project folder and install the dependencies using:
yarn add gatsby-plugin-netlify-identity react-netlify-identity-widget @reach/dialog @reach/tabs @reach/visually-hidden
We will use a modal dialog to allow users for creating a testimonial. I am using react-bootstrap
for that purpose. Please install it too,
yarn add react-bootstrap bootstrap
Configure Gatsby
Next, we have to tell Gatsby
that, we will use the idenity service from Netlify. For that, edit the gatsby-config.js
file and add the plugins
section as shown below:
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-netlify-identity`,
options: {
url: `https://your-project-identity.netlify.app/`
}
}
]
}
Please note, the url in the above configuration should match the domain name of your app. Here is an example of what you need to select and specify as the url in the gatsby-config.js
file:
Let's include it in the code
Time to modify the index.js
file to use the identity service from Netlify.
First, import required packages
import IdentityModal, { useIdentityContext } from "react-netlify-identity-widget"; import "react-netlify-identity-widget/styles.css"; import 'bootstrap/dist/css/bootstrap.min.css'; import Button from 'react-bootstrap/Button'; import Modal from 'react-bootstrap/Modal';
The
react-netlify-identity-widget
provides a modal dialog-box for providing credentials and creating account. We need to capture if the user is correctly authenticated using it and then, show theCreate Testimonial
button.// identity code to get if the user logged-in const identity = useIdentityContext(); const [dialog, setDialog] = useState(false); const name = (identity && identity.user && identity.user.user_metadata && identity.user.user_metadata.full_name) || "Untitled"; const isLoggedIn = identity && identity.isLoggedIn;
{ identity && identity.isLoggedIn ? ( <div className="auth-btn-grp"> <Button variant="outline-primary" onClick={handleShow}>Create Testimonial </Button> { ' '} <Button variant="outline-primary" className="login-btn" onClick={() => setDialog(true)}> {isLoggedIn ? `Hello ${name}, Log out here!` : "LOG IN"} </Button> </div> ) : ( <div className="auth-btn-grp"> <Button variant="outline-primary" className="login-btn" onClick={() => setDialog(true)}> {isLoggedIn ? `Hello ${name}, Log out here!` : "LOG IN"} </Button> </div> ) }
This is how to add the identity dialog:
<IdentityModal showDialog={dialog} onCloseDialog={() => setDialog(false)} />
Last, modify the
index.css
file to add these classes:.auth-btn-grp { float: right; padding: 5px; } .create-testimonial { color: #000000; } .create-form { display: flex; justify-content: center; flex-direction: column; }
That's all for including the identity service from Netlify
to the testimonial
app!
Here is the complete index.js
file after the modification. You can notice the way we are calling the /api/create-testimonial
for creating a testimonial after a successful authentication.
import React, { useEffect, useState } from 'react';
import axios from "axios";
import ReactStars from 'react-stars';
import "react-responsive-carousel/lib/styles/carousel.min.css";
import { Carousel } from "react-responsive-carousel";
import IdentityModal, { useIdentityContext } from "react-netlify-identity-widget";
import "react-netlify-identity-widget/styles.css";
import 'bootstrap/dist/css/bootstrap.min.css';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import './index.css';
export default () => {
const [status, setStatus ] = useState('loading...');
const [testimonials, setTestimonials] = useState(null);
useEffect(() => {
if (status !== "loading...") return;
axios("/api/get-testimonials").then(result => {
if (result.status !== 200) {
console.error("Error loading testimonials");
console.error(result);
return;
}
setTestimonials(result.data.messages);
setStatus("loaded");
});
}, [status]);
const getAvatar = () => {
const random = Math.floor(Math.random() * (testimonials.length - 0 + 1) + 0);
const imgUrl = `https://avatars.dicebear.com/api/human/${random}.svg?mood[]=happy`;
return imgUrl;
}
// identity code
const identity = useIdentityContext();
const [dialog, setDialog] = useState(false);
const name =
(identity && identity.user && identity.user.user_metadata && identity.user.user_metadata.full_name) || "Untitled";
const isLoggedIn = identity && identity.isLoggedIn;
// create testimonial
const [show, setShow] = useState(false);
const [rating, setRating] = useState(4);
const [text, setText] = useState('');
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const ratingChanged = (newRating) => {
setRating(newRating);
}
const textChanged = evt => {
const val = evt.target.value;
setText(val);
}
const handleCreate = async event => {
if(text === '') return;
await axios.post('/api/create-testimonial', { text, rating });
const newList = testimonials.concat({ text, rating });
setTestimonials(newList);
setShow(false);
}
return (
<>
{
identity && identity.isLoggedIn ? (
<div className="auth-btn-grp">
<Button variant="outline-primary" onClick={handleShow}>Create Testimonial</Button>
{ ' '}
<Button variant="outline-primary" className="login-btn" onClick={() => setDialog(true)}>
{isLoggedIn ? `Hello ${name}, Log out here!` : "LOG IN"}
</Button>
</div>
) : (
<div className="auth-btn-grp">
<Button variant="outline-primary" className="login-btn" onClick={() => setDialog(true)}>
{isLoggedIn ? `Hello ${name}, Log out here!` : "LOG IN"}
</Button>
</div>
)
}
<Carousel
className="main"
showArrows={true}
infiniteLoop={true}
showThumbs={false}
showStatus={false}
autoPlay={false} >
{testimonials && testimonials.map((testimonial, index) => (
<div key={ index } className="testimonial">
<img
src={ getAvatar() }
height="50px"
width="50px"
alt="avatar" />
<div className="message">
<ReactStars
className="rating"
count={ testimonial.rating }
size={24}
color1={'#ffd700'}
edit={false}
half={false} />
<p className="text">
{ testimonial.text }
</p>
</div>
</div>
))}
</Carousel>
<IdentityModal showDialog={dialog} onCloseDialog={() => setDialog(false)} />
<Modal
show={show}
onHide={handleClose}
animation={true}
className="create-testimonial"
>
<Modal.Header closeButton>
<Modal.Title>Create a Testimonial</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="create-form">
<textarea
onChange={(evt) => textChanged(evt)}
placeholder="Enter your message here" />
<br />
<span>Rating:</span> {' '}
<ReactStars
count={5}
value={rating}
onChange={ratingChanged}
size={24}
color2={'#ffd700'}
half={false} />
</div>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Cancel
</Button>
<Button variant="primary" onClick={(evt) => handleCreate(evt)}>Create</Button>
</Modal.Footer>
</Modal>
</>
);
}
We are done. Just push the code to your git repo. A build should get started in Netlify automatically and the updated app should be deployed to make the site live.
Here is the git repo link. Don't forget to give it a star, if you liked the work.
Conclusion
Thanks for reading through and trying out the application. Hope you enjoyed it. Let us end with a few useful links:
- Getting started with FaunaDB
- Netlify's AWS lambda functions
- Deploy Gatsby to Netlify
- Netlify Identity
You may also like,
If it was useful to you, please Like/Share so that, it reaches others as well. To get email notification on my latest posts, please subscribe to my blog by hitting the Subscribe button at the top of the page.
Follow me on twitter @tapasadhikary for more updates.