Thursday, May 21, 2020

Consuming REST APIs In React With Fetch And Axios

Consuming REST APIs in a React Application can be done in various ways, but in this tutorial, we will be discussing how we can consume REST APIs using two of the most popular methods known as Axios (a promise-based HTTP client) and Fetch API (a browser in-built web API). I will discuss and implement each of these methods in detail and shed light on some of the cool features each of them have to offer.
APIs are what we can use to supercharge our React applications with data. There are certain operations that can’t be done on the client-side, so these operations are implemented on the server-side. We can then use the APIs to consume the data on the client-side.
APIs consist of a set of data, that is often in JSON format with specified endpoints. When we access data from an API, we want to access specific endpoints within that API framework. We can also say that an API is a contractual agreement between two services over the shape of request and response. The code is just a byproduct. It also contains the terms of this data exchange.
In React, there are various ways we can consume REST APIs in our applications, these ways include using the JavaScript inbuilt fetch() method and Axios which is a promise-based HTTP client for the browser and Node.js.
Note: A good knowledge of ReactJS, React Hooks, JavaScript and CSS will come in handy as you work your way throughout this tutorial.
Let’s get started with learning more about the REST API.

What Is A REST API

A REST API is an API that follows what is structured in accordance with the REST Structure for APIs. REST stands for “Representational State Transfer”. It consists of various rules that developers follow when creating APIs.

The Benefits Of REST APIs

  1. Very easy to learn and understand;
  2. It provides developers with the ability to organize complicated applications into simple resources;
  3. It easy for external clients to build on your REST API without any complications;
  4. It is very easy to scale;
  5. A REST API is not language or platform-specific, but can be consumed with any language or run on any platform.

An Example Of A REST API Response

The way a REST API is structured depends on the product it’s been made for — but the rules of REST must be followed.
The sample response below is from the Github Open API. We’ll be using this API to build a React app later on in this tutorial.


{
"login": "bktivist123",
"id": 26572907,
"node_id": "MDQ6VXNlcjI2NTcyOTA3",
"avatar_url": "https://avatars3.githubusercontent.com/u/26572907?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/hacktivist123",
"html_url": "https://github.com/hacktivist123",
"followers_url": "https://api.github.com/users/bktivist123/followers",
"following_url": "https://api.github.com/users/bktivist123/following{/other_user}",
"gists_url": "https://api.github.com/users/bktivist123/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bktivist123/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bktivist123/subscriptions",
"organizations_url": "https://api.github.com/users/bktivist123/orgs",
"repos_url": "https://api.github.com/users/bktivist123/repos",
"events_url": "https://api.github.com/users/bktivist123/events{/privacy}",
"received_events_url": "https://api.github.com/users/bktivist123/received_events",
"type": "User",
"site_admin": false,
"name": "Shesh",
"company": null,
"blog": "https://shessh.xyz",
"location": "Lagos, NN",
"email": null,
"hireable": true,
"bio": "☕ Software Engineer | | Developer Advocate🥑|| ❤ Everything JavaScript",
"public_repos": 68,
"public_gists": 1,
"followers": 130,
"following": 246,
"created_at": "2017-03-21T12:55:48Z",
"updated_at": "2020-05-11T13:02:57Z"
}


The response above is from the Github REST API when I make a GET request to the following endpoint https://api.github.com/users/bktivist123. It returns all the stored data about a user called hacktivist123. With this response, we can decide to render it whichever way we like in our React app.

Consuming APIs Using The Fetch API

The fetch() API is an inbuilt JavaScript method for getting resources from a server or an API endpoint. It’s similar to XMLHttpRequest, but the fetch API provides a more powerful and flexible feature set.
It defines concepts such as CORS and the HTTP Origin header semantics, supplanting their separate definitions elsewhere.
The fetch() API method always takes in a compulsory argument, which is the path or URL to the resource you want to fetch. It returns a promise that points to the response from the request, whether the request is successful or not. You can also optionally pass in an init options object as the second argument.
Once a response has been fetched, there are several inbuilt methods available to define what the body content is and how it should be handled.

The Difference Between The Fetch API And jQuery Ajax

The Fetch API is different from jQuery Ajax in three main ways, which are:
  1. The promise returned from a fetch() request will not reject when there’s an HTTP error, no matter the nature of the response status. Instead, it will resolve the request normally, if the response status code is a 400 or 500 type code, it’ll set the ok status. A request will only be rejected either because of network failure or if something is preventing the request from completing
  2. fetch() will not allow the use of cross-site cookies i.e you cannot carry out a cross-site session using fetch()
  3. fetch() will also not send cookies by default unless you set the credentials in the init option.

Parameters For The Fetch API

  • resource
    This is the path to the resource you want to fetch, this can either be a direct link to the resource path or a request object
  • init
    This is an object containing any custom setting or credentials you’ll like to provide for your fetch() request. The following are a few of the possible options that can be contained in the init object:
    • method
      This is for specifying the HTTP request method e.g GET, POST, etc.
    • headers
      This is for specifying any headers you would like to add to your request, usually contained in an object or an object literal.
    • body
      This is for specifying a body that you want to add to your request: this can be a Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object
    • mode
      This is for specifying the mode you want to use for the request, e.g., cors, no-cors, or same-origin.
    • credentials
      This for specifying the request credentials you want to use for the request, this option must be provided if you consider sending cookies automatically for the current domain.

Basic Syntax for Using the Fetch() API

A basic fetch request is really simple to write, take a look at the following code:
fetch('https://api.github.com/users/bktivist123/repos')
  .then(response => response.json())
  .then(data => console.log(data));
In the code above, we are fetching data from a URL that returns data as JSON and then printing it to the console. The simplest form of using fetch() often takes just one argument which is the path to the resource you want to fetch and then return a promise containing the response from the fetch request. This response is an object.
The response is just a regular HTTP response and not the actual JSON. In other to get the JSON body content from the response, we’d have to change the response to actual JSON using the json() method on the response.

Using Fetch API In React Apps

Using the Fetch API in React Apps is the normal way we’d use the Fetch API in javascript, there is no change in syntax, the only issue is deciding where to make the fetch request in our React app. Most fetch requests or any HTTP request of any sort is usually done in a React Component.
This request can either be made inside a Lifecycle Method if your component is a Class Component or inside a useEffect() React Hook if your component is a Functional Component.
For example, In the code below, we will make a fetch request inside a class component, which means we’ll have to do it inside a lifecycle method. In this particular case, our fetch request will be made inside a componentDidMount lifecycle method because we want to make the request just after our React Component has mounted.
import React from 'react';

