The Jamstack is a modern web development architecture based on client-side JavaScript, reusable APIs, and prebuilt Markup.
The key aspects of a Jamstack application are the following:
- The entire app runs on a CDN (or ADN). CDN stands for Content Delivery Network and an ADN is an Application Delivery Network.
- Everything lives in Git.
- Automated builds run with a workflow when developers push the code.
- There’s Automatic deployment of the prebuilt markup to the CDN/ADN.
- Reusable APIs make hasslefree integrations with many of the services. To take a few examples, Stripe for the payment and checkout, Mailgun for email services, etc. We can also write custom APIs targeted to a specific use-case. We will see such examples of custom APIs in this article.
- It’s practically Serverless. To put it more clearly, we do not maintain any servers, rather make use of already existing services (like email, media, database, search, and so on) or serverless functions.
In this article, we will learn how to build a Jamstack application that has:
- A global data store with GraphQL support to store and fetch data with ease. We will use Fauna to accomplish this.
- Serverless functions that also act as the APIs to fetch data from the Fauna data store. We will use Netlify serverless functions for this.
- We will build the client side of the app using a Static Site Generator called Gatsbyjs.
- Finally we will deploy the app on a CDN configured and managed by Netlify CDN.
So, what are we building today?
We all love shopping. How cool would it be to manage all of our shopping notes in a centralized place? So we’ll be building an app called ‘shopnote’ that allows us to manage shop notes. We can also add one or more items to a note, mark them as done, mark them as urgent, etc.
At the end of this article, our shopnote app will look like this,

TL;DR
We will learn things with a step-by-step approach in this article. If you want to jump into the source code or demonstration sooner, here are links to them.
- You can access the shop note demo from here: https://shopnote.netlify.app/
- All the source code used in this article is in my GitHub repo. Feel free to follow it, as I keep updating the source code frequently. https://github.com/atapas/shopnote
Set up Fauna
Fauna is the data API for client-serverless applications. If you are familiar with any traditional RDBMS, a major difference with Fauna would be, it is a relational NOSQL system that gives all the capabilities of the legacy RDBMS. It is very flexible without compromising scalability and performance.
Fauna supports multiple APIs for data-access,
- GraphQL: An open source data query and manipulation language. If you are new to the GraphQL, you can find more details from here, https://graphql.org/
- Fauna Query Language (FQL): An API for querying Fauna. FQL has language specific drivers which makes it flexible to use with languages like JavaScript, Java, Go, etc. Find more details of FQL from here.
In this article we will explain the usages of GraphQL for the ShopNote application.
First thing first, sign up using this URL. Please select the free plan which is with a generous daily usage quota and more than enough for our usage.
Next, create a database by providing a database name of your choice. I have used shopnotes as the database name.

After creating the database, we will be defining the GraphQL schema and importing it into the database. A GraphQL schema defines the structure of the data. It defines the data types and the relationship between them. With schema we can also specify what kind of queries are allowed.
At this stage, let us create our project folder. Create a project folder somewhere on your hard drive with the name, shopnote. Create a file with the name, shopnotes.gql
with the following content:
type ShopNote { name: String! description: String updatedAt: Time items: [Item!] @relation } type Item { name: String! urgent: Boolean checked: Boolean note: ShopNote! } type Query { allShopNotes: [ShopNote!]! }
Here we have defined the schema for a shopnote list and item, where each ShopNote
contains name, description, update time and a list of Items. Each Item
type has properties like, name, urgent, checked and which shopnote it belongs to.
Note the @relation
directive here. You can annotate a field with the @relation
directive to mark it for participating in a bi-directional relationship with the target type. In this case, ShopNote
and Item
are in a one-to-many relationship. It means, one ShopNote
can have multiple Items, where each Item can be related to a maximum of one ShopNote.
You can read more about the @relation
directive from here. More on the GraphQL relations can be found from here.
As a next step, upload the shopnotes.gql
file from the Fauna dashboard using the IMPORT SCHEMA button,

