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.
The following tools, frameworks, and modules are required for this tutorial:
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.
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.
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
We will use Sequelize-CLI to generate a new model. 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
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 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});
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});
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](http://localhost:3000/graphql "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
}
}
}
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/](http://localhost:8080/ "http://localhost:8080/")
in the browser.
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')
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>
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>
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>
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>
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.
#node-js #express #postgresql #vue-js #graphql #web-development