class myComponent extends React.Component {
  componentDidMount() {
    const apiUrl = 'https://api.github.com/users/bktivist123/repos';
    fetch(apiUrl)
      .then((response) => response.json())
      .then((data) => console.log('This is your data', data));
  }
  render() {
    return <h1>my Component has Mounted, Check the browser 'console' </h1>;
  }
}
export default myComponent;
In the code above, we are creating a very simple class component that makes a fetch request that logs the final data from the fetch request we have made to the API URL into the browser console after the React component has finished mounting.
The fetch() method takes in the path to the resource we want to fetch, which is assigned to a variable called apiUrl. After the fetch request has been completed it returns a promise that contains a response object. Then, we are extracting the JSON body content from the response using the json() method, finally we log the final data from the promise into the console.


Let’s Consume A REST API With Fetch Method

In this section, we will be building a simple react application that consumes an external API, we will be using the Fetch method to consume the API.
The simple application will display all the repositories and their description that belongs to a particular user. For this tutorial, I’ll be using my GitHub username, you can also use yours if you wish.
The first thing we need to do is to generate our React app by using create-react-app:
npx create-react-app myRepos
The command above will bootstrap a new React app for us. As soon as our new app has been created, all that’s left to do is to run the following command and begin coding:
npm start
If our React is created properly we should see this in our browser window when we navigate to localhost:3000 after running the above command.
In your src folder, create a new folder called component. This folder will hold all of our React components. In the new folder, create two files titled List.js and withListLoading.js. These two files will hold the components that will be needed in our app.
The List.js file will handle the display of our Repositories in the form of a list, and the withListLoading.js file will hold a higher-order component that will be displayed when the Fetch request we will be making is still ongoing.
In the List.js file we created inside the components folder, let’s paste in the following code:
import React from 'react';
const List = (props) => {
  const { repos } = props;
  if (!repos || repos.length === 0) return <p>No repos, sorry</p>;
  return (
    <ul>
      <h2 className='list-head'>Available Public Repositories</h2>
      {repos.map((repo) => {
        return (
          <li key={repo.id} className='list'>
            <span className='repo-text'>{repo.name} </span>
            <span className='repo-description'>{repo.description}</span>
          </li>
        );
      })}
    </ul>
  );
};
export default List;
The code above is a basic React list component that would display the data, in this case, the repositories name and their descriptions in a list.
Now, Let me explain the code bit by bit.
const { repos } = props;
We are initializing a prop for the component called repos.
if (repos.length === 0 || !repos) return <p>No repos, sorry</p>;
Here, all we are doing is making a conditional statement that will render a message when the length of the repos we get from the request we make is equal to zero.
return (
    <ul>
      <h2 className='list-head'>Available Public Repositories</h2>
      {repos.map((repo) => {
        return (
          <li key={repo.id} className='list'>
            <span className='repo-text'>{repo.name} </span>
            <span className='repo-description'>{repo.description}</span>
          </li>
        );
      })}
    </ul>
  );
Here, we are mapping through each of the repositories that will be provided by the API request we make and extracting each of the repositories names and their descriptions then we are displaying each of them in a list.
export default List;
Here we are exporting our List component so that we can use it somewhere else.
In the withListLoading.js file we created inside the components folder, let’s paste in the following code:
import React from 'react';

function WithListLoading(Component) {
  return function WihLoadingComponent({ isLoading, ...props }) {
    if (!isLoading) return <Component {...props} />;
    return (
      <p style={{ textAlign: 'center', fontSize: '30px' }}>
        Hold on, fetching data may take some time :)
      </p>
    );
  };
}
export default WithListLoading;
The code above is a higher-order React component that takes in another component and then returns some logic. In our case, our higher component will wait to check if the current isLoading state of the component it takes is true or false. If the current isLoading state is true, it will display a message Hold on, fetching data may take some time :). Immediately the isLoading state changes to false it’ll render the component it took in. In our case, it’ll render the List component.
In your *App.js file inside the src folder, let’s paste in the following code:
import React, { useEffect, useState } from 'react';
import './App.css';
import List from './components/List';
import withListLoading from './components/withListLoading';
function App() {
  const ListLoading = withListLoading(List);
  const [appState, setAppState] = useState({
    loading: false,
    repos: null,
  });

  useEffect(() => {
    setAppState({ loading: true });
    const apiUrl = `https://api.github.com/users/bktivist123/repos`;
    fetch(apiUrl)
      .then((res) => res.json())
      .then((repos) => {
        setAppState({ loading: false, repos: repos });
      });
  }, [setAppState]);
  return (
    <div className='App'>
      <div className='container'>
        <h1>My Repositories</h1>
      </div>
      <div className='repo-container'>
        <ListLoading isLoading={appState.loading} repos={appState.repos} />
      </div>
      <footer>
        <div className='footer'>
          Built{' '}
          <span role='img' aria-label='love'>
            💚
          </span>{' '}
          with by Shedrack Akintayo
        </div>
      </footer>
    </div>
  );
}
export default App;
Our App.js is a functional component that makes use of React Hooks for handling state and also side effects. If you’re not familiar with React Hooks, read my Getting Started with React Hooks Guide.
Let me explain the code above bit by bit.
import React, { useEffect, useState } from 'react';
import './App.css';
import List from './components/List';
import withListLoading from './components/withListLoading';
Here, we are importing all the external files we need and also the components we created in our components folder. We are also importing the React Hooks we need from React.
const ListLoading = withListLoading(List);
  const [appState, setAppState] = useState({
    loading: false,
    repos: null,
  });
Here, we are creating a new component called ListLoading and assigning our withListLoading higher-order component wrapped around our list component. We are then creating our state values loading and repos using the useState() React Hook.
useEffect(() => {
    setAppState({ loading: true });
    const user = `https://api.github.com/users/bktivist123/repos`;
    fetch(user)
      .then((res) => res.json())
      .then((repos) => {
        setAppState({ loading: false, repos: repos });
      });
  }, [setAppState]);
Here, we are initializing a useEffect() React Hook. In the useEffect() hook, we are setting our initial loading state to true, while this is true, our higher-order component will display a message. We are then creating a constant variable called user and assigning the API URL we’ll be getting the repositories data from.
We are then making a basic fetch() request like we discussed above and then after the request is done we are setting the app loading state to false and populating the repos state with the data we got from the request.
return (
    <div className='App'>
      <div className='container'>
        <h1>My Repositories</h1>
      </div>
      <div className='repo-container'>
        <ListLoading isLoading={AppState.loading} repos={AppState.repos} />
      </div>
    </div>
  );
}
export default App;
Here we are basically just rendering the Component we assigned our higher-order component to and also filling the isLoading prop and repos prop with their state value.
Now, we should see this in our browser, when the fetch request is still being made, courtesy of our withListLoading higher-order component:

