A comprehensive step by step tutorial on building CRUD Web App using Node, Express, PostgreSQL, Vue 2 and Graphql CRUD Web App. For the client-side (Vue 2) we will use Vue-Apollo module. For the backend side, we will use Node, Express, Sequelize, and PostgreSQL with Express-Graphql module and their dependencies. The scenario for this tutorial is simple as usual, just the CRUD operation which data accessible through GraphQL.
Table of Contents:
- Create Express.js Application and Install Required Modules
- Add and Configure Sequelize.js Module and Dependencies
- Create or Generate Sequelize Models and Migrations
- Install GraphQL Modules and Dependencies
- Create GraphQL Schemas for the Book
- Add GraphQL Mutation to the Schema
- Test GraphQL using GraphiQL
- Create Vue 2 Application
- Install Required Modules, Dependencies, and Router
- Create Bootstrap-Vue Component to Display List of Books
- Create Bootstrap-Vue Component to Show and Delete Books
- Create Bootstrap-Vue Component to Add a New Book
- Create Bootstrap-Vue Component to Edit a Book
- Run and Test GraphQL CRUD from the Vue 2 Application
The following tools, frameworks, and modules are required for this tutorial:
- Node.js (choose recommended version)
- Vue 2
- Express.js
- GraphQL
- Express-GraphQL
- Vue-Apollo
- Bootstrap-Vue
- Terminal (Mac/Linux) or Node Command Line (Windows)
- IDE or Text Editor (We are using Visual Studio Code)
We assume that you have already installed Node.js. Make sure Node.js command line is working (on Windows) or runnable in Linux/OS X terminal.
node -v
v10.15.1
npm -v
6.8.0
yarn -v
1.10.1
That the versions that we are uses. Let's continue with the main steps.
Create Express.js Application and Install Required Modules
We will use Express.js as backend server for GraphQL. Express is a web application framework for Node.js, released as free and open-source software under the MIT License. It is designed for building web applications and APIs. It has been called the de facto standard server framework for Node.js. Open your terminal or node command line the go to your projects folder. First, install express-generator using this command.
sudo npm install express-generator -g
Next, create an Express.js app using this command.
express vue-graphql
This will create Express.js project with files and directories.
create : vue-graphql/
create : vue-graphql/public/
create : vue-graphql/public/javascripts/
create : vue-graphql/public/images/
create : vue-graphql/public/stylesheets/
create : vue-graphql/public/stylesheets/style.css
create : vue-graphql/routes/
create : vue-graphql/routes/index.js
create : vue-graphql/routes/users.js
create : vue-graphql/views/
create : vue-graphql/views/error.jade
create : vue-graphql/views/index.jade
create : vue-graphql/views/layout.jade
create : vue-graphql/app.js
create : vue-graphql/package.json
create : vue-graphql/bin/
create : vue-graphql/bin/www
Next, go to the newly created project folder then install node modules.
cd vue-graphql && npm install
There's no view yet using the latest Express generator. We don't need it because we will create a GraphQL server.
Add and Configure Sequelize.js Module and Dependencies
Sequelize is a promise-based Node.js ORM for Postgres, MySQL, MariaDB, SQLite, and Microsoft SQL Server. It features solid transaction support, relations, eager and lazy loading, read replication and more. Before installing the modules for this project, first, install Sequelize-CLI by type this command.
sudo npm install -g sequelize-cli
To install Sequelize.js module, type this command.
npm install --save sequelize
Then install the module for PostgreSQL.
npm install --save pg pg-hstore
Next, create a new file at the root of the project folder.
touch .sequelizerc
Open and edit that file then add these lines of codes.
const path = require('path');
module.exports = {
"config": path.resolve('./config', 'config.json'),
"models-path": path.resolve('./models'),
"seeders-path": path.resolve('./seeders'),
"migrations-path": path.resolve('./migrations')
};
That files will tell Sequelize initialization to generate config, models, seeders and migrations files to specific directories. Next, type this command to initialize the Sequelize.
sequelize init
That command will create `config/config.json`, `models/index.js`, `migrations` and `seeders` directories and files. Next, open and edit `config/config.json` then make it like this.
{
"development": {
"username": "djamware",
"password": "dj@mw@r3",
"database": "node_sequelize",
"host": "127.0.0.1",
"dialect": "postgres"
},
"test": {
"username": "root",
"password": "dj@mw@r3",
"database": "node_sequelize",
"host": "127.0.0.1",
"dialect": "postgres"
},
"production": {
"username": "root",
"password": "dj@mw@r3",
"database": "node_sequelize",
"host": "127.0.0.1",
"dialect": "postgres"
}
}
We use the same configuration for all the environment because we are using the same machine, server, and database for this tutorial.
Before run and test connection, make sure you have created a database as described in the above configuration. You can use the `psql` command to create a user and database.
psql postgres --u postgres
Next, type this command for creating a new user with password then give access for creating the database.
postgres-# CREATE ROLE djamware WITH LOGIN PASSWORD 'dj@mw@r3';
postgres-# ALTER ROLE djamware CREATEDB;
Quit `psql` then log in again using the new user that previously created.
postgres-# \q
psql postgres -U djamware
Enter the password, then you will enter this `psql` console.
psql (9.5.13)
Type "help" for help.
postgres=>
Type this command to creating a new database.
postgres=> CREATE DATABASE book_store;
Then give that new user privileges to the new database then quit the `psql`.
postgres=> GRANT ALL PRIVILEGES ON DATABASE book_store TO djamware;
postgres=> \q
Create or Generate Sequelize Models and Migrations
We will use Sequelize-CLI to generate a new mode and migrationl. Type this command to create a model for 'Book'.
sequelize model:generate --name Book --attributes isbn:string,title:string,author:string,description:string,publishedYear:integer,publisher:string
That commands will generate models and migration files. The content of the model file looks like this.
'use strict';
module.exports = (sequelize, DataTypes) => {
const Book = sequelize.define('Book', {
isbn: DataTypes.STRING,
title: DataTypes.STRING,
author: DataTypes.STRING,
description: DataTypes.STRING,
publishedYear: DataTypes.INTEGER,
publisher: DataTypes.STRING
}, {});
Book.associate = function(models) {
// associations can be defined here
};
return Book;
};
And the migration file looks like this.
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Books', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
isbn: {
type: Sequelize.STRING
},
title: {
type: Sequelize.STRING
},
author: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
publishedYear: {
type: Sequelize.INTEGER
},
publisher: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Books');
}
};
Finally, for migrations, there's nothing to change and they all ready to generate the table to the PostgreSQL Database. Type this command to generate the table to the database.
sequelize db:migrate
Install GraphQL Modules and Dependencies
Now, the GraphQL time. Type this command to install GraphQL modules and it's dependencies.
npm install express express-graphql graphql graphql-date cors --save
Next, open and edit `app.js` then declare all of those modules and dependencies.
var graphqlHTTP = require('express-graphql');
var schema = require('./graphql/bookSchemas');
var cors = require("cors");
The schema is not created yet, we will create it in the next steps. Next, add these lines of codes for configuring GraphQL that can use over HTTP.
app.use('*', cors());
app.use('/graphql', cors(), graphqlHTTP({
schema: schema,
rootValue: global,
graphiql: true,
}));
That's configuration are enabled CORS and the GraphiQL. GraphiQL is the user interface for testing GraphQL query.
Create GraphQL Schemas for the Book
Create a folder at the server folder for hold GraphQL Schema files then create a Javascript file for the schema.
mkdir graphql
touch graphql/bookSchemas.js
Next, open and edit `server/graphql/bookSchemas.js` then declares all required modules and models.
var GraphQLSchema = require('graphql').GraphQLSchema;
var GraphQLObjectType = require('graphql').GraphQLObjectType;
var GraphQLList = require('graphql').GraphQLList;
var GraphQLObjectType = require('graphql').GraphQLObjectType;
var GraphQLNonNull = require('graphql').GraphQLNonNull;
var GraphQLID = require('graphql').GraphQLID;
var GraphQLString = require('graphql').GraphQLString;
var GraphQLInt = require('graphql').GraphQLInt;
var GraphQLDate = require('graphql-date');
var BookModel = require('../models').Book;
Create a GraphQL Object Type for Book models.
var bookType = new GraphQLObjectType({
name: "book",
fields: function() {
return {
id: {
type: GraphQLInt
},
isbn: {
type: GraphQLString
},
title: {
type: GraphQLString
},
author: {
type: GraphQLString
},
description: {
type: GraphQLString
},
publishedYear: {
type: GraphQLInt
},
publisher: {
type: GraphQLString
},
createdAt: {
type: GraphQLDate
},
updatedAt: {
type: GraphQLDate
}
};
}
});
Next, create a GraphQL query type that calls a list of book and single book by ID.
var queryType = new GraphQLObjectType({
name: 'Query',
fields: function () {
return {
books: {
type: new GraphQLList(bookType),
resolve: function () {
const books = BookModel.findAll({
order: [
['createdAt', 'DESC']
],
})
if (!books) {
throw new Error('Error')
}
return books
}
},
book: {
type: bookType,
args: {
id: {
name: 'id',
type: GraphQLString
}
},
resolve: function (root, params) {
const bookDetails = BookModel.findByPk(params.id).exec()
if (!bookDetails) {
throw new Error('Error')
}
return bookDetails
}
}
}
}
});
Finally, exports this file as GraphQL schema by adding this line at the end of the file.
module.exports = new GraphQLSchema({query: queryType});
Add GraphQL Mutation to the Schema
For completing CRUD (Create, Read, Update, Delete) operation of the GraphQL, we need to add a mutation that contains create, update and delete operations. Open and edit `graphql/bookSchemas.js` then add this mutation as GraphQL Object Type.
var mutation = new GraphQLObjectType({
name: 'Mutation',
fields: function () {
return {
addBook: {
type: bookType,
args: {
isbn: {
type: new GraphQLNonNull(GraphQLString)
},
title: {
type: new GraphQLNonNull(GraphQLString)
},
author: {
type: new GraphQLNonNull(GraphQLString)
},
description: {
type: new GraphQLNonNull(GraphQLString)
},
publishedYear: {
type: new GraphQLNonNull(GraphQLInt)
},
publisher: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: function (root, params) {
const bookModel = new BookModel(params);
const newBook = bookModel.save();
if (!newBook) {
throw new Error('Error');
}
return newBook
}
},
updateBook: {
type: bookType,
args: {
id: {
name: 'id',
type: new GraphQLNonNull(GraphQLInt)
},
isbn: {
type: new GraphQLNonNull(GraphQLString)
},
title: {
type: new GraphQLNonNull(GraphQLString)
},
author: {
type: new GraphQLNonNull(GraphQLString)
},
description: {
type: new GraphQLNonNull(GraphQLString)
},
publishedYear: {
type: new GraphQLNonNull(GraphQLInt)
},
publisher: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(root, params) {
return BookModel
.findByPk(params.id)
.then(book => {
if (!book) {
throw new Error('Not found');
}
return book
.update({
isbn: params.isbn || book.isbn,
title: params.title || book.title,
author: params.author || book.author,
description: params.description || book.description,
publishedYear: params.publishedYear || book.publishedYear,
publisher: params.publisher || book.publisher,
})
.then(() => { return book; })
.catch((error) => { throw new Error(error); });
})
.catch((error) => { throw new Error(error); });
}
},
removeBook: {
type: bookType,
args: {
id: {
type: new GraphQLNonNull(GraphQLInt)
}
},
resolve(root, params) {
return BookModel
.findByPk(params.id)
.then(book => {
if (!book) {
throw new Error('Not found');
}
return book
.destroy()
.then(() => { return book; })
.catch((error) => { throw new Error(error); });
})
.catch((error) => { throw new Error(error); });
}
}
}
}
});
Finally, add this mutation to the GraphQL Schema exports like below.
module.exports = new GraphQLSchema({query: queryType, mutation: mutation});
Test GraphQL using GraphiQL
To test the queries and mutations of CRUD operations, re-run again the Express.js app then open the browser. Go to this address `http://localhost:3000/graphql` to open the GraphiQL User Interface.
To get the list of books, replace all of the text on the left pane with this GraphQL query then click the Play button.
To get a single book by ID, use this GraphQL query.
{
book(id: 1) {
id
isbn
title
author
description
publishedYear
publisher
updatedAt
}
}
To add a book, use this GraphQL mutation.
mutation {
addBook(
isbn: "12345678",
title: "Whatever this Book Title",
author: "Mr. Bean",
description: "The short explanation of this Book",
publisher: "Djamware Press",
publishedYear: 2019
) {
updatedAt
}
}
You will the response at the right pane like this.
{
"data": {
"addBook": {
"updatedAt": "2019-02-26T13:55:39.160Z"
}
}
}
To update a book, use this GraphQL mutation.
mutation {
updateBook(
id: 1,
isbn: "12345678221",
title: "The Learning Curve of GraphQL",
author: "Didin J.",
description: "The short explanation of this Book",
publisher: "Djamware Press",
publishedYear: 2019
) {
id,
updatedAt
}
}
You will see the response in the right pane like this.
{
"data": {
"updateBook": {
"id": 1,
"updated_date": "2019-02-26T13:58:35.811Z"
}
}
}
To delete a book by ID, use this GraphQL mutation.
mutation {
removeBook(id: 1) {
id
}
}
You will see the response in the right pane like this.
{
"data": {
"removeBook": {
"id": 1
}
}
}
Create Vue 2 Application
We will use Vue-CLI to create a Vue 2 web app, to install Vue-CLI type this command from the Terminal or Node command line.
sudo npm install -g @vue/cli
or
yarn global add @vue/cli
Next, check the version to make sure that you have the 3.x version of Vue-CLI.
vue --version
3.7.0
Next, create a new Vue.js project by type this command.
vue create client
For now, use the default for every question that shows up in the Terminal. Next, go to the newly created folder.
cd ./client
To make sure that created Vue.js project working, type this command to run the Vue.js application.
npm run serve
or
yarn serve
You will see this page when open `http://localhost:8080/` in the browser.
Install/Configure the Required Modules, Dependencies, and Router
Now, we have to install and configure all of the required modules and dependencies. Type this command to install the modules.
npm install apollo-boost vue-apollo graphql-tag graphql vue-router --save
Next, open and edit `src/main.js` then add these imports.
import ApolloClient from "apollo-boost";
import VueApollo from "vue-apollo";
Add these constant variables then register `VueApollo` in Vue 2 app.
const apolloClient = new ApolloClient({
uri: 'http://localhost:3000/graphql'
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient
});
Vue.use(VueApollo);
new Vue({
apolloProvider,
render: h => h(App)
}).$mount('#app')
To register or create routes for the whole application navigation, create a router folder and `index.js` file.
mkdir src/router
touch src/router/index.js
Open and edit `src/router/index.js` then add these imports.
import VueRouter from 'vue-router'
import BookList from '@/components/BookList'
import ShowBook from '@/components/ShowBook'
import AddBook from '@/components/AddBook'
import EditBook from '@/components/EditBook'
Add the router to each component or page.
export default new VueRouter({
routes: [
{
path: '/',
name: 'BookList',
component: BookList
},
{
path: '/show-book/:id',
name: 'ShowBook',
component: ShowBook
},
{
path: '/add-book',
name: 'AddBook',
component: AddBook
},
{
path: '/edit-book/:id',
name: 'EditBook',
component: EditBook
}
]
})
Add Vue files for above-registered components or pages.
touch src/components/BookList.vue
touch src/components/ShowBook.vue
touch src/components/AddBook.vue
touch src/components/EditBook.vue
Finally, add or register this router file to `src/main.js` by adding these imports.
import VueRouter from 'vue-router'
import router from './router'
Register the Vue-Router after `Vue.config`.
Vue.use(VueRouter)
Modify `new Vue` to be like this.
new Vue({
apolloProvider,
router,
render: h => h(App)
}).$mount('#app')
Create Bootstrap-Vue Component to Display List of Books
Before create or show data to the views, we have to add Bootstrap-Vue. Type this command to install the module.
npm i bootstrap-vue
Next, open and edit `src/main.js` then add these imports.
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Add this line after `Vue.config`.
Vue.use(BootstrapVue);
Now, open and edit `src/components/BookList.vue` then add this template tags that contain a bootstrap-vue table.
<template>
<b-row>
<b-col cols="12">
<h2>
Book List
<b-link href="#/add-Book">(Add Book)</b-link>
</h2>
<b-table striped hover :items="books" :fields="fields">
<template slot="actions" scope="row">
<b-btn size="sm" @click.stop="details(row.item)">Details</b-btn>
</template>
</b-table>
</b-col>
</b-row>
</template>
Next, add the `script` tag for hold all Vue 2 codes.
<script></script>
Inside the script tag, add these imports.
import gql from "graphql-tag";
import router from "../router";
Declare the constant variables for GraphQL query.
const GET_BOOKS = gql`
{
books {
id
title
author
}
}
`;
Add the main Vue 2 export that contains Vue-Apollo calls that filled Vue 2 data.
export default {
name: "BookList",
apollo: {
books: {
query: GET_BOOKS,
pollInterval: 300
}
},
data() {
return {
fields: {
title: { label: "Title", sortable: true, class: "text-left" },
author: { label: "Author", sortable: true, class: "text-left" },
actions: { label: "Action", class: "text-center" }
},
books: []
};
},
methods: {
details(book) {
router.push({ name: "ShowBook", params: { id: book.id } });
}
}
};
Finally, add the `style` tag for styling the template.
<style>
.table {
width: 96%;
margin: 0 auto;
}
</style>
Create Bootstrap-Vue Component to Show and Delete Books
To show the book details that contains all book detail, edit and delete buttons, open and edit `src/components/ShowBook.vue` then add these template tags that contain a Bootstrap-Vue component for display the details.
<template>
<b-row>
<b-col cols="12">
<h2>
Book List
<b-link href="#/">(Book List)</b-link>
</h2>
<b-jumbotron>
<template slot="header">{{book.title}}</template>
<template slot="lead">
ISBN: {{book.isbn}}
<br>
Author: {{book.author}}
<br>
Description: {{book.description}}
<br>
Published Year: {{book.publishedYear}}
<br>
Publisher: {{book.publisher}}
<br>
Update At: {{book.updatedAt}}
<br>
</template>
<hr class="my-4">
<b-btn class="edit-btn" variant="success" @click.stop="editBook(book.id)">Edit</b-btn>
<b-btn variant="danger" @click.stop="deleteBook(book.id)">Delete</b-btn>
</b-jumbotron>
</b-col>
</b-row>
</template>
Next, add the script tag.
<script></script>
Inside the script tag, add these imports.
import gql from "graphql-tag";
import router from "../router";
Declare the constant variables that handle get a single book and delete book queries.
const GET_BOOK = gql`
query book($bookId: Int) {
book(id: $bookId) {
id
isbn
title
author
description
publishedYear
publisher
updatedAt
}
}
`;
const DELETE_BOOK = gql`
mutation removeBook($id: Int!) {
removeBook(id: $id) {
id
}
}
`;
Inside main Vue export, add all required functions, variables, and Vue-Apollo function.
export default {
name: "ShowBook",
data() {
return {
book: '',
bookId: parseInt(this.$route.params.id)
};
},
apollo: {
book: {
query: GET_BOOK,
pollInterval: 300,
variables() {
return {
bookId: this.bookId
};
}
}
},
methods: {
editBook(id) {
router.push({
name: "EditBook",
params: { id: id }
});
},
deleteBook(id) {
this.$apollo
.mutate({
mutation: DELETE_BOOK,
variables: {
id: id
}
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
}
}
};
Finally, add the style tags to give the view some styles.
<style>
.jumbotron {
padding: 2rem;
}
.edit-btn {
margin-right: 20px;
width: 70px;
}
</style>
Create Bootstrap-Vue Component to Add a New Book
To add a new book, open and edit `src/components/AddBook.vue` then add this Vue 2 template tag that contains a Bootstrap-Vue form.
<template>
<b-row>
<b-col cols="12">
<h2>
Add Book
<b-link href="#/">(Book List)</b-link>
</h2>
<b-jumbotron>
<b-form @submit="onSubmit">
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter ISBN"
>
<b-form-input id="isbn" v-model.trim="book.isbn"></b-form-input>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Title"
>
<b-form-input id="title" v-model.trim="book.title"></b-form-input>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Author"
>
<b-form-input id="author" v-model.trim="book.author"></b-form-input>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Description"
>
<b-form-textarea
id="description"
v-model="book.description"
placeholder="Enter something"
:rows="2"
:max-rows="6"
>{{book.description}}</b-form-textarea>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Publisher"
>
<b-form-input id="publisher" v-model.trim="book.publisher"></b-form-input>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Published Year"
>
<b-form-input type="number" id="publishedYear" v-model.trim="book.publishedYear"></b-form-input>
</b-form-group>
<b-button type="submit" variant="primary">Save</b-button>
</b-form>
</b-jumbotron>
</b-col>
</b-row>
</template>
Next, add the script tag.
<script></script>
Inside the script, tag adds Vue 2 codes that contain Vue-Apollo GraphQL mutation to save a new book.
import gql from "graphql-tag";
import router from "../router";
const ADD_BOOK = gql`
mutation AddBook(
$isbn: String!
$title: String!
$author: String!
$description: String!
$publisher: String!
$publishedYear: Int!
) {
addBook(
isbn: $isbn
title: $title
author: $author
description: $description
publisher: $publisher
publishedYear: $publishedYear
) {
id
}
}
`;
export default {
name: "AddBook",
data() {
return {
book: {}
};
},
methods: {
onSubmit(evt) {
evt.preventDefault();
this.$apollo
.mutate({
mutation: ADD_BOOK,
variables: {
isbn: this.book.isbn,
title: this.book.title,
author: this.book.author,
description: this.book.description,
publisher: this.book.publisher,
publishedYear: parseInt(this.book.publishedYear)
}
})
.then(data => {
console.log(data);
router.push({ name: "BookList" });
})
.catch(error => {
console.error(error);
});
}
}
};
Finally, give the view a style by adding the style tag.
<style>
.jumbotron {
padding: 2rem;
}
</style>
Create Bootstrap-Vue Component to Edit a Book
To edit a book after getting single book data, open and edit `src/components/EditBook.vue` then add this Vue 2 template that contains a Bootstrap-Vue form.
<template>
<b-row>
<b-col cols="12">
<h2>
Edit Book
<router-link :to="{ name: 'ShowBook', params: { id: bookId } }">(Show Book)</router-link>
</h2>
<b-jumbotron>
<b-form @submit="onSubmit">
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter ISBN"
>
<b-form-input id="isbn" v-model.trim="book.isbn"></b-form-input>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Title"
>
<b-form-input id="title" v-model.trim="book.title"></b-form-input>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Author"
>
<b-form-input id="author" v-model.trim="book.author"></b-form-input>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Description"
>
<b-form-textarea
id="description"
v-model="book.description"
placeholder="Enter something"
:rows="2"
:max-rows="6"
>{{book.description}}</b-form-textarea>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Publisher"
>
<b-form-input id="publisher" v-model.trim="book.publisher"></b-form-input>
</b-form-group>
<b-form-group
id="fieldsetHorizontal"
horizontal
:label-cols="4"
breakpoint="md"
label="Enter Published Year"
>
<b-form-input type="number" id="publishedYear" v-model.trim="book.publishedYear"></b-form-input>
</b-form-group>
<b-button type="submit" variant="primary">Update</b-button>
</b-form>
</b-jumbotron>
</b-col>
</b-row>
</template>
Next, add the script tag that contains all required Vue 2 codes with get data and update function.
<script>
import gql from "graphql-tag";
import router from "../router";
const GET_BOOK = gql`
query book($bookId: Int) {
book(id: $bookId) {
id
isbn
title
author
description
publishedYear
publisher
}
}
`;
const UPDATE_BOOK = gql`
mutation updateBook(
$id: Int!
$isbn: String!
$title: String!
$author: String!
$description: String!
$publisher: String!
$publishedYear: Int!
) {
updateBook(
id: $id
isbn: $isbn
title: $title
author: $author
description: $description
publisher: $publisher
publishedYear: $publishedYear
) {
updatedAt
}
}
`;
export default {
name: "EditBook",
data() {
return {
bookId: this.$route.params.id,
book: {}
};
},
apollo: {
book: {
query: GET_BOOK,
variables() {
return {
bookId: this.bookId
};
}
}
},
methods: {
onSubmit(evt) {
evt.preventDefault();
this.$apollo
.mutate({
mutation: UPDATE_BOOK,
variables: {
id: parseInt(this.book.id),
isbn: this.book.isbn,
title: this.book.title,
author: this.book.author,
description: this.book.description,
publisher: this.book.publisher,
publishedYear: parseInt(this.book.publishedYear)
}
})
.then(data => {
console.log(data);
router.push({
name: "ShowBook",
params: { id: this.$route.params.id }
});
})
.catch(error => {
console.error(error);
});
}
}
};
</script>
Finally, give the view some style by adding the style tag.
<style>
.jumbotron {
padding: 2rem;
}
</style>
Run and Test GraphQL CRUD from the Vue 2 Application
We assume the PostgreSQL server already running, so you just can run Node/Express.js application and Vue 2 app in the separate terminal tabs.
nodemon
cd client
npm run serve
Next, open the browser then go to this address `localhost:8080` and you should see these pages.
That it's, the Node, Express, PostgreSQL, Vue 2 and Graphql CRUD Web App. You can find the full source code on our GitHub.
That just the basic. If you need more deep learning about Node.js, Express.js, PostgreSQL, Vue.js and GraphQL or related you can take the following cheap course:
- Node. JS
- Learning Express JS: Creating Future Web Apps & REST APIs
- Angular + NodeJS + PostgreSQL + Sequelize
- Build 7 Real World Applications with Vue. js
- Full-Stack Vue with GraphQL - The Ultimate Guide
Thanks!