Vue Elasticsearch With Node.js: Step-By-Step Guide

Here are the steps to implement elasticsearch with Node.js in Vue.

Step 1: Install Vue.js

We install Vue.js using Vue CLI. So if you have not installed Vue CLI, you can install it using the following command.

npm install -g @vue/cli

# OR

yarn global add @vue/cli

Create a new Vue.js project using the following command.

vue create vue-elastic 

Vue Elasticsearch Tutorial With Node.js From Scratch

Go into the project folder.

cd vue-elastic

Start the dev server using the following command.

npm run serve

Step 2: Create Bootstrap Form For Search

Install Bootstrap 4 using the following command.

npm install bootstrap --save

# or

yarn add bootstrap

Import the Bootstrap 4 file inside the src >> main.js file.

// main.js

import Vue from 'vue'
import App from './App.vue'

import '../node_modules/bootstrap/dist/css/bootstrap.min.css';

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

Replace the existing code with the following code inside App.vue file.

<!-- App.vue -->

<template>
  <div id="app" class="container">
    <div class="input-group input-group-lg bottom">
      <div class="input-group-prepend">
        <span class="input-group-text">Search</span>
      </div>
      <input type="text" class="form-control col-md-6">
    </div>
  </div>
</template>

<script>

export default {
  
}
</script>

<style>
  .bottom {
    margin-top: 50px;
    margin-left: 200px;
  }
</style>

Vue Elasticsearch example

The next step is to add the @keydown event to the search area.

We need to do this because when the user starts typing in the search box, we send a network request to the server with the search query and then get a result and display the result. So write the following code inside App.vue file.

// App.vue

<template>
  <div id="app" class="container">
    <div class="input-group input-group-lg bottom">
      <div class="input-group-prepend">
        <span class="input-group-text">Search</span>
      </div>
      <input type="text" 
        class="form-control col-md-6" 
        @keyup.prevent="search"
        v-model="query" />
    </div>
  </div>
</template>

<script>

export default {
  data() {
    return {
      query: ''
    }
  },
  methods: {
    search() {
      console.log(this.query);
      return this.query;
    }
  }
}
</script>

<style>
  .bottom {
    margin-top: 50px;
    margin-left: 200px;
  }
</style>

Step 3: Install Axios HTTP Promise-based library.

Type the following command to install Axios Promise-based HTTP library.

npm install axios vue-axios --save

# or

yarn add axios vue-axios

We have not created the node.js serve but will make it in a minute. But first, we set up axios to send a network request to the server.

Add the axios and vue-axios inside the  main.js file.

// main.js

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios';
import VueAxios from 'vue-axios';

import '../node_modules/bootstrap/dist/css/bootstrap.min.css'

Vue.config.productionTip = false

Vue.use(axios, VueAxios);

new Vue({
  render: h => h(App)
}).$mount('#app')

Finally, we can use Axios inside App.vue file.

// App.vue

export default {
  data() {
    return {
      query: '',
      data: []
    }
  },
  methods: {
    search() {
        axios.get("http://localhost:5000/search?q=" + this.query)
              .then(response => {
                console.log(response.data);
                this.data = response.data;
          })
    }
  }
}

We will send a network request to the node.js server at port 5000. It is not running because we have not created it yet.

Let us create the next step.

Step 4: Create a node.js web server.

Inside your vue-elastic project, create one folder called a server; inside that, create one file called server.js.

Install the following node.js dependencies using the following command.

npm install express body-parser elasticsearch cors --save

Write the following code inside a server.js file.

// server.js

const express = require('express');
const app = express();

const PORT = 5000;

app.listen(PORT, function() {
    console.log('Your node.js server is running on PORT: ',PORT);
});

Start this node.js server by typing the following command inside the server folder.

node server

Step 5: Install Elasticsearch on the local machine.

You do not need this step if you have installed Elasticsearch on the Mac. Otherwise, you can continue with this step. So type the following command to install Elasticsearch via homebrew.

brew install elasticsearch 

It will install it and now start the services using the following command.

brew services start elasticsearch

We need to connect our node.js application to the elastic server.

// server.js

