GraphQL with Vue (POC)
2021-09-12
1. Scaffolding
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
-
Apollo itself isn’t specific to Vue, but the
vue-apollo
package is the Vue-specific version of apollo client. -
apollo-boost
is a package that makes it easier to set up a GraphQL client without getting into too much configuration.
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
- use in-memory db
- 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
- add methods for the GraphQL schema queries
// 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
- we extract datasources, schema, resolvers into different files
// 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
- we have a resolvers.js file to contain different resolvers, cos for large applications, there’re going to be query, mutation and many more objects
// backend/resolver.js
const Query = require("./resolvers/query");
module.exports = {
Query,
};
- note the postAPI here is the one we are passing into the ApolloServer in index.js
// 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
- client uri is the backend endpoint
- provider can point to multiple clients (if necessary)
// 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>