DEV Community

abdfn
abdfn

Posted on • Edited on

How to build a cool simple contact page with Nuxt and FaunaDB

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

First, we'll build a simple page and in it how to contact us like Twitter, Dev, Github...

Nuxtjs

Nuxt 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

Fauna 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)

Pre-requisites

before we move on, you'll need :

Let's go

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

$ npm i -g create-nuxt-app
Enter fullscreen mode Exit fullscreen mode

after install let's create our site

$ create-nuxt-app PROJ_NAME && cd PROJ_NAME
Enter fullscreen mode Exit fullscreen mode

Alt Text

now we need three packages :

  • faunadb: JavaScript driver for FaunaDB

  • slugify: we'll use this package to generate slugs from con names

  • dotenv

$ npm i faunadb slugify dotenv
Enter fullscreen mode Exit fullscreen mode

now let's create graphql schema

$ mkdir graphql && cd graphql && touch schema.gql
Enter fullscreen mode Exit fullscreen mode

data modiling (graphql schema)

structure:

  • desc: contact description

  • repoUrl: contact url

  • brand || solid: check if fontawesome icon is fab or fas

  • faI: contact fontawesome icon

  • hashtag: if the contact has a hashtag

  • hashtag_name: hashtag name

inside schema.gql

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!]!
}
Enter fullscreen mode Exit fullscreen mode

now go to faunaDB dashboard & create new database

Alt Text

go to graphql section and import your schema

Alt Text

now you should have collections

Alt Text

Creating keys

create Admin key & Server key

ADMIN

Alt Text

Press SAVE

in your project create .env file

Alt Text

replace 🔑️ with your key

SERVER

Alt Text

Also press SAVE

in .env

Alt Text

replace 🗝️ with your second key

ok we finish all this

return to graphql folder and create db-connection.js

$ touch db-connection.js
Enter fullscreen mode Exit fullscreen mode

inside it paste this code

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;
Enter fullscreen mode Exit fullscreen mode
very important step

in nuxt.config.js in above

require("dotenv").config();
Enter fullscreen mode Exit fullscreen mode

and let's add generate prop

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;
    }
  }
Enter fullscreen mode Exit fullscreen mode

Import collections

return to faunaDB dashboard, collections section to create new collection

press new document

example data

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

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

Alt Text

now go to pages

before beginning, we need some packages

  • font-awesome

  • sass & sass-loader

$ npm i @fortawesome/fontawesome-svg-core @fortawesome/vue-fontawesome @fortawesome/free-brands-svg-icons @fortawesome/free-solid-svg-icons sass sass-loader
Enter fullscreen mode Exit fullscreen mode

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

css: [
  "@fortawesome/fontawesome-svg-core/styles.css",
  ...
],
Enter fullscreen mode Exit fullscreen mode

go to pages and create index.scss

in pages you should have two files

├── index.vue
└── index.scss
Enter fullscreen mode Exit fullscreen mode

in index.vue

template

<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>
Enter fullscreen mode Exit fullscreen mode

script

<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>
Enter fullscreen mode Exit fullscreen mode

style

<style lang="scss">
@import "index.scss";
</style>
Enter fullscreen mode Exit fullscreen mode

index.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;
}
Enter fullscreen mode Exit fullscreen mode

Everything is ok

the step before end

in terminal type...

$ npm run generate
Enter fullscreen mode Exit fullscreen mode

if you see this error

Alt Text

don't worry it's a soft error

after generate

we'll use serve to serve the output build

$ npx serve dist

# or

$ sudo npm i -g serve
$ serve dist
Enter fullscreen mode Exit fullscreen mode

go to localhost:5000 and see your result

Deploy to firebase (optional)

install firebase globally

if you've windows

npm i -g firebase firebase-tools
Enter fullscreen mode Exit fullscreen mode

mac or linux

$ sudo npm i -g firebase firebase-tools
Enter fullscreen mode Exit fullscreen mode

now go to firebase console

let's create a web app

Alt Text

and create a new project

Alt Text

hosting

Alt Text

final touches

Alt Text

copy "site" prop

go to the project and type

$ firebase login
Enter fullscreen mode Exit fullscreen mode

it's going to login in browser

initializing

$ firebase init
Enter fullscreen mode Exit fullscreen mode

Alt Text

Alt Text

Alt Text

Alt Text

Alt Text

go to firebase.json

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

Enter fullscreen mode Exit fullscreen mode

and the final step

$ firebase deploy --only hosting:contactus-x
Enter fullscreen mode Exit fullscreen mode

that it

$ echo happy coding ⌨️
Enter fullscreen mode Exit fullscreen mode

Top comments (0)