Upon importing a GraphQL Schema, FaunaDB will automatically create, maintain, and update, the following resources:
- Collections for each non-native GraphQL Type; in this case, ShopNote and Item.
- Basic CRUD Queries/Mutations for each Collection created by the Schema, e.g.
createShopNote
allShopNotes
; each of which are powered by FQL. - For specific GraphQL directives: custom Indexes or FQL for establishing relationships (i.e.
@relation
), uniqueness (@unique
), and more!
Behind the scene, Fauna will also help to create the documents automatically. We will see that in a while.
Fauna supports a schema-free object relational data model. A database in Fauna may contain a group of collections. A collection may contain one or more documents. Each of the data records are inserted into the document. This forms a hierarchy which can be visualized as:

Here the data record can be arrays, objects, or of any other supported types. With the Fauna data model we can create indexes, enforce constraints. Fauna indexes can combine data from multiple collections and are capable of performing computations.
At this stage, Fauna already created a couple of collections for us, ShopNote and Item. As we start inserting records, we will see the Documents are also getting created. We will be able view and query the records and utilize the power of indexes. You may see the data model structure appearing in your Fauna dashboard like this in a while,

Point to note here, each of the documents is identified by the unique ref
attribute. There is also a ts
field which returns the timestamp of the recent modification to the document. The data record is part of the data
field. This understanding is really important when you interact with collections, documents, records using FQL built-in functions. However, in this article we will interact with them using GraphQL queries with Netlify Functions.
With all these understanding, let us start using our Shopenotes database that is created successfully and ready for use.
Let us try some queries
Even though we have imported the schema and underlying things are in place, we do not have a document yet. Let us create one. To do that, copy the following GraphQL mutation query to the left panel of the GraphQL playground screen and execute.
mutation { createShopNote(data: { name: "My Shopping List" description: "This is my today's list to buy from Tom's shop" items: { create: [ { name: "Butther - 1 pk", urgent: true } { name: "Milk - 2 ltrs", urgent: false } { name: "Meat - 1lb", urgent: false } ] } }) { _id name description items { data { name, urgent } } } }
Note, as Fauna already created the GraphQL mutation classes in the background, we can directly use it like, createShopNote
. Once successfully executed, you can see the response of a ShopNote creation at the right side of the editor.

The newly created ShopNote
document has all the required details we have passed while creating it. We have seen ShopNote
has a one-to-many relation with Item
. You can see the shopnote response has the item data nested within it. In this case, one shopnote has three items. This is really powerful. Once the schema and relation are defined, the document will be created automatically keeping that relation in mind.
Now, let us try fetching all the shopnotes. Here is the GraphQL query:
query { allShopNotes { data { _id name description updatedAt items { data { name, checked, urgent } } } } }
Let’s try the query in the playground as before:

Now we have a database with a schema and it is fully operational with creating and fetch functionality. Similarly, we can create queries for adding, updating, removing items to a shopnote and also updating and deleting a shopnote. These queries will be used at a later point in time when we create the serverless functions.
If you are interested to run other queries in the GraphQL editor, you can find them from here,
Create a Server Secret Key
Next, we need to create a secured server key to make sure the access to the database is authenticated and authorized.
Click on the SECURITY option available in the FaunaDB interface to create the key, like so,
On successful creation of the key, you will be able to view the key’s secret. Make sure to copy and save it somewhere safe.
We do not want anyone else to know about this key. It is not even a good idea to commit it to the source code repository. To maintain this secrecy, create an empty file called .env
at the root level of your project folder.
Edit the .env
file and add the following line to it (paste the generated server key in the place of, <YOUR_FAUNA_KEY_SECRET>
).
FAUNA_SERVER_SECRET=<YOUR_FAUNA_KEY_SECRET>
Add a .gitignore
file and write the following content to it. This is to make sure we do not commit the .env file to the source code repo accidentally. We are also ignoring node_modules as a best practice.
.env
We are done with all that had to do with Fauna’s setup. Let us move to the next phase to create serverless functions and APIs to access data from the Fauna data store. At this stage, the directory structure may look like this,