Now, let’s style our project a little bit, in your App.css file, copy and paste this code.
@import url('https://fonts.googleapis.com/css2?family=Amiri&display=swap');
:root {
  --basic-color: #23cc71;
}
.App {
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  font-family: 'Amiri', serif;
  overflow: hidden;
}
.container {
  display: flex;
  flex-direction: row;
}
.container h1 {
  font-size: 60px;
  text-align: center;
  color: var(--basic-color);
}
.repo-container {
  width: 50%;
  height: 700px;
  margin: 50px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  overflow: scroll;
}
@media screen and (max-width: 600px) {
  .repo-container {
    width: 100%;
    margin: 0;
    box-shadow: none;
  }
}
.repo-text {
  font-weight: 600;
}
.repo-description {
  font-weight: 600;
  font-style: bold;
  color: var(--basic-color);
}
.list-head {
  text-align: center;
  font-weight: 800;
  text-transform: uppercase;
}
.footer {
  font-size: 15px;
  font-weight: 600;
}
.list {
  list-style: circle;
}
So in the code above, we are styling our app to look more pleasing to the eyes, we have assigned various class names to each element in our App.js file and thus we are using these class names to style our app.

Now our app looks much better. 😊
So that’s how we can use the Fetch API to consume a REST API. In the next section, we’ll be discussing Axios and how we can use it to consume the same API in the same App.

Consuming APIs With Axios

Axios is an easy to use promise-based HTTP client for the browser and node.js. Since Axios is promise-based, we can take advantage of async and await for more readable and asynchronous code. With Axios, we get the ability to intercept and cancel request, it also has a built-in feature that provides client-side protection against cross-site request forgery.

Features Of Axios

  • Request and response interception
  • Streamlined error handling
  • Protection against XSRF
  • Support for upload progress
  • Response timeout
  • The ability to cancel requests
  • Support for older browsers
  • Automatic JSON data transformation

Making Requests With Axios

Making HTTP Requests with Axios is quite easy. The code below is basically how to make an HTTP request.
// Make a GET request
axios({
  method: 'get',
  url: 'https://api.github.com/users/bktivist123',
});

// Make a Post Request
axios({
  method: 'post',
  url: '/login',
  data: {
    firstName: 'ssss',
    lastName: 'aaaaa'
  }
});
The code above shows the basic ways we can make a GET and POST HTTP request with Axios.
Axios also provides a set of shorthand method for performing different HTTP requests. The Methods are as follows:
  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.options(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])
For example, if we want to make a similar request like the example code above but with the shorthand methods we can do it like so:
// Make a GET request with a shorthand method
axios.get('https://api.github.com/users/bktivist123');

// Make a Post Request with a shorthand method
axios.post('/signup', {
    firstName: 'ssssss',
    lastName: 'aaaaaa'
});
In the code above, we are making the same request as what we did above but this time with the shorthand method. Axios provides flexibility and makes your HTTP requests even more readable.

Making Multiple Requests With Axios

Axios provides developers the ability to make and handle simultaneous HTTP requests using the axios.all() method. This method takes in an array of arguments and it returns a single promise object that resolves only when all arguments passed in the array have resolved.
For example, we can make multiple requests to the GitHub api using the axios.all() method like so:
axios.all([
  axios.get('https://api.github.com/users/bktivist123'),
  axios.get('https://api.github.com/users/aaaaaa')
])
.then(response => {
  console.log('Date created: ', response[0].data.created_at);
  console.log('Date created: ', response[1].data.created_at);
});
The code above makes simultaneous requests to an array of arguments in parallel and returns the response data, in our case, it will log to the console the created_at object from each of the API responses.

Let’s Consume A REST API With Axios Client

In this section, all we’ll be doing is replacing fetch() method with Axios in our existing React Application. All we need to do is to install Axios and then use it in our App.js file for making the HTTP request to the GitHub API.
Now let’s install Axios in our React app by running either of the following:
With NPM:
npm install axios
With Yarn:
yarn add axios
After installation is complete, we have to import axios into our App.js. In our App.js we’ll add the following line to the top of our App.js file:
import axios from 'axios'
After adding the line of code our App.js all we have to do inside our useEffect() is to write the following code:
useEffect(() => {
    setAppState({ loading: true });
    const apiUrl = 'https://api.github.com/users/bktivist123/repos';
    axios.get(apiUrl).then((repos) => {
      const allRepos = repos.data;
      setAppState({ loading: false, repos: allRepos });
    });
  }, [setAppState]);
You may have noticed that we have now replaced the fetch API with the Axios shorthand method axios.get to make a get request to the API.
axios.get(apiUrl).then((repos) => {
      const allRepos = repos.data;
      setAppState({ loading: false, repos: allRepos });
    });
In this block of code, we are making a GET request then we are returning a promise that contains the repos data and assigning the data to a constant variable called allRepos. We are then setting the current loading state to false and also passing the data from the request to the repos state variable.
If we did everything correctly, we should see our app still render the same way without any change.

So this is how we can use Axios client to consume a REST API.

Fetch vs Axios

In this section, I will be listing our certain features and then I’ll talk about how well Fetch and Axios support these features.
  1. Basic Syntax
    Both Fetch and Axios have very simple syntaxes for making requests. But Axios has an upper hand because Axios automatically converts a response to JSON, so when using Axios we skip the step of converting the response to JSON, unlike Fetch() where we’d still have to convert the response to JSON. Lastly, Axios shorthand methods allow us to make specific HTTP Requests easier.
  2. Browser Compatibility
    One of the many reasons why developers would prefer Axios over Fetch is because Axios is supported across major browsers and versions unlike Fetch that is only supported in Chrome 42+, Firefox 39+, Edge 14+, and Safari 10.1+.
  3. Handling Response Timeout
    Setting a timeout for responses is very easy to do in Axios by making use of the timeout option inside the request object. But in Fetch, it is not that easy to do this. Fetch provides a similar feature by using the AbortController() interface but it takes more time to implement and can get confusing.
  4. Intercepting HTTP Requests
    Axios allows developers to intercept HTTP requests. HTTP interceptors are needed when we need to change HTTP requests from our application to the server. Interceptors give us the ability to do that without having to write extra code.
  5. Making Multiple Requests Simultaneously
    Axios allows us to make multiple HTTP requests with the use of the axios.all() method ( I talked about this above). fetch() provides the same feature with the use of the promise.all() method, we can make multiple fetch() requests inside it.

Conclusion

