Luna Tech

Tutorials For Dummies.

GraphQL with Vue (POC)

2021-09-12


1. Scaffolding

github repo

Organizing folders

mkdir graphql-poc
cd graphql-poc
mkdir frontend
mkdir backend

Frontend

cd frontend
# ref: https://cli.vuejs.org/guide/creating-a-project.html#vue-create
# select default options for all questions
vue create .

# add graphql libraries
# ref: https://apollo.vuejs.org/guide/installation.html#vue-cli-plugin
npm install -s graphql vue-apollo apollo-boost

Note

Backend

cd backend

# init project
# keep everything default
# ref: https://www.apollographql.com/docs/apollo-server/getting-started/
npm init --yes
# install graphql related packages
npm install apollo-server graphql nodemon
touch index.js

2. Backend

To start simple

  1. use in-memory db
  2. no mutation, only query data
// index.js
const { ApolloServer, gql } = require("apollo-server");

// in-memory db
const data = {
  posts: [
    {
      id: "xyz-1",
      content: "First Post",
    },
    {
      id: "xyz-2",
      content: "Second Post",
    },
    {
      id: "xyz-3",
      content: "Random Post",
    },
  ],
};

// define schema (query, mutation, objects) based on db and use case
const schema = gql`
  type Query {
    posts: [Post]
    postById(id: String!): Post
  }

  type Post {
    id: ID!
    content: String!
  }
`;

// define resolvers (hook up db and schema)
var resolvers = {
  Query: {
    posts: (parent, args) => {
      return data.posts;
    },
    postById: (parent, args) => {
      return data.posts.filter((p) => p.id === args.id);
    },
  },
};

// create server
const server = new ApolloServer({
  typeDefs: schema,
  resolvers: resolvers,
});

// define port
server.listen(4001).then(({ url }) => {
  console.log("API server running at http://localhost:4001");
});

use RESTful API for fetching data

install package

cd backend
npm i apollo-datasource-rest

hook up with the json-server endpoint

// backend/datasources/posts.js
const { RESTDataSource } = require("apollo-datasource-rest");

class PostAPI extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = "http://localhost:3000/posts";
  }

  async getPostById(id) {
    const data = await this.get(`/${id}`);
    return data;
  }

  async getPosts() {
    const data = await this.get("/");
    return data;
  }
}

module.exports = PostAPI;

restructuring

// backend/index.js
const { ApolloServer } = require("apollo-server");
const PostAPI = require("./datasources/posts");
const typeDefs = require("./schema");
const resolvers = require("./resolvers");

// we can have multiple datasources
const dataSources = () => ({
  postAPI: new PostAPI(),
});

const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources,
});

server.listen(4001).then(() => {
  console.log("API server running at https://localhost:4001");
});

resolver structure

// backend/resolver.js
const Query = require("./resolvers/query");
module.exports = {
  Query,
};
// backend/resolvers/query.js
module.exports = {
  posts: (_, __, { dataSources }) => {
    return dataSources.postAPI.getPosts();
  },
  postById: (_, { id }, { dataSources }) => {
    return dataSources.postAPI.getPostById(id);
  },
};

3. Frontend

Create Apollo Client

// frontend/main.js

import Vue from "vue";
import App from "./App.vue";
import ApolloClient from "apollo-boost";
import VueApollo from "vue-apollo";

Vue.config.productionTip = false;

const apolloClient = new ApolloClient({
  uri: "http://localhost:4001",
});

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
});

// use middleware
Vue.use(VueApollo);

new Vue({
  render: (h) => h(App),
  // inject apolloClient here like vue-router or vuex
  apolloProvider,
}).$mount("#app");

Query data from backend

// frontend/src/App.vue
<script>
  import gql from "graphql-tag";

  const posts_gql = gql`
    query getPosts {
      posts {
        id
        content
      }
    }
  `;

  export default {
    name: "App",
    data: function () {
      return {
        // posts here is the data in Vue component (must be the same name as apollo)
        // Having the same name is how the posts state can be synced to the posts query’s result.
        posts: [],
      };
    },
    apollo: {
      // posts is the return data key
      // posts_gql is the query we defined
      posts: posts_gql,
    },
  };
</script>

Display data

<template>
  <div id="app">
    <ul>
      <li v-for="post in posts" :key="post.id">{{ post.content }}</li>
    </ul>
  </div>
</template>

4. Flow of data