const express = require('express');
const app = express();
const elasticsearch = require('elasticsearch');
const fs = require('fs');

const PORT = 5000;

const client = new elasticsearch.Client({
    host: '127.0.0.1:9200',
    log: 'error'
 });

 client.ping({ requestTimeout: 30000 }, function(error) {
    if (error) {
        console.error('elasticsearch cluster is down!');
    } else {
        console.log('Everything is ok');
    }
});

app.listen(PORT, function() {
    console.log('Your node.js server is running on PORT:',PORT);
});

Restart the node.js server and see the console. You can get it like this in the console.

Vue Express Elasticsearch example

Step 6: Create an index and indexing of data.

We need the data to index. So inside the server folder, create one file called data.json and add the following code. First, we will search for some of the Game of Thrones main characters.

[
    {
        "cast_name": "Tyrion Lannister",
        "og_name": "Peter Dinklage",
        "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/5/58/Tyrion_main_s7_e6.jpg/revision/latest?cb=20170818050344"
    },
    {
        "cast_name": "Daenerys Targaryen",
        "og_name": "Emilia Clarke",
        "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/5/5f/Daenerys_Dragonpit.jpg/revision/latest?cb=20171015095128"
    },
    {
        "cast_name": "Jon Snow",
        "og_name": "kit Harington",
        "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/a/a5/Profile-JonSnow-707.png/revision/latest?cb=20170828030553"
    },
    {
        "cast_name": "Cersei Lannister",
        "og_name": "Lena Heady",
        "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/c/c3/Profile-CerseiLannister.png/revision/latest?cb=20170828071355"
    },
    {
        "cast_name": "Sansa Stark",
        "og_name": "Sophie Turner",
        "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/7/7e/Sansastark706.jpg/revision/latest/scale-to-width-down/316?cb=20170828072803"
    },
    {
        "cast_name": "Arya Stark",
        "og_name": "Maisie Williams",
        "url": "https://upload.wikimedia.org/wikipedia/en/3/39/Arya_Stark-Maisie_Williams.jpg"
    },
    {
        "cast_name": "Jaime Lannister",
        "og_name": "Nikolaj Coster-Waldau",
        "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/e/eb/Jaime.jpg/revision/latest?cb=20170818052054"
    },
    {
        "cast_name": "Ramsay Bolton",
        "og_name": "Iwan Rheon",
        "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/d/d2/Ramsay_S06E09_RESZIED_FOR_INFOBOX.jpg/revision/latest?cb=20160622071734"
    },
    {
        "cast_name": "Petyr Baelish",
        "og_name": "Aidan Gillen",
        "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/9/9f/Profile-Littlefinger.png/revision/latest?cb=20170826005231"
    },
    {
        "cast_name": "Joffrey Baratheon",
        "og_name": "Jack Gleeson",
        "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/2/25/Joffrey_Season_4_Episode_2_TLATR.jpg/revision/latest?cb=20171105180252"
    }
]

We need to index these data. So let us create another file inside the server folder called elasticsearch.js and add the following code.

// elasticsearch.js

const elasticsearch = require('elasticsearch');
const fs = require('fs');

const client = new elasticsearch.Client({
    hosts: [ 'http://localhost:9200']
});

client.indices.create({
    index: 'vue-elastic'
}, function(error, response, status) {
    if (error) {
        console.log(error);
    } else {
        console.log("created a new index", response);
    }
});

const bulkIndex = function bulkIndex(index, type, data) {
    let bulkBody = [];
  
    data.forEach(item => {
      bulkBody.push({
        index: {
          _index: index,
          _type: type
        }
      });
      bulkBody.push(item);
    });
  
client.bulk({body: bulkBody})
    .then(response => {
      let errorCount = 0;
      response.items.forEach(item => {
        if (item.index && item.index.error) {
          console.log(++errorCount, item.index.error);
        }
      });
      console.log(
        `Successfully indexed ${data.length - errorCount}
         out of ${data.length} items`
      );
    })
    .catch(console.err);
  };

async function indexData() {
    const articlesRaw = await fs.readFileSync('./data.json');
    const articles = JSON.parse(articlesRaw);
    console.log(`${articles.length} items parsed from data file`);
    bulkIndex('vue-elastic', 'characters_list', articles);
  }

