HarperDB is a scalable SQL and NoSQL database that we can use to store data.
We can make queries including more complex ones like joins.
It’s useful for creating reports and data analysis with the versatility of its API.
It lets us control the database via HTTP requests instead of direct queries.
In this article, we’ll look at how to create a simple web app with HarperDB, Express, and Vue.
Getting Started
We first install the Postman HTTP client.
Then we download the Postman collection from https://examples.harperdb.io/?version=latest.
It lets us look at what features are available and how to issue commands to use them.
Then we install HarperDB by installing Node 12.16.1.
If we use nvm, we can run nvm install 12.16.1
.
Then we switch to that version by running:
nvm use 12.16.1
Next, we install HarperDB by running:
npm install -g harperdb
Then we run it by running:
harperdb run
Creating Our App
Next, we create our project with Express and Vue.
It’ll let us read, create, update, and delete data about dogs.
To do that, we create a project folder with the backend
and frontend
folders.
In the backend
folder, we run:
npx express-generator
in the backend
folder.
Then we install the cors
middleware to let our front end use the API and axios
to talk to HarperDB by running:
npm i cors axios
in the same folder.
In the frontend
folder, we run:
npx vue create .
to create the Vue project.
We just go with the default options.
Back End
Now we can work on the back end.
To interact with HarperDB, we just make HTTP requests.
We first rename user.js
in the routes
folder to dog.js
.
Then in app.js
, we write:
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var cors = require('cors')var indexRouter = require('./routes/index');
var dogsRouter = require('./routes/dogs');var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(cors())
app.use('/', indexRouter);
app.use('/dogs', dogsRouter);// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
to add the dogs
route with:
var dogsRouter = require('./routes/dogs');
and:
app.use('/dogs', dogsRouter);
And we add the CORS middleware with:
var cors = require('cors')
and:
app.use('/dogs', dogsRouter);
Next in dog.js
, we create our routes.
To do that, we add:
var express = require('express');
var router = express.Router();
const axios = require('axios');
const HDB_ENDPOINT = 'http://localhost:9925';
const TOKEN = 'Basic SERCX0FETUlOOnBhc3N3b3Jk';
const headers = {
Authorization: TOKEN,
Accept: '*/*',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Content-Type': 'application/json'
}
router.get('/', async (req, res) => {
try {
const { data } = await axios.post(HDB_ENDPOINT, {
"operation": "sql",
"sql": `SELECT * FROM dev.dog`
}, {
headers
})
res.json(data);
} catch (error) {
console.log(error)
}});
router.get('/:id', async (req, res) => {
const { id } = req.params;
const { data } = await axios.post(HDB_ENDPOINT, {
"operation": "sql",
"sql": `SELECT * FROM dev.dog where id = ${id}`
}, {
headers
})
res.json(data);
});
router.post('/', async (req, res) => {
const { dog_name, owner_name, age, weight_lbs } = req.body;
const { data } = await axios.post(HDB_ENDPOINT, {
"operation": "insert",
"schema": "dev",
"table": "dog",
"records": [
{
dog_name, owner_name, age, weight_lbs
}
]
}, {
headers
})
res.json(data);
});
router.put('/:id', async (req, res) => {
const { id } = req.params;
const { dog_name, owner_name, age, weight_lbs } = req.body;
const { data } = await axios.post(HDB_ENDPOINT, {
"operation": "update",
"schema": "dev",
"table": "dog",
"records": [
{
id,
dog_name, owner_name, age, weight_lbs
}
]
}, {
headers
})
res.json(data);
});
router.delete('/:id', async (req, res) => {
try {
const { id } = req.params;
const { data } = await axios.post(HDB_ENDPOINT, {
"operation": "sql",
"sql": `DELETE FROM dev.dog WHERE id = '${id}'`
}, {
headers
})
res.json(data);
} catch (error) {
res.json(error)
}
});
module.exports = router;
to the file.
We have the get
routes that issue the select command.
The headers have the token, encoding, and content-type
headers.
The token is from the Postman sample, which we can use to interact with the local version of HarperDB.
We authenticate with the DB with the token.
If we’re using it in production, then we’ve to make the token configurable.
Then we issue our commands by making the HTTP request with the SQL command to retrieve and delete data.
To create and update, we just send the data we want to add or update.
The operation
property in the request payload will tell HarperDB what to do.
Front End
To build the front end, we add the DogForm.vue
file to the components
folder.
We add the form in the code.
We add:
<template>
<div>
<form @submit.prevent="submit">
<div>
<label>Dog Name</label>
<input v-model="dog.dog_name" name="dogName" type="text" />
</div>
<div>
<label>Owner Name</label>
<input v-model="dog.owner_name" name="ownerName" type="text" />
</div>
<div>
<label>Age</label>
<input v-model="dog.age" name="age" type="number" />
</div>
<div>
<label>Weight</label>
<input v-model="dog.weight_lbs" name="weight" type="number" />
</div>
<div>
<input type="submit" value="save" />
<button type="button" @click="remove">remove</button>
</div>
</form>
</div>
</template>
<script>
const axios = require("axios");
const APIURL = "http://localhost:3000/dogs";
export default {
name: "DogForm",
props: {
dog: {
type: Object,
default: () => ({})
}
},
data() {
return {};
},
methods: {
async submit() {
const { dog_name, owner_name, age, weight_lbs, id } = this.dog;
if (!id) {
await axios.post(APIURL, {
dog_name,
owner_name,
age,
weight_lbs
});
} else {
await axios.put(`${APIURL}/${id}`, {
dog_name,
owner_name,
age,
weight_lbs
});
}
this.$emit("submitted");
},
async remove() {
const { id } = this.dog;
await axios.delete(`${APIURL}/${id}`);
this.$emit("submitted");
}
}
};
</script>
We add the form to let us add, edit, or remove a dog data entry.
The props
has the type
object with the default value being an object.
We need to have a function that returns an object to specify the default value for objects.
Then we have our submit
method that makes a post request if there’s no id
, which means it’s new.
If it’s a production app, then we should add some form validation.
If there’s an id
, then we update the existing entry.
Also, we have the remove
method to remove an entry.
It’s called when we click remove.
All methods emit the submit event, which we listen to in App.vue
.
In App.vue
, we have:
<template>
<div id="app">
<DogForm @submitted="getDogs" />
<DogForm v-for="dog of dogs" :key="dog.id" :dog="dog" @submitted="getDogs" />
</div>
</template>
<script>
import DogForm from "./components/DogForm.vue";
const axios = require("axios");
const APIURL = "http://localhost:3000/dogs";
export default {
name: "App",
components: {
DogForm
},
data() {
return {
dogs: []
};
},
beforeMount() {
this.getDogs();
},
methods: {
async getDogs() {
const { data } = await axios.get(APIURL);
this.dogs = data;
}
}
};
</script>
We use the DogForm
for entering and editing entries.
They listen to the submitted
event so the new data will be retrieved.
We use the getDogs
method to get the data with a get request.
Run Our App
We go to the Postman collection and create the Dog schema and table.
We go to the QuickStart Examples folder in the collection and run the ‘Create dev Schema’ and ‘Create do Table’ requests in the order they’re listed.
Then we run npm start
in the backend
folder to start the Express app.
And we run npm run dev
to in the frontend
folder to run the front end app.
Now we get something like:
And we can enter what we want.
Conclusion
We can create database-driven apps with HarperDB easily.
The only difference is that we send commands to our database with HTTP requests instead of issuing commands directly.