Set up Netlify Serverless Functions
Netlify is a great platform to create hassle-free serverless functions. These functions can interact with databases, file-system, and in-memory objects.
Netlify functions are powered by AWS Lambda. Setting up AWS Lambdas on our own can be a fairly complex job. With Netlify, we will simply set a folder and drop our functions. Writing simple functions automatically becomes APIs.
First, create an account with Netlify. This is free and just like the FaunaDB free tier, Netlify is also very flexible.
Now we need to install a few dependencies using either npm or yarn. Make sure you have nodejs installed. Open a command prompt at the root of the project folder. Use the following command to initialize the project with node dependencies,
npm init -y
Install the netlify-cli utility so that we can run the serverless function locally.
npm install netlify-cli -g
Now we will install two important libraries, axios and dotenv. axios will be used for making the HTTP calls and dotenv will help to load the FAUNA_SERVER_SECRET
environment variable from the .env
file into process.env
.
yarn add axios dotenv
Or:
npm i axios dotenv
Create serverless functions
Create a folder with the name, functions
at the root of the project folder. We are going to keep all serverless functions under it.
Now create a subfolder called utils
under the functions
folder. Create a file called query.js
under the utils
folder. We will need some common code to query the data store for all the serverless functions. The common code will be in the query.js
file.
First we import the axios library functionality and load the .env
file. Next, we export an async function that takes the query and variables. Inside the async function, we make calls using axios with the secret key. Finally, we return the response.
// query.js const axios = require("axios"); require("dotenv").config(); module.exports = async (query, variables) => { const result = await axios({ url: "https://graphql.fauna.com/graphql", method: "POST", headers: { Authorization: `Bearer $ {process.env.FAUNA_SERVER_SECRET}` }, data: { query, variables } }); return result.data; };
Create a file with the name, get-shopnotes.js
under the functions
folder. We will perform a query to fetch all the shop notes.
// get-shopnotes.js const query = require("./utils/query"); const GET_SHOPNOTES = ` query { allShopNotes { data { _id name description updatedAt items { data { _id, name, checked, urgent } } } } } `; exports.handler = async () => { const { data, errors } = await query(GET_SHOPNOTES); if (errors) { return { statusCode: 500, body: JSON.stringify(errors) }; } return { statusCode: 200, body: JSON.stringify({ shopnotes: data.allShopNotes.data }) }; };
Time to test the serverless function like an API. We need to do a one time setup here. Open a command prompt at the root of the project folder and type:
netlify login
This will open a browser tab and ask you to login and authorize access to your Netlify account. Please click on the Authorize button.
Next, create a file called, netlify.toml
at the root of your project folder and add this content to it,
[build] functions = "functions" [[redirects]] from = "/api/*" to = "/.netlify/functions/:splat" status = 200
This is to tell Netlify about the location of the functions we have written so that it is known at the build time.
Netlify automatically provides the APIs for the functions. The URL to access the API is in this form, /.netlify/functions/get-shopnotes
which may not be very user-friendly. We have written a redirect to make it like, /api/get-shopnotes
.
Ok, we are done. Now in command prompt type,
netlify dev
By default the app will run on localhost:8888 to access the serverless function as an API.

Open a browser tab and try this URL, http://localhost:8888/api/get-shopnotes
:

Congratulations!!! You have got your first serverless function up and running.
Let us now write the next serverless function to create a ShopNote
. This is going to be simple. Create a file named, create-shopnote.js
under the functions
folder. We need to write a mutation by passing the required parameters.
//create-shopnote.js const query = require("./utils/query"); const CREATE_SHOPNOTE = ` mutation($ name: String!, $ description: String!, $ updatedAt: Time!, $ items: ShopNoteItemsRelation!) { createShopNote(data: {name: $ name, description: $ description, updatedAt: $ updatedAt, items: $ items}) { _id name description updatedAt items { data { name, checked, urgent } } } } `; exports.handler = async event => { const { name, items } = JSON.parse(event.body); const { data, errors } = await query( CREATE_SHOPNOTE, { name, items }); if (errors) { return { statusCode: 500, body: JSON.stringify(errors) }; } return { statusCode: 200, body: JSON.stringify({ shopnote: data.createShopNote }) }; };
Please give your attention to the parameter, ShopNotesItemRelation
. As we had created a relation between the ShopNote
and Item
in our schema, we need to maintain that while writing the query as well.
We have de-structured the payload to get the required information from the payload. Once we got those, we just called the query method to create a ShopNote
.
Alright, let’s test it out. You can use postman or any other tools of your choice to test it like an API. Here is the screenshot from postman.