indexData();

Save the file, open the terminal inside the server folder, and hit the following command.

node elasticsearch.js

It will index the data. So now our Game of thrones characters are ready to query.

Step 7: Create the query

Inside the server.js file, add the following code.

// server.js

const express = require('express');
const app = express();
const elasticsearch = require('elasticsearch');
const cors = require('cors');
const bodyParser = require('body-parser');

app.set('port', process.env.PORT || 5000 );
app.use(cors());
app.use(bodyParser.json());

const client = new elasticsearch.Client({
    host: '127.0.0.1:9200',
    log: 'error'
 });

client.ping({ requestTimeout: 30000 }, function(error) {
    if (error) {
        console.error('elasticsearch cluster is down!');
    } else {
        console.log('Everything is ok');
    }
});

app.get('/search', function (req, res){
    
    let body = {
      size: 100,
      from: 0,
      query: {
        match: {
                cast_name: {
                    query: req.query['q'],
                    fuzziness: 4
                }
            }
        }
    }
   
    client.search({index:'vue-elastic', body:body, type:'characters_list'})
    .then(results => {
            res.send(results.hits.hits);
        
    })
    .catch(err=>{
      console.log(err)
      res.send([]);
    });
  
  })

app.listen(app.get('port'), function() {
    console.log('Your node.js server is running on PORT: ',app.get('port'));
});

When the user tries to search any phrase, it will get this get /search route, and we pass the query parameter.

According to the query, it will find the results and send them back to the client.

Step 8: Display the queried data.

Inside App.vue file, add the following code.

<!-- App.vue -->

<template>
  <div id="app" class="container">
    <div class="input-group input-group-lg bottom">
      <div class="input-group-prepend">
        <span class="input-group-text">Search</span>
      </div>
      <input type="text" 
        class="form-control col-md-6" 
        @keyup.prevent="search"
        v-model="query" />
    </div>
    
    <div  class="card-row">
    <div v-if="data" v-for="(value, index) in data" 
      :key="index"
      :ref="`card_${index}`"
         class="card">
      
      <img class="card-image" :src="value._source.url">
      
      <div class="card-footer">
        <h3 class="card-title">{{value._source.cast_name}}</h3>
        <p class="card-text">by 
          <span class="card-author">{{value._source.og_name}}</span>
        </p>
      </div>
    </div>
  </div>
  </div>
</template>


<script>

export default {
  data() {
    return {
      query: '',
      data: []
    }
  },
  methods: {
    search() {
        this.axios.get('http://localhost:5000/search?q='+this.query)
              .then(response => {
                this.data = response.data;
          })
    }
  }
}
</script>

<style>
body {
  background-color: #E1E7E7;
}

  .bottom {
    margin-top: 50px;
    margin-left: 200px;
  }
.card-row {
  display: flex;
  justify-content: center;
  align-items: center;  
  min-width: 780px;
  width: 100%;
  height: 500px;
}
.card {
  position: relative;
  background-color: #FFFFFF;
  height: 370px;
  width: 240px;
  margin: 10px;
  overflow: hidden;
  box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.5);
}
.card-image {
  position: absolute;
  left: -9999px;
  right: -9999px;
  margin: auto;
  height: 220px;
  min-width: 100%;
}
.card-footer {
  position: absolute;
  bottom: 0;
  height: 130px;
  padding: 10px 15px;
  font-family: Helvetica;
}
.card-text {
  font-size: 14px;
  color: rgba(0, 0, 0, 0.7);
}
.card-title {
  font-family: Serif;
}
.card-author {
  font-size: 14px;
  color: #BAB096;
}
</style>

Finally, we can search the data from Vue.js using elasticsearch.

That’s it for this tutorial. Find the GitHub Code here.

1 thought on “Vue Elasticsearch With Node.js: Step-By-Step Guide”

  1. I got an error, ” this.axios.get(‘http://localhost:8082/search?q=’+this.query)” –TypeError: Cannot read property ‘get’ of undefined.

    How to solve it ?Thanks

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.