Axios and fetch() are all great ways of consuming APIs but I advise you to use fetch() when building relatively small applications and make use of Axios when building large applications for scalability reasons. I hope you enjoyed working through this tutorial, you could always read more on Consuming REST APIs with either Fetch or Axios from the references below. If you have any questions, you can leave it in the comments section below and I’ll be happy to answer every single one.








Tuesday, May 19, 2020

From Static Sites To End User JAMstack Apps With FaunaDB

To make the move from “site” to app, we’ll need to dive into the world of “app-generated” content. In this article, we’ll get started in this world with the power of serverless data. We’ll start with a simple demo by ingesting and posting data to FaunaDB and then extend that functionality in a full-fledged application using Auth0, FaunaDB’s Token system and User-Defined Functions.


The JAMstack has proven itself to be one of the top ways of producing content-driven sites, but it’s also a great place to house applications, as well. If you’ve been using the JAMstack for your performant websites, the demos in this article will help you extend those philosophies to applications as well.
When using the JAMstack to build applications, you need a data service that fits into the most important aspects of the JAMstack philosophy:
  • Global distribution
  • Zero operational needs
  • A developer-friendly API.
In the JAMstack ecosystem there are plenty of software-as-a-service companies that provide ways of getting and storing specific types of data. Whether you want to send emails, SMS or make phone calls (Twilio) or accept form submissions efficiently (Formspree, Formingo, Formstack, etc.), it seems there’s an API for almost everything.
These are great services that can do a lot of the low-level work of many applications, but once your data is more complex than a spreadsheet or needs to be updated and store in real-time, it might be time to look into a database.
The service API can still be in use, but a central database managing the state and operations of your app becomes much more important. Even if you need a database, you still want it to follow the core JAMstack philosophies we outlined above. That means, we don’t want to host our own database server. We need a Database-as-a-Service solution. Our database needs to be optimized for the JAMstack:
  • Optimized for API calls from a browser or build process.
  • Flexible to model your data in the specific ways your app needs.
  • Global distribution of our data like a CDN houses our sites.
  • Hands-free scaling with no need of a database administrator or developer intervention.
Whatever service you look into needs to follow these tenets of serverless data. In our demos, we’ll explore FaunaDB, a global serverless database, featuring native GraphQL to assure that we keep our apps in step with the philosophies of the JAMstack.
Let’s dive into the code!

A JAMstack Guestbook App With Gatsby And Fauna

I’m a big fan of reimagining the internet tools and concepts of the 1990s and early 2000s. We can take these concepts and make them feel fresh with the new set of tools and interactions.

In this demo, we’ll create an application that was all the rage in that time period: the guestbook. A guestbook is nothing but app-generated content and interaction. A user can come to the site, see all the signatures of past “guests” and then leave their own.
To start, we’ll statically render our site and build our data from Fauna during our build step. This will provide the fast performance we expect from a JAMstack site. To do this, we’ll use GatsbyJS.

Initial setup

Our first step will be to install Gatsby globally on our computer. If you’ve never spent much time in the command line, Gatsby’s “part 0” tutorial will help you get up and running. If you already have Node and NPM installed, you’ll install the Gatsby CLI globally and create a new site with it using the following commands:
npm install -g gatsby-cli
gatsby new <directory-to-install-into> <starter>
Gatsby comes with a large repository of starters that can help bootstrap your project. For this demo, I chose a simple starter that came equipped with the Bulma CSS framework.
gatsby new guestbook-app https://github.com/amandeepmittal/gatsby-bulma-quickstart
This gives us a good starting point and structure. It also has the added benefit of coming with styles that are ready to go.
Let’s do a little cleanup for things we don’t need. We’ll start by simplifying our components.header.js
import React from 'react';

import './style.scss';

const Header = ({ siteTitle }) => (
  <section className="hero gradientBg ">
    <div className="hero-body">
      <div className="container container--small center">
        <div className="content">
          <h1 className="is-uppercase is-size-1 has-text-white">
            Sign our Virtual Guestbook
          </h1>
          <p className="subtitle has-text-white is-size-3">
            If you like all the things that we do, be sure to sign our virtual guestbook
          </p>
        </div>
      </div>
    </div>
  </section>
);

export default Header;
This will get rid of much of the branded content. Feel free to customize this section, but we won’t write any of our code here.
Next we’ll clean out the components/midsection.js file. This will be where our app’s code will render.
import React, { useState } from 'react';
import Signatures from './signatures';
import SignForm from './sign-form';


const Midsection = () => {

    const [sigData, setSigData] = useState(data.allSignatures.nodes);
    return (
        <section className="section">
            <div className="container container--small">
                <section className="section is-small">
                    <h2 className="title is-4">Sign here</h2>
                    <SignForm></SignForm>
                </section>

                <section className="section">
                    <h2 className="title is-5">View Signatures</h2>
                    <Signatures></Signatures>
                </section>
            </div>
        </section>
    )
}

export default Midsection;
In this code, we’ve mostly removed the “site” content and added in a couple new components. A <SignForm> that will contain our form for submitting a signature and a <Signatures> component to contain the list of signatures.
Now that we have a relatively blank slate, we can set up our FaunaDB database.

Setting Up A FaunaDB Collection

After logging into Fauna (or signing up for an account), you’ll be given the option to create a new Database. We’ll create a new database called guestbook.

Inside this database, we’ll create a “Collection” called signature. Collections in Fauna a group of Documents that are in turn JSON objects.
In this new Collection, we’ll create a new Document with the following JSON:
{
 name: "Bryan Robinson",
 message:
   "Lorem ipsum dolor amet sum Lorem ipsum dolor amet sum Lorem ipsum dolor amet sum Lorem ipsum dolor amet sum"
}
This will be the simple data schema for each of our signatures. For each of these Documents, Fauna will create additional data surrounding it.
{
 "ref": Ref(Collection("signatures"), "262884172900598291"),
 "ts": 1586964733980000,
 "data": {
   "name": "Bryan Robinson",
   "message": "Lorem ipsum dolor amet sum Lorem ipsum dolor amet sum Lorem ipsum dolor amet sum Lorem ipsum dolor amet sum "
 }
}
The ref is the unique identifier inside of Fauna and the ts is the time (as a Unix timestamp) the document was created/updated.
After creating our data, we want an easy way to grab all that data and use it in our site. In Fauna, the most efficient way to get data is via an Index. We’ll create an Index called allSignatures. This will grab and return all of our signature Documents in the Collection.
Now that we have an efficient way of accessing the data in Gatsby, we need Gatsby to know where to get it. Gatsby has a repository of plugins that can fetch data from a variety of sources, Fauna included.

