Simple ToDo GraphQL API in VueJS & TailwindCSS with Docker [PART 03]

sulmanweb

Sulman Baig

Posted on August 14, 2020

Simple ToDo GraphQL API in VueJS & TailwindCSS with Docker [PART 03]

In this article, we will create VueJS app of ToDo App with TailwindCSS using GraphQL API we built in previous articles.

Demo at:

Code Repo is at:

ToDo App

TODO APP

built by @sulmanweb

Technologies

  • Docker

API (rails-api)

  • Ruby on Rails 6
  • MongoDB 4
  • GraphQL

Vue Front End

  • VueJS 2
  • TailwindCSS
  • FontAwesome
  • Apollo GraphQL Client

To Run




In the previous article, we created rails, graphql API with MongoDB:

Now we move to VueJS part of the project. So, first create the VueJS app with the following command in the front-vue directory:

docker run --rm -v "${PWD}:/$(basename `pwd`)" -w "/$(basename `pwd`)" -it node:14.5-alpine sh -c "yarn global add @vue/cli && vue create . && vue add apollo"
Enter fullscreen mode Exit fullscreen mode

This command will create VueJS app in the directory and add plugin of vue-apollo which we will use for the graphql api backend access.

Then create the front-vue service in docker and start in docker by following commands:

todo-app/docker-compose.yml

    front-vue:
        build: ./front-vue
        ports:
          - "8080:8080" # use port that you want to in your local instead of 8091
        volumes:
          - ./front-vue:/front-vue
          - front_node_modules:/front-vue/node_modules
volumes:
    front_node_modules:
Enter fullscreen mode Exit fullscreen mode

todo-app/front-vue/Dockerfile

FROM node:14.5-alpine
WORKDIR /front-vue
COPY package*.json ./
RUN yarn install
COPY . .
CMD ["yarn", "serve"]
Enter fullscreen mode Exit fullscreen mode
docker-compose up -d --build
Enter fullscreen mode Exit fullscreen mode

Init Project:

We will use the following packages which you can see todo-app/front-vue/package.json

Initialize tailwindcss and fontawesome in main.js by adding following code:

todo-app/front-vue/src/main.js

