Vue 3 is the up and coming version of the popular Vue front end framework.
We can pair that we the back end of our choice to create an app that we want to create.
In this article, we’ll create a Vue 3 front end that’s paired with an Express back end that uses Sematext for logging.
Get Started
We can start by creating our scaffold.
First, we create a project folder with a backend
and frontend
folders inside.
Then we can go into our backend
folder and create our Express app.
We can use the Express generator package to make this easy.
To run it, we run:
npx express-generator
in our backend
folder to add the files.
Then we run:
npm i
to install the packages.
Next, we create our Vue 3 front end project.
To do that, we go into the project folder root and run:
npm init vite-app frontend
This will create the project files in the frontend
folder and install the required packages.
Backend
Now we have the scaffolding for both apps, we can work on the back end app.
We install a few more packages that we’ll need for our back end.
To do that we run:
npm i cors dotenv sematext-agent-express sqlite3
cors
is a package to let us communicate between front end and back end regardless of the domain they’re in.
dotenv
lets us read the environment variables.
sematext-agent-express
is the Sematext package for Express apps.
sqlite3
lets us save data to a SQLite database.
Next, we create a todo.js
file in the routes
folder.
And then in app.js
, we change the existing code to:
require('dotenv').config()
const { stHttpLoggerMiddleware } = require('sematext-agent-express')
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 todosRouter = require('./routes/todo');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(cors())
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(stHttpLoggerMiddleware)
app.use('/', indexRouter);
app.use('/todos', todosRouter);
// 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;
We added the todosRouter
middleware to let us use the todos
route file.
And we have:
app.use('/todos', todosRouter);
to use the todosRouter
file.
Also, we have:
require('dotenv').config()
to read the .env
file which we’ll need for reading the API token for Sematext.
We add the Sematext Express middleware into our app with:
app.use(stHttpLoggerMiddleware)
so we can use its logging capabilities in our app.
We also have:
var cors = require('cors');
to let us do cross-domain communication.
Next, we go to todo.js
and replace the existing code with:
var express = require('express');
var sqlite3 = require('sqlite3').verbose();
const { stLogger } = require('sematext-agent-express')
var db = new sqlite3.Database('./todos.db');
var router = express.Router();
router.get('/', (req, res) => {
db.serialize(() => {
db.all("SELECT * FROM todos", (err, results) => {
if (err) {
stLogger.error(err);
return res.status(500).send(err);
}
stLogger.info(results);
res.json(results);
});
});
});
router.post('/', (req, res) => {
const { name } = req.body;
db.serialize(() => {
db.run("INSERT INTO todos (name) VALUES (?)", name, (err, results) => {
if (err) {
stLogger.error(err);
return res.status(500).send(err);
}
stLogger.info(results);
res.json(results);
});
});
});
router.put('/:id', (req, res) => {
const { id } = req.params;
const { name } = req.body;
db.serialize(() => {
db.run("UPDATE todos SET name = ? WHERE id = ?", name, id, (err, result) => {
if (err) {
stLogger.error(err);
return res.status(500).send(err);
}
stLogger.info(result);
res.json(result);
});
});
});
router.delete('/:id', (req, res) => {
const { id } = req.params;
db.serialize(() => {
db.run("DELETE FROM todos WHERE id = ?", id, (err, results) => {
if (err) {
stLogger.error(err);
return res.status(500).send(err);
}
stLogger.info(results);
res.json(results);
});
});
});
module.exports = router;
We add the routes with the router
methods.
We use the stLogger
object from the sematext-agent-express
package to let us do the logging.
In each route, we have the stLogger.error
method to log errors.
And we have stLogger.info
to log other information like the database results.
Each time the route middleware runs, we’ll see something logged.
If there’s an error we return that as the response back to the client with the if
statements.
We call db.serialize
to run database queries in sequence.
And we use db.run
to run INSERT
, UPDATE
, DELETE
statements.
To run SELECT
queries, we run db.all
to get all the rows.
We use parameterized queries so that values are escaped before we run the queries.
req.params
gets the request parameters from the URL.
req.body
gets the request body.
We have:
var db = new sqlite3.Database('./todos.db');
The todos.db
file will be created if it doesn’t exist.
Once it’s created we can open the file by using the SQLite browser from https://sqlitebrowser.org/.
We can download the file and install it.
Then we can open the todos.db
file and run:
CREATE TABLE todos (id INTEGER PRIMARY KEY, name TEXT NOT NULL)
to create the todos
table so we can write to it.
Now the SQL statements in our code should run properly.
Then we go into the backend
folder and create the .env
file.
And then we add the LOGS_TOKEN
key so that we can use Sematext for logging:
LOGS_TOKEN=YOUR_OWN_KEY_FROM_SEMATEXT
We can get the key by signing up for an account by going to https://apps.sematext.com/ui/login/.
Once we’re in, we can click on Apps on the left menu.
Then click on New App on the top right to create our app.
Once you see the app entry, we click on the menu button on the right side of the row, and click Integrations.
Then we see Node.js on the left side of what’s open and follow the instructions from there.
Front End
Now that the back end is done, we can move onto the front end.
First, we install the axios
HTTP client so that we can make HTTP requests.
We can do that by running:
npm i axios
We go to the frontend/components
folder and create a Todo.vue
file.
Then we add:
<template>
<form @submit.prevent="save">
<input type="text" v-model="todo.name" />
<input type="submit" value="save" />
<button type="button" @click="deleteTodo" v-if='todo.id'>delete</button>
</form>
</template>
<script>
import axios from "axios";
const APIURL = "http://localhost:3000";
export default {
name: "Todo",
props: {
todo: {
type: Object,
default() {
return {};
},
},
},
data() {
return {
name: "",
};
},
methods: {
async save() {
const { name } = this.todo;
if (this.todo.id) {
await axios.put(`${APIURL}/todos/${this.todo.id}`, { name });
} else {
await axios.post(`${APIURL}/todos`, { name });
}
this.$emit("saved-todo");
},
async deleteTodo() {
const { data } = await axios.delete(`${APIURL}/todos/${this.todo.id}`);
this.$emit("saved-todo");
},
},
};
</script>
to it.
We use the form to add or edit the files.
Also, we have a delete button to delete the todos.
The save
method makes a PUT request to our back end if the todo
has an id
.
This mneans it’s an existing entry, so we make a PUT request.
Otherwise, we make a POST request.
Once they’re successful, then we emit the save-todos
event so that we can get the latest data later.
Also, we have the deleteTodo
method so that we can make a DELETE request to delete a item.
The Todo
component takes a todo
prop, which is optional.
We have the default
method to return the default value for it.
The v-if
in the template checks if the todo.id
exists.
If it does, then it’s displayed so we can call delete
to delete the todo item.
We also have a @subnmit.prevent
directive to let us submit our form and run the save
method.
Next we work on the App.vue
file.
We write:
<template>
<div>
<Todo @saved-todo="getTodos"></Todo>
<Todo @saved-todo="getTodos" v-for="t of todos" :todo="t" :key="t.id"></Todo>
</div>
</template>
<script>
import axios from "axios";
import Todo from "./components/Todo.vue";
const APIURL = "http://localhost:3000";
export default {
name: "App",
components: {
Todo,
},
data() {
return {
todos: [],
};
},
beforeMount() {
this.getTodos();
},
methods: {
async getTodos() {
const { data } = await axios.get(`${APIURL}/todos`);
this.todos = data;
},
},
};
</script>
We add the Todo
component that we created earlier.
The first one is for adding the todo item.
The getTodos
method lets us get the todo items.
It’s run when the saved-todo
event is emitted.
We listen to the event with @saved-todo
on the template.
Also, we call getTodos
in the beforeMount
hook so that we can get the todos when the page loads.
Running Our App
Once this is done, we can run our app.
We first go into the backend
folder and run:
npm start
Then we go into the frontend
folder and run:
npm run dev
Once that’s done, we can go to http://localhost:3001
to see the app.
Now we should see:
when we go to http://localhost:3001
and when we do something in our app, we see something like:
logged in Sematext.
It’ll log your activities.
Conclusion
Using Sematext with an Express app is easy.
We just use the sematext-express-agent
package to let us log with it.
Creating a Vue front end is also easy.
Vue 3 is the up and coming version of Vue. It’s almost ready for production.