Great, we can create a ShopNote
with all the items we want to buy from a shopping mart. What if we want to add an item to an existing ShopNote
? Let us create an API for it. With the knowledge we have so far, it is going to be really quick.
Remember, ShopNote
and Item
are related? So to create an item, we have to mandatorily tell which ShopNote
it is going to be part of. Here is our next serverless function to add an item to an existing ShopNote
.
//add-item.js const query = require("./utils/query"); const ADD_ITEM = ` mutation($ name: String!, $ urgent: Boolean!, $ checked: Boolean!, $ note: ItemNoteRelation!) { createItem(data: {name: $ name, urgent: $ urgent, checked: $ checked, note: $ note}) { _id name urgent checked note { name } } } `; exports.handler = async event => { const { name, urgent, checked, note} = JSON.parse(event.body); const { data, errors } = await query( ADD_ITEM, { name, urgent, checked, note }); if (errors) { return { statusCode: 500, body: JSON.stringify(errors) }; } return { statusCode: 200, body: JSON.stringify({ item: data.createItem }) }; };
We are passing the item properties like, name, if it is urgent, the check value and the note the items should be part of. Let’s see how this API can be called using postman,

As you see, we are passing the id
of the note while creating an item for it.
We won’t bother writing the rest of the API capabilities in this article, like updating, deleting a shop note, updating, deleting items, etc. In case, you are interested, you can look into those functions from the GitHub Repository.
However, after creating the rest of the API, you should have a directory structure like this,

