How to build a cool simple contact page with Nuxt and FaunaDB
Tags: nuxt, graphql, fauna, vue
Published: 2020-10-30 16:56:56

# In this article, you'll know how to build a contact website and contact ways...

- front-end: [nuxtjs][nuxtUrl]

- back-end: [faunaDB](https://fauna.com/)

- deploy: [firebase](https://firebase.google.com)

> First, we'll build a simple page and in it how to contact us like [Twitter](https://twitter.com), [Dev](https://dev.to), [Github](https://github.com)...

## [Nuxtjs][nuxtUrl]
[Nuxt][nuxtUrl] is an open-source web application framework built on top of Vue.js, It is well known for its SSR capabilities, but it can also do static.

Instead of having a Node server process each client request - eventually fetching data from an API or database in between, we'll be using Nuxt as a static site generator to do the heavy lifting during the build stage.

## [FaunaDB][faunaDBUrl]
[Fauna][faunaDBUrl] is a globally distributed, low-latency database, with native GraphQL support, that promises to be always consistent and always secure.

> As a serverless database, FaunaDB allows applications to access data through a secure API, in contrast to more "traditional" relational databases that require you to open a connection. In that sense, FaunaDB is “connectionless” and rather behaves like an API, which fits perfectly in a Jamstack architecture. There is also no need to host and manage our own database. It requires zero server configuration and supports seamless scalability out-of-the-box.

#### `Demo`

> you can try the project ([demo](https://contact-devx.web.app))

### Pre-requisites

before we move on, you'll need :

- Npm, Node & Npx installed.

- A [FaunaDB Account](https://dashboard.fauna.com/accounts/register).


### Let's go

first, you'll need to install `create-nuxt-app`

```sh
$ npm i -g create-nuxt-app
```

after install let's create our site

```sh
$ create-nuxt-app PROJ_NAME && cd PROJ_NAME
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/n8t3d85532tcq9dlqbbe.png)

> now we need three packages :

- [faunadb](https://www.npmjs.com/package/faunadb): JavaScript driver for FaunaDB

- [slugify](https://www.npmjs.com/package/slugify): we'll use this package to generate slugs from con names

- [dotenv](https://www.npmjs.com/package/dotenv)

```sh
$ npm i faunadb slugify dotenv
```

now let's create [graphql][graphqlUrl] schema

```sh
$ mkdir graphql && cd graphql && touch schema.gql
```

### data modiling (graphql schema)

structure:

- **desc**: contact description

- **repoUrl**: contact url

- **brand** || **solid**: check if fontawesome icon is `fab` or `fas`

- **faI**: contact [fontawesome](https://fontawesome.com/icons?d=gallery) icon

- **hashtag**: if the contact has a hashtag

- **hashtag_name**: hashtag name

inside `schema.gql`

```graphql
type Repo {
  desc: String! @unique
  repoUrl: String! @unique

  # if font-awesome icon is brand or solid
  brand: Boolean
  solid: Boolean
  faI: String

  # this only for twitter & dev contacts
  hashtag: Boolean
  hashtag_name: String
}

type Query {
  allRepos: [Repo!]!
}
```

now go to faunaDB dashboard & create `new database`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/expryn008fmgr2tqvc55.png)

go to graphql section and import your schema

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/b2zrsz099g899rcy0lyj.png)

`now you should have collections`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/la5p7krfneqxu8py0xy9.png)

### Creating keys

create `Admin key` & `Server key`

##### `ADMIN`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zwo2fwya6plwu74y2hy2.png)

Press `SAVE`

in your project create `.env` file

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ogpsskmlkw2wfj1mzqsk.png)

replace 🔑️ with your key

##### `SERVER`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/culd11hv4ixm9ea0kv93.png)

Also press `SAVE`

in `.env`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/aajpd1da13om4ulawisx.png)

replace 🗝️ with your second key

`ok we finish all this`

return to `graphql` folder and create `db-connection.js`

```sh
$ touch db-connection.js
```

inside it paste this code

```js
require("dotenv").config();
const faunadb = require("faunadb");
const query = faunadb.query;

function createClient() {
  if (!process.env.FAUNA_ADMIN_KEY) {
    throw new Error("FAUNA_ADMIN_KEY not found");
  }

  const client = new faunadb.Client({
    secret: process.env.FAUNA_ADMIN_KEY
  });

  return client;
}

exports.client = createClient();
exports.query = query;
```

##### very important step
in `nuxt.config.js` in above

```js
require("dotenv").config();
```

and let's add `generate` prop

```js
generate: {
    async routes() {
      const faunadb = require("faunadb");
      const query = faunadb.query;
      const slugify = require("slugify");
      const q = query;

      if (!process.env.FAUNA_SERVER_KEY) {
        throw new Error("FAUNA_SERVER_KEY not found.");
      }

      const client = new faunadb.Client({
        secret: process.env.FAUNA_SERVER_KEY
      });

      const result = await client.query(
        q.Map(
          q.Paginate(q.Match(q.Index("allRepos"))),
          q.Lambda("X", q.Get(q.Var("X")))
        )
      );

      const repos = result.data.map(repo => repo.data);
      const routes = repos.map(repo => {
        const repoUrlParts = repo.repoUrl.split("/");
        const repoOwner = repoUrlParts[repoUrlParts.length - 2];
        const repoName = repoUrlParts[repoUrlParts.length - 1];

        const slug = slugify(repoName, {
          remove: /[*+~.()'"!:@]/g
        });

        repo.slug = slug;
        repo.owner = repoOwner;
        repo.name = repoName;

        return {
          payload: repo
        };
      });

      routes.push({
        route: "/",
        payload: repos
      });
      return routes;
    }
  }
```

### Import collections

return to [faunaDB][faunaDBUrl] dashboard, collections section to create new collection

press `new document`

example data

```json
{
  "desc": "you can make a github issue",
  "repoUrl": "https://github.com/YOUR_REPO/issues",
  "brand": true,
  "solid": false,
  "faI": "github",
  "hashtag": false
}
```

I put dev-x Twitter, GitHub issue, Dev org

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/k04ft6d4rsetm22g184m.png)

##### now go to `pages`

before beginning, we need some packages

* font-awesome

* sass & sass-loader

```sh
$ npm i @fortawesome/fontawesome-svg-core @fortawesome/vue-fontawesome @fortawesome/free-brands-svg-icons @fortawesome/free-solid-svg-icons sass sass-loader
```

now go to `nuxt.config.js` in `css` section
we going to import font-awesome style
type this

```js
css: [
  "@fortawesome/fontawesome-svg-core/styles.css",
  ...
],
```

go to `pages` and create `index.scss`

in `pages` you should have two files

```
├── index.vue
└── index.scss
```

in `index.vue`

`template`

```vue
<template>
  <div class="container">
    <div>
      <Logo />
      <h1 class="title">contact.dev-x</h1>

      <div>
        <div class="card">
          <h3>Contacts &rarr;</h3>

          <ul v-for="repo in repos" :key="repo.desc">
            <li>
              {{ repo.desc }}

              <strong v-if="repo.hashtag">{{ repo.hashtag_name }}</strong>

              <a :href="repo.repoUrl">
                <a class="btn">
                  <fai v-if="repo.brand" :icon="['fab', `${repo.faI}`]" />
                  <fai v-if="repo.solid" :icon="repo.faI" />
                </a>
              </a>
            </li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</template>
```

`script`

```vue
<script>
import Vue from "vue";

import { library, config } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { fab } from "@fortawesome/free-brands-svg-icons";
import { fas } from "@fortawesome/free-solid-svg-icons";

// This is important, we are going to let Nuxt.js worry about the CSS
config.autoAddCss = false;

// You can add your icons directly in this plugin. See other examples for how you
// can add other styles or just individual icons.
library.add(fab);
library.add(fas);

// Register the component globally
Vue.component("fai", FontAwesomeIcon);

Vue.config.productionTip = false;

export default {
  asyncData({ payload }) {
    return { repos: payload };
  }
};
</script>
```

`style`

```vue
<style lang="scss">
@import "index.scss";
</style>
```

`index.scss`

```scss
$color: #121312;

.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.title {
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #31465e;
  letter-spacing: 1px;
}

.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}

.links {
  padding-top: 15px;
}

.card {
  cursor: pointer;
  margin: 1rem;
  flex-basis: 45%;
  padding: 1.5rem;
  text-align: left;
  color: inherit;
  text-decoration: none;
  border: 2px solid#000000;
  border-radius: 2.5px;
  flex-direction: column;
  box-shadow: 5px 6px 0px black;
}

.card h3 {
  margin: 0 0 1rem 0;
  font-size: 1.5rem;
}

.card li {
  margin: 0;
  font-size: 1.25rem;
  line-height: 1.5;
  list-style: none;
  font-family: DF;
}

a {
  color: rgb(0, 119, 255);
}

strong {
  color: dodgerblue;
}

.btn {
  display: inline-block;
  border-radius: 4px;
  border: 1px solid $color;
  color: $color;
  text-decoration: none;
  padding: 10px 30px;
  margin-left: 15px;
}

.btn:hover {
  color: #fff;
  background-color: $color;
}
```

Everything is ok

##### the step before end
in terminal type...

```sh
$ npm run generate
```

if you see this error

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0c765ieo4q16fuy7m86h.png)

don't worry it's a soft error

after generate

we'll use `serve` to serve the output build

```sh
$ npx serve dist

# or

$ sudo npm i -g serve
$ serve dist
```

go to `localhost:5000` and see your result

### Deploy to [firebase][firebaseUrl] (optional)

install firebase globally

if you've windows

```cmd
npm i -g firebase firebase-tools
```

mac or linux

```sh
$ sudo npm i -g firebase firebase-tools
```

now go to [firebase console](https://console.firebase.google.com)

let's create a web app

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/e07kqz38tls3dzq6gw5p.png)

`and create a new project`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jdkp59jz0z9863onu60w.png)

`hosting`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3ouu0oewdraloo3f6blp.png)

`final touches`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pu7begybkrldrtq6riwr.png)

copy `"site"` prop

go to the project and type

```sh
$ firebase login
```

it's going to login in browser

`initializing`

```sh
$ firebase init
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7fue9cf6jt5hyn2i64ox.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1gmxgrchn69011uzhz56.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1gmxgrchn69011uzhz56.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7gunx4sy7ldsve194mpo.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/osnrtuz3nzxshhilm5k0.png)

go to `firebase.json`

```json
{
  "hosting": {
    "site": "contactus-x",
    "public": "dist",
    ...
  }
}

```

and the final step

```sh
$ firebase deploy --only hosting:contactus-x
```

that it

```sh
$ echo happy coding ⌨️
```

[nuxtUrl]: https://nuxt.org
[faunaDBUrl]: https://fauna.com
[graphqlUrl]: https://graphql.org
[firebaseUrl]: https://firebase.google.com