Setting up the Fauna Gatsby Data Source Plugin

npm install gatsby-source-faunadb
After we install this plugin to our project, we need to configure it in our gatsby-config.js file. In the plugins array of our project, we’ll add a new item.
{
    resolve: `gatsby-source-faunadb`,
    options: {
    // The secret for the key you're using to connect to your Fauna database.
    // You can generate on of these in the "Security" tab of your Fauna Console.
        secret: process.env.YOUR_FAUNADB_SECRET,
    // The name of the index you want to query
    // You can create an index in the "Indexes" tab of your Fauna Console.
        index: `allSignatures`,
    // This is the name under which your data will appear in Gatsby GraphQL queries
    // The following will create queries called `allBird` and `bird`.
        type: "Signatures",
    // If you need to limit the number of documents returned, you can specify a 
    // Optional maximum number to read.
    // size: 100
    },
},
In this configuration, you provide it your Fauna secret Key, the Index name we created and the “type” we want to access in our Gatsby GraphQL query.
Where did that process.env.YOUR_FAUNADB_SECRET come from?
In your project, create a .env file — and include that file in your .gitignore! This file will give Gatsby’s Webpack configuration the secret value. This will keep your sensitive information safe and not stored in GitHub.
YOUR_FAUNADB_SECRET = "value from fauna"
We can then head over to the “Security” tab in our Database and create a new key. Since this is a protected secret, it’s safe to use a “Server” role. When you save the Key, it’ll provide your secret. Be sure to grab that now, as you can’t get it again (without recreating the Key).
Once the configuration is set up, we can write a GraphQL query in our components to grab the data at build time.

Getting the data and building the template

We’ll add this query to our Midsection component to make it accessible by both of our components.
const Midsection = () => {
 const data = useStaticQuery(
 graphql`
            query GetSignatures {
                allSignatures {
                  nodes {
                    name
                    message
                    _ts
                    _id
                  }
                }
            }`
        );
// ... rest of the component
}
This will access the Signatures type we created in the configuration. It will grab all the signatures and provide an array of nodes. Those nodes will contain the data we specify we need: name, message, ts, id.
We’ll set that data into our state — this will make updating it live easier later.
const [sigData, setSigData] = useState(data.allSignatures.nodes);
Now we can pass sigData as a prop into <Signatures> and setSigData into <SignForm>.
<SignForm setSigData={setSigData}></SignForm>


<Signatures sigData={sigData}></Signatures>
Let’s set up our Signatures component to use that data!
import React from 'react';
import Signature from './signature'   

const Signatures = (props) => {
    const SignatureMarkup = () => {
        return props.sigData.map((signature, index) => {
            return (
                <Signature key={index} signature={signature}></Signature>
            )
        }).reverse()
    }

    return (
        <SignatureMarkup></SignatureMarkup>
    )
}

export default Signatures
In this function, we’ll .map() over our signature data and create an Array of markup based on a new <Signature> component that we pass the data into.
The Signature component will handle formatting our data and returning an appropriate set of HTML.
import React from 'react';

const Signature = ({signature}) => {
    const dateObj = new Date(signature._ts / 1000);
    let dateString = `${dateObj.toLocaleString('default', {weekday: 'long'})}, ${dateObj.toLocaleString('default', { month: 'long' })} ${dateObj.getDate()} at ${dateObj.toLocaleTimeString('default', {hour: '2-digit',minute: '2-digit', hour12: false})}`

    return (
    <article className="signature box">      
        <h3 className="signature__headline">{signature.name} - {dateString}</h3>
        <p className="signature__message">
            {signature.message} 
        </p>
    </article>
)};

export default Signature;
At this point, if you start your Gatsby development server, you should have a list of signatures currently existing in your database. Run the following command to get up and running:
gatsby develop
Any signature stored in our database will build HTML in that component. But how can we get signatures INTO our database?
Let’s set up a signature form component to send data and update our Signatures list.

Let’s Make Our JAMstack Guestbook Interactive

First, we’ll set up the basic structure for our component. It will render a simple form onto the page with a text input, a textarea, and a button for submission.
import React from 'react';

import faunadb, { query as q } from "faunadb"

var client = new faunadb.Client({ secret: process.env.GATSBY_FAUNA_CLIENT_SECRET  })

export default class SignForm extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            sigName: "",
            sigMessage: ""
        }
    }

    handleSubmit = async event => {
        // Handle the submission
    }

    handleInputChange = event => {
        // When an input changes, update the state
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <div className="field">
                    <div className="control">
                 <label className="label">Label
                    <input 
                        className="input is-fullwidth"
                        name="sigName" 
                        type="text"
                        value={this.state.sigName}
                        onChange={this.handleInputChange}
                    />
                    </label>
                    </div>
                </div>
                <div className="field">
                    <label>
                        Your Message:
                        <textarea 
                            rows="5"
                            name="sigMessage" 
                            value={this.state.sigMessage}
                            onChange={this.handleInputChange} 
                            className="textarea" 
                            placeholder="Leave us a happy note"></textarea>

                    </label>
                </div>
                <div className="buttons">
                    <button className="button is-primary" type="submit">Sign the Guestbook</button>
                </div>
            </form>
        )
    }

}
To start, we’ll set up our state to include the name and the message. We’ll default them to blank strings and insert them into our <textarea> and <input>.
When a user changes the value of one of these fields, we’ll use the handleInputChange method. When a user submits the form, we’ll use the handleSubmit method.
Let’s break down both of those functions.
  handleInputChange = event => {
 const target = event.target
 const value = target.value
 const name = target.name
 this.setState({
            [name]: value,
        })
    }
The input change will accept the event. From that event, it will get the current target’s value and name. We can then modify the state of the properties on our state object — sigName, sigMessage or anything else.
Once the state has changed, we can use the state in our handleSubmit method.
  handleSubmit = async event => {
        event.preventDefault();
 const placeSig = await this.createSignature(this.state.sigName, this.state.sigMessage);
 this.addSignature(placeSig);
    }
This function will call a new createSignature() method. This will connect to Fauna to create a new Document from our state items.
The addSignature() method will update our Signatures list data with the response we get back from Fauna.
In order to write to our database, we’ll need to set up a new key in Fauna with minimal permissions. Our server key is allowed higher permissions because it’s only used during build and won’t be visible in our source.
This key needs to only allow for the ability to only create new items in our signatures collection.
Note: A user could still be malicious with this key, but they can only do as much damage as a bot submitting that form, so it’s a trade-off I’m willing to make for this app.