We have successfully created a data store with Fauna, set it up for use, created an API backed by serverless functions, using Netlify Functions, and tested those functions/routes.
Congratulations, you did it. Next, let us build some user interfaces to show the shop notes and add items to it. To do that, we will use Gatsby.js (aka, Gatsby) which is a super cool, React-based static site generator.
The following section requires you to have basic knowledge of ReactJS. If you are new to it, you can learn it from here. If you are familiar with any other user interface technologies like, Angular, Vue, etc feel free to skip the next section and build your own using the APIs explained so far.
Set up the User Interfaces using Gatsby
We can set up a Gatsby project either using the starter projects or initialize it manually. We will build things from scratch to understand it better.
Install gatsby-cli globally.
npm install -g gatsby-cli
Install gatsby, react and react-dom
yarn add gatsby react react-dom
Edit the scripts section of the package.json
file to add a script for develop
.
"scripts": { "develop": "gatsby develop" }
Gatsby projects need a special configuration file called, gatsby-config.js
. Please create a file named, gatsby-config.js
at the root of the project folder with the following content,
module.exports = { // keep it empty }
Let’s create our first page with Gatsby. Create a folder named, src
at the root of the project folder. Create a subfolder named pages
under src
. Create a file named, index.js
under src/pages
with the following content:
import React, { useEffect, useState } from 'react'; export default () => { const [loading, setLoading ] = useState(false); const [shopnotes, setShopnotes] = useState(null); return ( <> <h1>Shopnotes to load here...</h1> </> ) }
Let’s run it. We generally need to use the command gatsby
develop to run the app locally. As we have to run the client side application with netlify functions, we will continue to use, netlify dev
command.
netlify dev
That’s all. Try accessing the page at http://localhost:8888
. You should see something like this,

Gatsby project build creates a couple of output folders which you may not want to push to the source code repository. Let us add a few entries to the .gitignore
file so that we do not get unwanted noise.
Add .cache
, node_modules
and public
to the .gitignore
file. Here is the full content of the file:
.cache public node_modules *.env
At this stage, your project directory structure should match with the following:

Thinking of the UI components
We will create small logical components to achieve the ShopNote
user interface. The components are:
- Header: A header component consists of the Logo, heading and the create button to create a shopnote.
- Shopenotes: This component will contain the list of the shop note (
Note
component). - Note: This is individual notes. Each of the notes will contain one or more items.
- Item: Each of the items. It consists of the item name and actions to add, remove, edit an item.
You can see the sections marked in the picture below:

Install a few more dependencies
We will install a few more dependencies required for the user interfaces to be functional and look better. Open a command prompt at the root of the project folder and install these dependencies,
yarn add bootstrap lodash moment react-bootstrap react-feather shortid
Lets load all the Shop Notes
We will use the Reactjs useEffect
hook to make the API call and update the shopnotes state variables. Here is the code to fetch all the shop notes.
useEffect(() => { axios("/api/get-shopnotes").then(result => { if (result.status !== 200) { console.error("Error loading shopnotes"); console.error(result); return; } setShopnotes(result.data.shopnotes); setLoading(true); }); }, [loading]);
Finally, let us change the return section to use the shopnotes data. Here we are checking if the data is loaded. If so, render the Shopnotes
component by passing the data we have received using the API.
return ( <div className="main"> <Header /> { loading ? <Shopnotes data = { shopnotes } /> : <h1>Loading...</h1> } </div> );
You can find the entire index.js file code from here The index.js
file creates the initial route(/
) for the user interface. It uses other components like, Shopnotes
, Note
and Item
to make the UI fully operational. We will not go to a great length to understand each of these UI components. You can create a folder called components under the src
folder and copy the component files from here.
Finally, the index.css
file
Now we just need a css file to make things look better. Create a file called index.css
under the pages
folder. Copy the content from this CSS file to the index.css
file.
import 'bootstrap/dist/css/bootstrap.min.css'; import './index.css'
That’s all. We are done. You should have the app up and running with all the shop notes created so far. We are not getting into the explanation of each of the actions on items and notes here not to make the article very lengthy. You can find all the code in the GitHub repo. At this stage, the directory structure may look like this,

A small exercise
I have not included the Create Note UI implementation in the GitHib repo. However, we have created the API already. How about you build the front end to add a shopnote? I suggest implementing a button in the header, which when clicked, creates a shopnote using the API we’ve already defined. Give it a try!
Let’s Deploy
All good so far. But there is one issue. We are running the app locally. While productive, it’s not ideal for the public to access. Let’s fix that with a few simple steps.
Make sure to commit all the code changes to the Git repository, say, shopnote. You have an account with Netlify already. Please login and click on the button, New site from Git.

Next, select the relevant Git services where your project source code is pushed. In my case, it is GitHub.

Browse the project and select it.

Provide the configuration details like the build command, publish directory as shown in the image below. Then click on the button to provide advanced configuration information. In this case, we will pass the FAUNA_SERVER_SECRET
key value pair from the .env file. Please copy paste in the respective fields. Click on deploy.

You should see the build successful in a couple of minutes and the site will be live right after that.
In Summary
To summarize:
- The Jamstack is a modern web development architecture based on client-side JavaScript, reusable APIs, and prebuilt Markup.
- 70% – 80% of the features that once required a custom back-end can now be done either on the front end or there are APIs, services to take advantage of.
- Fauna provides the data API for the client-serverless applications. We can use GraphQL or Fauna’s FQL to talk to the store.
- Netlify serverless functions can be easily integrated with Fauna using the GraphQL mutations and queries. This approach may be useful when you have the need of custom authentication built with Netlify functions and a flexible solution like Auth0.
- Gatsby and other static site generators are great contributors to the Jamstack to give a fast end user experience.
Thank you for reading this far! Let’s connect. You can @ me on Twitter (@tapasadhikary) with comments, or feel free to follow.
The post How to create a client-serverless Jamstack app using Netlify, Gatsby and Fauna appeared first on CSS-Tricks.
You can support CSS-Tricks by being an MVP Supporter.
Roll Your Own Comments With Gatsby and FaunaDB
If you haven’t used Gatsby before have a read about why it’s fast in every way that matters, and if you haven’t used FaunaDB before you’re in for a treat. If you’re looking to make your static sites full blown Jamstack applications this is the back end solution for you!
This tutorial will only focus on the operations you need to use FaunaDB to power a comment system for a Gatsby blog. The app comes complete with inputs fields that allow users to comment on your posts and an admin area for you to approve or delete comments before they appear on each post. Authentication is provided by Netlify’s Identity widget and it’s all sewn together using Netlify serverless functions and an Apollo/GraphQL API that pushes data up to a FaunaDB database collection.
I chose FaunaDB for the database for a number of reasons. Firstly there’s a very generous free tier! perfect for those small projects that need a back end, there’s native support for GraphQL queries and it has some really powerful indexing features!
…and to quote the creators;
You can see the finished comments app here.
Get Started
To get started clone the repo at https://github.com/PaulieScanlon/fauna-gatsby-comments
or:
Then install all the dependencies:
Also
cd
in tofunctions/apollo-graphql
and install the dependencies for the Netlify function:This is a separate package and has its own dependencies, you’ll be using this later.
We also need to install the Netlify CLI as you’ll also use this later:
Now lets add three new files that aren’t part of the repo.
At the root of your project create a
.env
.env.development
and.env.production
Add the following to
.env
:Add the following to
.env.development
:Add the following to
.env.production
:You’ll come back to these later but in case you’re wondering
GATSBY_FAUNA_DB
is the FaunaDB secret key for your databaseGATSBY_FAUNA_COLLECTION
is the FaunaDB collection nameGATSBY_SHOW_SIGN_UP
is used to hide the Sign up button when the site is in productionGATSBY_ADMIN_ID
is a user id that Netlify Identity will generate for youIf you’re the curious type you can get a taster of the app by running
gatsby develop
oryarn develop
and then navigate tohttp://localhost:8000
in your browser.FaunaDB
So Let’s get cracking, but before we write any operations head over to https://fauna.com/ and sign up!
Database and Collection
fauna-gatsby-comments
demo-blog-comments
Server Key
Now you’ll need to to set up a server key. Go to SECURITY
fauna-gatsby-comments
for exampledemo-blog-server-key
Environment Variables Pt. 1
Copy the server key and add it to
GATSBY_FAUNA_DB
in.env.development
,.env.production
and.env
.You’ll also need to add the name of the collection to
GATSBY_FAUNA_COLLECTION
in.env.development
,.env.production
and.env
.Adding these values to
.env
are just so you can test your development FaunaDB operations, which you’ll do next.Let’s start by creating a comment so head back to
boop.js
:The breakdown of this function is as follows;
q
is the instance offaunadb.query
Create
is the FaunaDB method to create an entry within a collectionCollection
is area in the database to store the data. It takes the name of the collection as the first argument and a data object as the second.The second argument is the shape of the data you need to drive the applications comment system.
For now you’re going to hard-code
slug
,name
andcomment
but in the final app these values are captured by the input form on the posts page and passed in via argsThe breakdown for the shape is as follows;
isApproved
is the status of the comment and by default it’s false until we approve it in the Admin pageslug
is the path to the post where the comment was writtendate
is the time stamp the comment was writtenname
is the name the user entered in the comments fromcomment
is the comment the user entered in the comments formWhen you (or a user) creates a comment you’re not really interested in dealing with the response because as far as the user is concerned all they’ll see is either a success or error message.
After a user has posted a comment it will go in to your Admin queue until you approve it but if you did want to return something you could surface this in the UI by returning something from the
createComment
function.Create a comment
If you’ve hard coded a
slug
,name
andcomment
you can now run the following in your CLIIf everything worked correctly you should see a log in your terminal of the new comment.
If you head over to COLLECTIONS in FaunaDB you should see your new entry in the collection.
You’ll need to create a few more comments while in development so change the hard-coded values for
name
andcomment
and run the following again.Do this a few times so you end up with at least three new comments stored in the database, you’ll use these in a moment.
Delete comment by id
Now that you can create comments you’ll also need to be able to delete a comment.
By adding the
commentId
of one of the comments you created above you can delete it from the database. ThecommentId
is theid
in theref.@ref
objectAgain you’re not really concerned with the return value here but if you wanted to surface this in the UI you could do so by returning something from the
deleteCommentById
function.The breakdown of this function is as follows
client
is the FaunaDB client instancequery
is a method to get data from FaunaDBq
is the instance offaunadb.query
Delete
is the FaunaDB delete method to delete entries from a collectionRef
is the unique FaunaDB ref used to identify the entryCollection
is area in the database where the data is storedIf you’ve hard coded a
commentId
you can now run the following in your CLI:If you head back over to COLLECTIONS in FaunaDB you should see that entry no longer exists in collection
Indexes
Next you’re going to create an INDEX in FaunaDB.
An INDEX allows you to query the database with a specific term and define a specific data shape to return.
When working with GraphQL and / or TypeScript this is really powerful because you can use FaunaDB indexes to return only the data you need and in a predictable shape. This makes data typing responses in GraphQL and / TypeScript a dream… I’ve worked on a number of applications that just return a massive object of useless values which will inevitably cause bugs in your app. blurg!
get-all-comments
As mentioned above when you query the database using this index you can tell FaunaDB which parts of the entry you want to return.
You can do this by adding “values” but be careful to enter the values exactly as they appear below because (on the FaunaDB free tier) you can’t amend these after you’ve created them so if there’s a mistake you’ll have to delete the index and start again… bummer!
The values you need to add are as follows:
ref
data.isApproved
data.slug
data.date
data.name
data.comment
After you’ve added all the values you can click SAVE.
Get all comments
The breakdown of this function is as follows
client
is the FaunaDB client instancequery
is a method to get data from FaunaDBq
is the instance offaunadb.query
Paginate
paginates the responsesMatch
returns matched resultsIndex
is the name of the Index you just createdThe shape of the returned result here is an array of the same shape you defined in the Index “values”
If you run the following you should see the list of all the comments you created earlier:
Get comments by slug
You’re going to take a similar approach as above but this time create a new Index that allows you to query FaunaDB in a different way. The key difference here is that when you
get-comments-by-slug
you’ll need to tell FaunaDB about this specific term and you can do this by addingdata.slug
to the Terms field.get-comments-by-slug
data.slug
in the terms fieldThe values you need to add are as follows:
ref
data.isApproved
data.slug
data.date
data.name
data.comment
After you’ve added all the values you can click SAVE.
The breakdown of this function is as follows:
client
is the FaunaDB client instancequery
is a method to get data from FaunaDBq
is the instance offaunadb.query
Paginate
paginates the responsesMatch
returns matched resultsIndex
is the name of the Index you just createdThe shape of the returned result here is an array of the same shape you defined in the Index “values” you can create this shape in the same way you did above and be sure to add a value for terms. Again be careful to enter these with care.
If you run the following you should see the list of all the comments you created earlier but for a specific
slug
:Approve comment by id
When you create a comment you manually set the
isApproved
value to false. This prevents the comment from being shown in the app until you approve it.You’ll now need to create a function to do this but you’ll need to hard-code a
commentId
. Use acommentId
from one of the comments you created earlier:The breakdown of this function is as follows:
client
is the FaunaDB client instancequery
is a method to get data from FaunaDBq
is the instance offaunadb.query
Update
is the FaundaDB method up update an entryRef
is the unique FaunaDB ref used to identify the entryCollection
is area in the database where the data is storedIf you’ve hard coded a
commentId
you can now run the following in your CLI:If you run the
getCommentsBySlug
again you should now see theisApproved
status of the entry you hard-coded thecommentId
for will have changed totrue
.These are all the operations required to manage the data from the app.
In the repo if you have a look at
apollo-graphql.js
which can be found infunctions/apollo-graphql
you’ll see the all of the above operations. As mentioned before the hard-coded values are replaced byargs
, these are the values passed in from various parts of the app.Netlify
Assuming you’ve completed the Netlify sign up process or already have an account with Netlify you can now push the demo app to your GitHub account.
To do this you’ll need to have initialize git locally, added a remote and have pushed the demo repo upstream before proceeding.
You should now be able to link the repo up to Netlify’s Continuous Deployment.
If you click the “New site from Git” button on the Netlify dashboard you can authorize access to your GitHub account and select the
gatsby-fauna-comments
repo to enable Netlify’s Continuous Deployment. You’ll need to have deployed at least once so that we have a pubic URL of your app.The URL will look something like this
https://ecstatic-lewin-b1bd17.netlify.app
but feel free to rename it and make a note of the URL as you’ll need it for the Netlify Identity step mentioned shortly.Environment Variables Pt. 2
In a previous step you added the FaunaDB database secret key and collection name to your
.env
files(s). You’ll also need to add the same to Netlify’s Environment variables.Proceed to add the following:
While you’re here you’ll also need to amend the Sensitive variable policy, select Deploy without restrictions
Netlify Identity Widget
I mentioned before that when a comment is created the
isApproved
value is set tofalse
, this prevents comments from appearing on blog posts until you (the admin) have approved them. In order to become admin you’ll need to create an identity.You can achieve this by using the Netlify Identity Widget.
If you’ve completed the Continuous Deployment step above you can navigate to the Identity page from the Netlify navigation.
You wont see any users in here just yet so lets use the app to connect the dots, but before you do that make sure you click Enable Identity
Before you continue I just want to point out you’ll be using
netlify dev
instead ofgatsby develop
oryarn develop
from now on. This is because you’ll be using some “special” Netlify methods in the app and staring the server usingnetlify dev
is required to spin up various processes you’ll be using.netlify dev
http://localhost:8888/admin/
You will also need to point the Netlify Identity widget at your newly deployed app URL. This was the URL I mentioned you’ll need to make a note of earlier, if you’ve not renamed your app it’ll look something like this
https://ecstatic-lewin-b1bd17.netlify.app/
There will be a prompt in the pop up window to Set site’s URL.You can now complete the necessary sign up steps.
After sign up you’ll get an email asking you to confirm you identity and once that’s completed refresh the Identity page in Netlify and you should see yourself as a user.
It’s now login time, but before you do this find
Identity.js
insrc/components
and temporarily un-comment theconsole.log()
on line 14. This will log the Netlify Identity user object to the console.netlify dev
If this all works you should be able to see a console log for
netlifyIdentity.currentUser:
find theid
key and copy the value.Set this as the value for
GATSBY_ADMIN_ID =
in both.env.production
and.env.development
You can now safely remove the
console.log()
on line 14 inIdentity.js
or just comment it out again.…and finally
netlify dev
Now you should be able to login as “Admin”… hooray!
Navigate to
http://localhost:8888/admin/
and Login.It’s important to note here you’ll be using
localhost:8888
for development now and NOTlocalhost:8000
which is more common with Gatsby developmentBefore you test this in the deployed environment make sure you go back to Netlify’s Environment variables and add your Netlify Identity user
id
to the Environment variables!Proceed to add the following:
If you have a play around with the app and enter a few comments on each of the posts then navigate back to Admin page you can choose to either approve or delete the comments.
Naturally only approved comments will be displayed on any given post and deleted ones are gone forever.
If you’ve used this tutorial for your project I’d love to hear from you at @pauliescanlon.
By Paulie Scanlon (@pauliescanlon), Front End React UI Developer / UX Engineer: After all is said and done, structure + order = fun.
Visit Paulie’s Blog at: www.paulie.dev
The post Roll Your Own Comments With Gatsby and FaunaDB appeared first on CSS-Tricks.
CSS-Tricks