import { library } from "@fortawesome/fontawesome-svg-core";
import {
  faPlus,
  faLongArrowAltLeft,
  faCheckCircle,
  faCircle,
  faTrash,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";

library.add(faPlus, faLongArrowAltLeft, faCheckCircle, faCircle, faTrash);

Vue.component("font-awesome-icon", FontAwesomeIcon);

import "./assets/styles/index.css";
Enter fullscreen mode Exit fullscreen mode

Clear the App.js and other files for code and styling. Also add purging css for production:
todo-app/front-vue/postcss.config.js

const autoprefixer = require("autoprefixer");
const tailwindcss = require("tailwindcss");
const postcssPurgecss = require(`@fullhuman/postcss-purgecss`);

const purgecss = postcssPurgecss({
  // Specify the paths to all of the template files in your project.
  content: ["./public/**/*.html", "./src/**/*.vue"],
  // Include any special characters you're using in this regular expression.
  // See: https://tailwindcss.com/docs/controlling-file-size/#understanding-the-regex
  defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
  // Whitelist auto generated classes for transitions and router links.
  // From: https://github.com/ky-is/vue-cli-plugin-tailwind
  whitelistPatterns: [
    /-(leave|enter|appear)(|-(to|from|active))$/,
    /^(?!(|.*?:)cursor-move).+-move$/,
    /^router-link(|-exact)-active$/,
    /svg.*/,
    /fa.*/,
  ],
});

module.exports = {
  plugins: [
    tailwindcss,
    autoprefixer,
    ...(process.env.NODE_ENV === "production" ? [purgecss] : []),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Also, add tailwind CSS file
todo-app/front-vue/src/assets/styles/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Change the name of token and URL of backend graphql
todo-app/front-vue/src/vue-apollo.js

// Name of the localStorage item
const AUTH_TOKEN = "token";

// Http endpoint
const httpEndpoint =
  process.env.VUE_APP_GRAPHQL_HTTP || "http://localhost:3000/graphql";
Enter fullscreen mode Exit fullscreen mode

GraphQL using Vue-Apollo

There are many ways to make graphql mutation and query through Vue-Apollo which I have explained in different components and views but the main way which I used everywhere and I like most is making querying and mutation in .gql files separately and then we can change the query or mutation even after project is created separately in their own files. Also the way the bearer authorization being handled by the vue-apollo automatically is great.

So, we create a folder graphql in src and we add all the queries and mutations in gql language there. For example:

todo-app/front-vue/src/graphql/mutations/signUp.gql

mutation signUp($email: String!, $password: String!) {
  signUp(input: { email: $email, password: $password }) {
    token
  }
}
Enter fullscreen mode Exit fullscreen mode

todo-app/front-vue/src/graphql/queries/me.gql

query me {
  me {
    lists {
      id
      name
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Different ways to make query and mutations

Mutations

We can use mutation as components with a template like in signUp:

todo-app/front-vue/src/views/auth/signUp.vue

<ApolloMutation
 :mutation="require('@/graphql/mutations/signUp.gql')"
 :variables="{ email, password }"
 @done="signedUp"
 >
  <template v-slot="{ mutate, loading, error }">
    <!-- Error -->
    <div class="bg-red-100 border-l-4 border-red-400 p-4" v-if="error">
      <div class="flex">
        <div class="flex-shrink-0">
          <svg
            class="h-5 w-5 text-red-400"
            viewBox="0 0 20 20"
            fill="currentColor"
          >
          <path
            fill-rule="evenodd"
            d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
            clip-rule="evenodd"
          />
          </svg>
        </div>
        <div class="ml-3">
          <p class="text-sm leading-5 text-red-700">
            {{ error.toString().replace("Error: GraphQL error: ", "") }}
          </p>
        </div>
      </div>
    </div>
    <!-- Sign Up Form -->
    <form action="#" class="mt-5" @submit.prevent="mutate()">
      <!-- Email -->
      <div class="mb-5">
        <input
          type="email"
          name="email"
          id="email"
          v-model="email"
          class="rounded w-full shadow border border-gray-900 tracking-wide text-xl p-2"
          placeholder="Email"
          autocomplete="on"
          required
        />
      </div>

      <!-- Password -->
      <div class="mb-5">
        <input
          type="password"
          name="password"
          v-model="password"
          class="rounded w-full shadow border border-gray-900 tracking-wide text-xl p-2"
          placeholder="Password"
          id="sign-up-password"
          autocomplete="off"
        />
      </div>

      <div class="text-center flex justify-between">
        <!-- sign in link -->
        <router-link
          to="/sign_in"
          class="self-center hover:underline hover:text-gray-800"
          >Sign In</router-link
        >
        <!-- sign up link -->
        <button
          type="submit"
          :disabled="loading"
          class="px-4 py-2 rounded tracking-wide font-semibold bg-black text-white text-lg hover:bg-gray-800 shadow-lg"
        >
          Sign Up
        </button>
      </div>
    </form>
  </template>
</ApolloMutation>
Enter fullscreen mode Exit fullscreen mode

Second way is to use in normal vue method like below:

todo-app/front-vue/src/views/listShow.vue

async changeStatus(task) {
  const result = await this.$apollo.mutate({
    mutation: require("@/graphql/mutations/changeTaskStatus.gql"),
    variables: {
      id: task.id,
    },
  });
  if (!!result.data) {
    await this.loadList();
  }
},
Enter fullscreen mode Exit fullscreen mode

Queries

The first way is graphql query as a component like we saw in mutations.

Second way is the same to call in function same as that of mutations like:

todo-app/front-vue/src/views/listShow.vue

async getList() {
  return await this.$apollo.query({
    query: require("@/graphql/queries/showList.gql"),
    variables: {
      id: this.$route.params.listId,
    },
    fetchPolicy: "network-only",
  });
},
Enter fullscreen mode Exit fullscreen mode

Third way is to call query in apollo object in the js section of the vue component like:

todo-app/front-vue/src/views/Home.vue

apollo: {
  me: {
    query: require("@/graphql/queries/me.gql"),
    result({ data }) {
      if (data) {
        this.lists = data.me.lists;
        this.loading = false;
      }
    },
  },
},
Enter fullscreen mode Exit fullscreen mode

See the Complete Development in Videos

Happy Coding!

💖 💪 🙅 🚩
sulmanweb
Sulman Baig

Posted on August 14, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related