For this, we’ll create a new “Role” in the “Security” tab of our dashboard. We can add permissions around one or more of our Collections. In this demo, we only need signatures and we can select the “Create” functionality.
After that, we generate a new key that uses that role.
To use this key, we’ll instantiate a new version of the Fauna JavaScript SDK. This is a dependency of the Gatsby plugin we installed, so we already have access to it.
import faunadb, { query as q } from "faunadb"

var client = new faunadb.Client({ secret: process.env.GATSBY_FAUNA_CLIENT_SECRET })
By using an environment variable prefixed with GATSBY_, we gain access to it in our browser JavaScript (be sure to add it to your .env file).
By importing the query object from the SDK, we gain access to any of the methods available in Fauna’s first-party Fauna Query Language (FQL). In this case, we want to use the Create method to create a new document on our Collection.
createSignature = async (sigName, sigMessage) => {
 try {
 const queryResponse = await client.query(
                q.Create(
                    q.Collection('signatures'),
                    { 
                        data: { 
                            name: sigName,
                            message: sigMessage
                        } 
                    }
                )
            )
 const signatureInfo = { name: queryResponse.data.name, message: queryResponse.data.message, _ts: queryResponse.ts, _id: queryResponse.id}
 return signatureInfo
        } catch(err) {
            console.log(err);
        }
    }
We pass the Create function to the client.query() method. Create takes a Collection reference and an object of information to pass to a new Document. In this case, we use q.Collection and a string of our Collection name to get the reference to the Collection. The second argument is for our data. You can pass other items in the object, so we need to tell Fauna, we’re specifically sending it the data property on that object.
Next, we pass it the name and message we collected in our state. The response we get back from Fauna is the entire object of our Document. This includes our data in a data object, as well as a Fauna ID and timestamp. We reformat that data in a way that our Signatures list can use and return that back to our handleSubmit function.
Our submit handler will then pass that data into our setSigData prop which will notify our Signatures component to rerender with that new data. This gives our user immediate feedback that their submission has been accepted.

Rebuilding the site

This is all working in the browser, but the data hasn’t been updated in our static application yet.
From here, we need to tell our JAMstack host to rebuild our site. Many have the ability to specify a webhook to trigger a deployment. Since I’m hosting this demo on Netlify, I can create a new “Deploy webhook” in their admin and create a new triggerBuild function. This function will use the native JavaScript fetch() method and send a post request to that URL. Netlify will then rebuild the application and pull in the latest signatures.
  triggerBuild = async () => {
 const response = await fetch(process.env.GATSBY_BUILD_HOOK, { method: "POST", body: "{}" });
 return response;
    }
Both Gatsby Cloud and Netlify have implemented ways of handling “incremental” builds with Gatsby drastically speeding up build times. This sort of build can happen very quickly now and feel almost as fast as a traditional server-rendered site.
Every signature that gets added gets quick feedback to the user that it’s been submitted, is perpetually stored in a database, and served as HTML via a build process.
Still feels a little too much like a typical website? Let’s take all these concepts a step further.

Create A Mindful App With Auth0, Fauna Identity And Fauna User-Defined Functions (UDF)

Being mindful is an important skill to cultivate. Whether it’s thinking about your relationships, your career, your family, or just going for a walk in nature, it’s important to be mindful of the people and places around you.

This app intends to help you focus on one randomized idea every day and review the various ideas from recent days.
To do this, we need to introduce a key element to most apps: authentication. With authentication, comes extra security concerns. While this data won’t be particularly sensitive, you don’t want one user accessing the history of any other user.
Since we’ll be scoping data to a specific user, we also don’t want to store any secret keys on browser code, as that would open up other security flaws.
We could create an entire authentication flow using nothing but our wits and a user database with Fauna. That may seem daunting and moves us away from the features we want to write. The great thing is that there’s certainly an API for that in the JAMstack! In this demo, we’ll explore integrating Auth0 with Fauna. We can use the integration in many ways.

Setting Up Auth0 To Connect With Fauna

Many implementations of authentication with the JAMstack rely heavily on Serverless functions. That moves much of the security concerns from a security-focused company like Auth0 to the individual developer. That doesn’t feel quite right.

The typical flow would be to send a login request to a serverless function. That function would request a user from Auth0. Auth0 would provide the user’s JSON Web Token (JWT) and the function would provide any additional information about the user our application needs. The function would then bundle everything up and send it to the browser.
There are a lot of places in that authentication flow where a developer could introduce a security hole.
Instead, let’s request that Auth0 bundle everything up for us inside the JWT it sends. Keeping security in the hands of the folks who know it best.

We’ll do this by using Auth0’s Rules functionality to ask Fauna for a user token to encode into our JWT. This means that unlike our Guestbook, we won’t have any Fauna keys in our front-end code. Everything will be managed in memory from that JWT token.

Setting up Auth0 Application and Rule

First, we’ll need to set up the basics of our Auth0 Application.
Following the configuration steps in their basic walkthrough gets the important basic information filled in. Be sure to fill out the proper localhost port for your bundler of choice as one of your authorized domains.
After the basics of the application are set up, we’ll go into the “Rules” section of our account.
Click “Create Rule” and select “Empty Rule” (or start from one of their many templates that are helpful starting points).
Here’s our Rule code
async function (user, context, callback) {
  const FAUNADB_SECRET = 'Your Server secret';
  const faunadb = require('faunadb@2.11.1');
  const { query: q } = faunadb;
  const client = new faunadb.Client({ secret: FAUNADB_SECRET });
  try {
    const token = await client.query(
      q.Call('user_login_or_create', user.email, user) // Call UDF in fauna
    );
    let newClient = new faunadb.Client({ secret: token.secret });

    context.idToken['https://faunadb.com/id/secret'] = token.secret;
    callback(null, user, context);
  } catch(error) {
    console.log('->', error);
    callback(error, user, context);
  }
}
We give the rule a function that takes the user, context, and a callback from Auth0. We need to set up and grab a Server token to initialize our Fauna JavaScript SDK and initialize our client. Just like in our Guestbook, we’ll create a new Database and manage our Tokens in “Security”.
From there, we want to send a query to Fauna to create or log in our user. To keep our Rule code simple (and make it reusable), we’ll write our first Fauna “User-Defined Function” (UDF). A UDF is a function written in FQL that runs on Fauna’s infrastructure.
First, we’ll set up a Collection for our users. You don’t need to make a first Document here, as they’ll be created behind the scenes by our Auth0 rule whenever a new Auth0 user is created.
Next, we need an Index to search our users Collection based on the email address. This Index is simpler than our Guestbook, so we can add it to the Dashboard. Name the Index user_by_email, set the Collection to users, and the Terms to data.email. This will allow us to pass an email address to the Index and get a matching user Document back.
It’s time to create our UDF. In the Dashboard, navigate to “Functions” and create a new one named user_login_or_create.
Query(
  Lambda(
    ["userEmail", "userObj"], // Arguments
    Let(
      { user: Match(Index("user_by_email"), Var("userEmail")) }, // Set user variable 
      If(
        Exists(Var("user")), // Check if the User exists
        Create(Tokens(null), { instance: Select("ref", Get(Var("user"))) }), // Return a token for that item in the users collection (in other words, the user)
        Let( // Else statement: Set a variable
          {
            newUser: Create(Collection("users"), { data: Var("userObj") }), // Create a new user and get its reference
            token: Create(Tokens(null), { // Create a token for that user
              instance: Select("ref", Var("newUser"))
            })
          },
          Var("token") // return the token
        )
      )
    )
  )
)
Our UDF will accept a user email address and the rest of the user information. If a user exists in a users Collection, it will create a Token for the user and send that back. If a user doesn’t exist, it will create that user Document and then send a Token to our Auth0 Rule.
We can then store the Token as an idToken attached to the context in our JWT. The token needs a URL as a key. Since this is a Fauna token, we can use a Fauna URL. Whatever this is, you’ll use it to access this in your code.
This Token doesn’t have any permissions yet. We need to go into our Security rules and set up a new Role.
We’ll create an “AuthedUser” role. We don’t need to add any permissions yet, but as we create new UDFs and new Collections, we’ll update the permissions here. Instead of generating a new Key to use this Role, we want to add to this Role’s “Memberships”. On the Memberships screen, you can select a Collection to add as a member. The documents in this Collection (in our case, our Users), will have the permissions set on this role given via their Token.
Now, when a user logs in via Auth0, they’ll be returned a Token that matches their user Document and has its permissions.
From here, we come back to our application.

Implement logic for when the User is logged in

Auth0 has an excellent walkthrough for setting up a “vanilla” JavaScript single-page application. Most of this code is a refactor of that to fit the code splitting of this application.



First, we’ll need the Auth0 SPA SDK.
npm install @auth0/auth0-spa-js
import createAuth0Client from '@auth0/auth0-spa-js';
import { changeToHome } from './layouts/home'; // Home Layout
import { changeToMission } from './layouts/myMind'; // Current Mindfulness Mission Layout

let auth0 = null;
var currentUser = null;
const configureClient = async () => {
    // Configures Auth0 SDK
    auth0 = await createAuth0Client({
      domain: "mindfulness.auth0.com",
      client_id: "32i3ylPhup47PYKUtZGRnLNsGVLks3M6"
    });
};

const checkUser = async () => {
    // return user info from any method
    const isAuthenticated = await auth0.isAuthenticated();
    if (isAuthenticated) {
        return await auth0.getUser();
    }
}

const loadAuth = async () => {
    // Loads and checks auth
    await configureClient();      
    
    const isAuthenticated = await auth0.isAuthenticated();
    if (isAuthenticated) {
        // show the gated content
        currentUser = await auth0.getUser();
        changeToMission(); // Show the "Today" screen
        return;
    } else {
        changeToHome(); // Show the logged out "homepage"
    }

    const query = window.location.search;
    if (query.includes("code=") && query.includes("state=")) {

        // Process the login state
        await auth0.handleRedirectCallback();
       
        currentUser = await auth0.getUser();
        changeToMission();

        // Use replaceState to redirect the user away and remove the querystring parameters
        window.history.replaceState({}, document.title, "/");
    }
}

const login = async () => {
    await auth0.loginWithRedirect({
        redirect_uri: window.location.origin
    });
}
const logout = async () => {
    auth0.logout({
        returnTo: window.location.origin
    });
    window.localStorage.removeItem('currentMindfulItem') 
    changeToHome(); // Change back to logged out state
}

export { auth0, loadAuth, currentUser, checkUser, login, logout }
First, we configure the SDK with our client_id from Auth0. This is safe information to store in our code.
Next, we set up a function that can be exported and used in multiple files to check if a user is logged in. The Auth0 library provides an isAuthenticated() method. If the user is authenticated, we can return the user data with auth0.getUser().
We set up a login() and logout() functions and a loadAuth() function to handle the return from Auth0 and change the state of our UI to the “Mission” screen with today’s Mindful idea.
Once this is all set up, we have our authentication and user login squared away.
We’ll create a new function for our Fauna functions to reference to get the proper token set up.
const AUTH_PROP_KEY = "https://faunad.com/id/secret";
var faunadb = require('faunadb'),
q = faunadb.query;

async function getUserClient(currentUser) {
    return new faunadb.Client({ secret: currentUser[AUTH_PROP_KEY]})
}
This returns a new connection to Fauna using our Token from Auth0. This token works the same as the Keys from previous examples.

Generate a random Mindful topic and store it in Fauna

To start, we need a Collection of items to store our list of Mindful objects. We’ll create a Collection called “mindful” things, and create a number of items with the following schema:
{
   "title": "Career",
   "description": "Think about the next steps you want to make in your career. What’s the next easily attainable move you can make?",
   "color": "#C6D4FF",
   "textColor": "black"
 }
From here, we’ll move to our JavaScript and create a function for adding and returning a random item from that Collection.
async function getRandomMindfulFromFauna(userObj) {
    const client = await getUserClient(userObj);

    try {
        let mindfulThings = await client.query(
            q.Paginate(
                q.Documents(q.Collection('mindful_things'))
            )
        )
        let randomMindful = mindfulThings.data[Math.floor(Math.random()*mindfulThings.data.length)];
        let creation = await client.query(q.Call('addUserMindful', randomMindful));
        
        return creation.data.mindful;

    } catch (error) {
        console.log(error)
    }   
}
To start, we’ll instantiate our client with our getUserClient() method.
From there, we’ll grab all the Documents from our mindful_things Collection. Paginate() by default grabs 64 items per page, which is more than enough for our data. We’ll grab a random item from the array that’s returned from Fauna. This will be what Fauna refers to as a “Ref”. A Ref is a full reference to a Document that the various FQL functions can use to locate a Document.
We’ll pass that Ref to a new UDF that will handle storing a new, timestamped object for that user stored in a new user_things Collection.
We’ll create the new Collection, but we’ll have our UDF provide the data for it when called.
We’ll create a new UDF in the Fauna dashboard with the name addUserMindful that will accept that random Ref.
As with our login UDF before, we’ll use the Lambda() FQL method which takes an array of arguments.
Without passing any user information to the function, FQL is able to obtain our User Ref just calling the Identity() function. All we have from our randomRef is the reference to our Document. We’ll run a Get() to get the full object. We’ll the Create() a new Document in the user_things Collection with our User Ref and our random information.
We then return the creation object back out of our Lambda. We then go back to our JavaScript and return the data object with the mindful key back to where this function gets called.

Render our Mindful Object on the page

When our user is authenticated, you may remember it called a changeToMission() method. This function switches the items on the page from the “Home” screen to markup that can be filled in by our data. After it’s added to the page, the renderToday() function gets called to add content by a few rules.
The first rule of Serverless Data Club is not to make HTTP requests unless you have to. In other words, cache when you can. Whether that’s creating a full PWA-scale application with Service Workers or just caching your database response with localStorage, cache data, and fetch only when necessary.
The first rule of our conditional is to check localStorage. If localStorage does contain a currentMindfulItem, then we need to check its date to see if it’s from today. If it is, we’ll render that and make no new requests.
The second rule of Serverless Data Club is to make as few requests as possible without the responses of those requests being too large. In that vein, our second conditional rule is to check the latest item from the current user and see if it is from today. If it is, we’ll store it in localStorage for later and then render the results.
Finally, if none of these are true, we’ll fire our getRandomMindfulFromFauna() function, format the result, store that in localStorage, and then render the result.

Get the latest item from a user

I glossed over it in the last section, but we also need some functionality to retrieve the latest mindful object from Fauna for our specific user. In our getLatestFromFauna() method, we’ll again instantiate our Fauna client and then call a new UDF.
Our new UDF is going to call a Fauna Index. An Index is an efficient way of doing a lookup on a Fauna database. In our case, we want to return all user_things by the user field. Then we can also sort the result by timestamp and reverse the default ordering of the data to show the latest first.
Simple Indexes can be created in the Index dashboard. Since we want to do the reverse sort, we’ll need to enter some custom FQL into the Fauna Shell (you can do this in the database dashboard Shell section).
CreateIndex({
  name: "getMindfulByUserReverse",
  serialized: true,
  source: Collection("user_things"),
  terms: [
    {
      field: ["data", "user"]
    }
  ],
  values: [
    {
      field: ["ts"],
      reverse: true
    },
    {
      field: ["ref"]
    }
  ]
})
This creates an Index named getMindfulByUserReverse, created from our user_thing Collection. The terms object is a list of fields to search by. In our case, this is just the user field on the data object. We then provide values to return. In our case, we need the Ref and the Timestamp and we’ll use the reverse property to reverse order our results by this field.
We’ll create a new UDF to use this Index.
Query(
  Lambda(
    [],
    If( // Check if there is at least 1 in the index
      GT(
        Count(
          Select(
            "data",
            Paginate(Match(Index("getMindfulByUserReverse"), Identity()))
          )
        ),
        0
      ),
      Let( // if more than 0
        {
          match: Paginate(
            Match(Index("getMindfulByUserReverse"), Identity()) // Search the index by our User
          ),
          latestObj: Take(1, Var("match")), // Grab the first item from our match
          latestRef: Select(
            ["data"],
            Get(Select(["data", 0, 1], Var("latestObj"))) // Get the data object from the item
          ),
          latestTime: Select(["data", 0, 0], Var("latestObj")), // Get the time
          merged: Merge( // merge those items into one object to return
            { latestTime: Var("latestTime") },
            { latestMindful: Var("latestRef") }
          )
        },
        Var("merged")
      ),
      Let({ error: { err: "No data" } }, Var("error")) // if there aren't any, return an error.
    )
  )
)
This time our Lambda() function doesn’t need any arguments since we’ll have our User based on the Token used.
First, we’ll check to see if there’s at least 1 item in our Index. If there is, we’ll grab the first item’s data and time and return that back as a merged object.
After we get the latest from Fauna in our JavaScript, we’ll format it to a structure our storeCurrent() and render() methods expect it and return that object.
Now, we have an application that creates, stores, and fetches data for a daily message to contemplate. A user can use this on their phone, on their tablet, on the computer, and have it all synced. We could turn this into a PWA or even a native app with a system like Ionic.
We’re still missing one feature. Viewing a certain number of past items. Since we’ve stored this in our database, we can retrieve them in whatever way we need to.

Pull the latest X Mindful Missions to get a picture of what you’ve thought about

We’ll create a new JavaScript method paired with a new UDF to tackle this.
getSomeFromFauna will take an integer count to ask Fauna for a certain number of items.
Our UDF will be very similar to the getLatestFromFauana UDF. Instead of returning the first item, we’ll Take() the number of items from our array that matches the integer that gets passed into our UDF. We’ll also begin with the same conditional, in case a user doesn’t have any items stored yet.
Query(
  Lambda(
    ["count"], // Number of items to return
    If( // Check if there are any objects
      GT( 
        Count(
          Select(
            "data",
            Paginate(Match(Index("getMindfulByUserReverse"), Identity(null)))
          )
        ),
        0
      ),
      Let(
        {
          match: Paginate(
            Match(Index("getMindfulByUserReverse"), Identity(null)) // Search the Index by our User
          ),
          latestObjs: Select("data", Take(Var("count"), Var("match"))), // Get the data that is returned
          mergedObjs: Map( // Loop over the objects
            Var("latestObjs"),
            Lambda(
              "latestArray",
              Let( // Build the data like we did in the LatestMindful function
                {
                  ref: Select(["data"], Get(Select([1], Var("latestArray")))),
                  latestTime: Select(0, Var("latestArray")),
                  merged: Merge(
                    { latestTime: Var("latestTime") },
                    Select("mindful", Var("ref"))
                  )
                },
                Var("merged") // Return this to our new array
              )
            )
          )
        },
        Var("mergedObjs") // return the full array
      ),
      { latestMindful: [{ title: "No additional data" }] } // if there are no items, send back a message to display
    )
  )
)
In this demo, we created a full-fledged app with serverless data. Because the data is served from a CDN, it can be as close to a user as possible. We used FaunaDB’s features, such as UDFs and Indexes, to optimize our database queries for speed and ease of use. We also made sure we only queried our database the bare minimum to reduce requests.

Where To Go With Serverless Data

The JAMstack isn’t just for sites. It can be used for robust applications as well. Whether that’s for a game, CRUD application or just to be mindful of your surroundings you can do a lot without sacrificing customization and without spinning up your own non-dist database system.
With performance on the mind of everyone creating on the JAMstack — whether for cost or for user experience — finding a good place to store and retrieve your data is a high priority. Find a spot that meets your needs, those of your users, and meets ideals of the JAMstack.