Vue.js MySQL Data Retrieval for Web Apps

By the end of this course, you'll be able to bridge the gap between your server and your client-side Vue.js application. You will learn to establish secure connections to your VPS MySQL database, fetch data efficiently, and render it dynamically in your Vue.js components, all while leveraging the speed of a CDN.

Setting Up the VPS

Choosing Your Server

A Virtual Private Server, or VPS, is your own personal space on the internet. It's a slice of a powerful physical server, giving you the freedom to install software and configure everything just the way you like. This makes it a perfect home for your Vue.js application and its MySQL database.

When picking a VPS provider, think about a few key things. Cost is always a factor, but also consider the physical location of the server. Choosing a data center close to your users can make your app feel faster. You'll also need to select an operating system. We'll be using Debian Linux for our examples because it's popular, well-documented, and easy to use. Providers like DigitalOcean, Linode, OVHCloud, and Vultr are all solid options to explore.

Securing Your New VPS

Once you've signed up with a provider, they'll give you an IP address and a root password. The first thing you'll do is connect to your server using SSH (Secure Shell). On a Mac or Linux computer, you can do this from your terminal. On Windows, you might use a tool like PuTTY or the built-in SSH client.

ssh root@YOUR_SERVER_IP

After logging in as the root user, your first priority is security. You should immediately create a new user for your daily work. Operating directly as root is risky, as a single mistake can damage your system.

# Add a new user (replace 'your_username')
adduser your_username

# Give the new user administrative powers
usermod -aG sudo your_username

Now, log out and log back in as your new user:

ssh your_username@YOUR_SERVER_IP

Next, let's bring the server's software up to date. This ensures you have the latest security patches.

sudo apt update && sudo apt upgrade -y

The final basic security step is setting up a firewall. A firewall acts as a gatekeeper, controlling what traffic is allowed to enter your server. (not part of this course)

Installing the Database and Web Server

With our server secured, it's time to install the core software: Nginx as our web server and MySQL for the database. Nginx is a high-performance web server that will handle requests from users' browsers and serve our Vue app. MySQL is a reliable database system that will store your application's data.

Let's install both using Debian's package manager.

sudo apt install nginx mysql-server

After MySQL is installed, it's crucial to run the included security script. This script will walk you through setting a password for the database's root user, removing anonymous users, and disabling remote root login. These are all essential steps to protect your data.

sudo mysql_secure_installation

Accept the defaults for the questions the script asks, and be sure to choose a strong password for the MySQL root user. You'll need this password later to create a database for your application.

Configuring Nginx

Nginx is running, but it doesn't know about your app yet. We need to create a configuration file, called a "server block," to tell Nginx where to find your app's files and how to serve them. This file will live in the /etc/nginx/sites-available/ directory.

Let's create a new file named after your domain, for example, yourdomain.com.

sudo nano /etc/nginx/sites-available/yourdomain.com

Inside this file, you'll add a configuration that points to the directory where your Vue app's build files will eventually live. A typical location is /var/www/yourdomain.com/html. The try_files directive is particularly important for a Vue app (or any single-page application), as it ensures that all requests are routed to your index.html file, allowing Vue Router to handle the navigation.

💡

Domain to use with the classs

classe.parisweb.art.0A51.38.179.200
classe.parisweb.art.0AAAA2001:41d0:305:2100::46a9
server {
    listen 80;
    listen [::]:80;

    server_name yourdomain.com www.yourdomain.com;

    root /var/www/yourdomain.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Configuration adding the API endpoint

server {
    listen 80;
    listen [::]:80;

    server_name yourdomain.com www.yourdomain.com;

    # Frontend Vue
    root /var/www/yourdomain.com/html;
    index index.html;

    # API → Express
    location /api/ {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Vue SPA fallback
    location / {
        try_files $uri $uri/ /index.html;
    }
}

After saving that file, you need to enable it by creating a symbolic link to the sites-enabled directory. Then, test the configuration for any syntax errors.

# Create the link to enable the site
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/

# Test the configuration
sudo nginx -t

If the test is successful, you can reload Nginx to apply the changes.

sudo systemctl reload nginx

Finally, you'll need to create the directory you specified in the configuration file. This is where you'll upload your built Vue application files later on.

sudo mkdir -p /var/www/yourdomain.com/html

Your server is now fully configured. It's secure, has a database ready for data, and a web server prepared to serve your Vue application to the world

Building the API Backbone

Backbone : foundations or infrastructure

With your database server ready, the next step is to build a bridge that lets your Vue.js application communicate with it. You can't connect your frontend directly to the database—that would be a huge security risk. Instead, you'll build an Application Programming Interface, or API. The API acts as a middleman, receiving requests from the frontend, securely fetching data from the database, and sending it back.

"Back-End Development: The server-side part of web development that handles data storage, application logic, and server configuration."— The Evolution and Best Practices of Web Development in Modern TimesDEV Community (article to read)

This server-side logic is where all the important work happens. It validates user input, interacts with the database, and enforces business rules. To build this, we use a backend framework, which provides tools and structure to make development faster and more organized.

‼️ Choosing Your Framework

There are many excellent backend frameworks, each with its own strengths. For developers already familiar with JavaScript, Node.js with the Express framework is a popular choice. It allows you to use the same language on both the frontend and backend. It's known for being lightweight and fast, making it great for building APIs.

Another solid option is PHP, one of the oldest and most widely-used languages for web development. Frameworks like Laravel or Symfony provide robust features for building complex applications. For this guide, we'll focus on Node.js and Express because of its simplicity and synergy with a JavaScript frontend like Vue.

Let's see how simple it is to get a basic Express server running. The following code creates a web server that listens for requests on a specific port.

💡
During development, Express runs on the developer’s machine.
In production, the same Express application runs on the VPS, behind Nginx.
// Import the Express library
const express = require('express');

// Create an instance of an Express application
const app = express();

// Define the port the server will run on
const PORT = process.env.PORT || 3000;

// A simple route for the homepage
app.get('/', (req, res) => {
  res.send('API is running!');
});

// Start the server
app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

Express – Setup in 10 Minutes

Objective: launch a working Express API, locally and on a Debian VPS.

Install Node.js with curl

💡

curl is a command-line tool that allows you to:

  • retrieve data from a URL
  • send HTTP / HTTPS requests
  • transfer files between machines

Check if Node.js is already installed
node -v
npm -v

If a version ≥ 18 is displayed → OK.

⸻

Install Node.js (if not installed)

Recommended option NodeSource

curl -fsSL <https://deb.nodesource.com/setup_18.x> | sudo -E bash -
sudo apt install -y nodejs

Verify:

node -v
npm -v


⸻

Create the Express project

mkdir api
cd api
npm init -y

Install Express:

npm install express


⸻

 Create a minimal server (2 min)

Create the index.js file:

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

const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({ status: 'API is running' });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});


⸻
Start the server

node index.js

Test in the browser:

<http://localhost:3000>

or curl if you work from VPS
curl http://localhost:3000


Expected response:

{ "status": "API is running" }

Crafting RESTful Endpoints

An API is made up of endpoints, which are specific URLs that your frontend can send requests to. A common and predictable way to design these endpoints is by following REST (Representational State Transfer) principles. REST uses standard HTTP methods to perform actions on data. These actions are often referred to as CRUD operations: Create, Read, Update, and Delete.

HTTP MethodCRUD OperationDescription
GETReadRetrieve data from the server.
POSTCreateSend new data to the server to be created.
PUTUpdateReplace an existing piece of data on the server.
DELETEDeleteRemove data from the server.

For example, to get a list of all users, your frontend might make a GET request to /api/users. To add a new user, it would make a POST request to that same URL, but with the new user's data in the request body.

Let's implement a GET endpoint to fetch data from our MySQL database. We'll need a library like mysql2 to connect Node.js to MySQL.

Install the mysql2 package:

npm install mysql2
For learning purposes, everything is kept in a single file.
In real-world applications, the code would be split into multiple files.
💡

Change products with you table name

const mysql = require('mysql2/promise');

// Database connection configuration
const dbConfig = {
  host: 'your_vps_ip', or 127.0.0.1 if you are in the vps
  user: 'your_db_user',
  password: 'your_password',
  database: 'your_database'
};

// Endpoint to get all products
app.get('/api/products', async (req, res) => {
  try {
    const connection = await mysql.createConnection(dbConfig);
    const [rows, fields] = await connection.execute('SELECT * FROM products');
    res.json(rows); // Send data back as JSON
  } catch (error) {
    res.status(500).json({ error: 'Database query failed' });
  }
});

Securing Your API

An open API is a vulnerable API. You need to control who can access your data and what they can do with it. This involves two key concepts: authentication and authorization.

Authentication is about verifying identity. Is this user who they say they are? This is often handled with API keys, username/password logins, or tokens like JWT (JSON Web Tokens).

Authorization comes after authentication. Now that we know who the user is, what are they allowed to do? Can they view all data, or only their own? Can they delete records?

A simple way to protect routes is with middleware, which is just a function that runs before your main route handler. This function can check for a valid API key or token in the request headers.

// Simple authentication middleware
const checkApiKey = (req, res, next) => {
  const apiKey = req.headers['x-api-key'];
  if (apiKey && apiKey === 'YOUR_SECRET_API_KEY') {
    next(); // API key is valid, proceed to the route
  } else {
    res.status(401).json({ error: 'Unauthorized' });
  }
};

// Use the middleware to protect a route
app.get('/api/products', checkApiKey, async (req, res) => {
  // This code only runs if checkApiKey calls next()
  // ... database query logic here
});
💡

curl with token (-H = HTTP Header)

curl -H "x-api-key: YOUR_SECRET_API_KEY" http://127.0.0.1:3000/api/products

Finally, securing your API also means securing your database queries. Never construct SQL queries by directly inserting user input into the query string. This opens you up to SQL injection attacks, where a malicious user could alter your query to steal or delete data.

Always use parameterized queries or prepared statements. The database driver handles sanitizing the input, keeping your data safe.

Instead of "SELECT * FROM users WHERE id = " + userId, use parameterized queries like "SELECT * FROM users WHERE id = ?", and pass [userId] as a separate argument. This prevents attackers from manipulating the SQL command.

Now that you have a grasp of the fundamentals, let's test your knowledge.

QUIZ 1

Why is it considered a major security risk to connect a frontend application like Vue.js directly to a database?

A Frontend JavaScript lacks the capability to execute SQL commands.

B It exposes database credentials, allowing unauthorized access.

C It significantly slows down the application's performance.

D It makes the frontend code more complex and harder to maintain.

response = >

QUIZ 2

In a RESTful API, which HTTP method is conventionally used to retrieve a list of all users from a /api/usersendpoint?

A DELETE

B UPDATE

C POST

D GET

response = >

QUIZ 3

A user successfully logs into your application. The system then checks if this user has permission to delete a specific post. What is this second step called?

A Validation

B Sanitization

C Authorization

D Authentication

response = >

QUIZ 4

Which of the following is the most effective way to prevent SQL injection attacks?

A Manually checking user input for SQL keywords like 'SELECT' or 'DROP'.

B Using an API key for all database requests.

C Encrypting the entire database.

D Using parameterized queries or prepared statements.

response = >

QUIZ 5

An API middleware function runs after the main route handler to format the response.

A True

B False

response = >

With a secure API in place, you now have a powerful and safe way for your application to interact with its data.

Setting Up Your Vue Project

With the backend API ready to serve data, it's time to build the frontend. We'll use Vue.js, a progressive JavaScript framework perfect for creating user interfaces. Before starting, make sure you have Node.js installed on your machine, as it includes the Node Package Manager (npm).

The fastest way to start a new Vue project is with the official command-line tool. Open your terminal and run this command:

npm create vue@latest

This will kick off an interactive setup process. You'll be asked to name your project and choose optional features like TypeScript or Pinia for state management. For now, you can stick with the defaults.

Once it's done, navigate into your new project directory and install the necessary dependencies:

cd <your-project-name>
npm install

Finally, start the development server with npm run dev. This will launch your new Vue application, usually accessible at http://localhost:5173. You'll see a default welcome page, which we are about to change.

Crafting Components

Vue applications are built from components. Think of them as custom, reusable HTML elements. A component encapsulates its own template (HTML), logic (JavaScript), and styling (CSS), making your code organized and easy to maintain.

Let's create a component to display a list of items we'll fetch from our API. In your project, go to the src/components directory and create a new file named ItemsList.vue.

Vue single-file components (SFCs) use the .vue extension and typically contain three parts: <template><script>, and <style>.

Here’s a basic structure for ItemsList.vue. This component will define a list of items and then render them using a v-for directive, which is Vue's way of looping over an array.

<script setup>
import { ref } from 'vue'

// A reactive variable to hold our items
// We'll populate this from our API later
const items = ref([
  { id: 1, name: 'Sample Item 1' },
  { id: 2, name: 'Sample Item 2' }
])
</script>

<template>
  <div>
    <h2>Items</h2>
    <ul>
      <li v-for="item in items" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<style scoped>
/* Styles here only apply to this component */
ul {
  list-style-type: none;
  padding: 0;
}
li {
  padding: 8px 16px;
  border-bottom: 1px solid #ddd;
}
</style>

To see this component, we need to use it in our main application file, src/App.vue. Clear out the existing content in App.vue and replace it with this:

<script setup>
import ItemsList from './components/ItemsList.vue'
</script>

<template>
  <main>
    <ItemsList />
  </main>
</template>

<style scoped>
main {
  max-width: 600px;
  margin: 2rem auto;
  font-family: sans-serif;
}
</style>

Your browser should now display a simple list with our two sample items. We've successfully created and integrated a reusable component.

Connecting to the Backend

Our component currently uses hard-coded data. Let's make it dynamic by fetching data from the backend API you built earlier. We'll use Axios, a popular promise-based library for making HTTP requests.

First, add Axios to your project by running this command in your terminal:

npm install axios

Now, we'll modify ItemsList.vue to make an API call when the component is created. Vue provides lifecycle hooks for this purpose. The onMounted hook is perfect, as it runs right after the component has been added to the page.

Let's update ItemsList.vue one last time. We'll import Axios and the onMounted hook, remove the sample data, and fetch real data from our backend.

<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'

// This will hold the items fetched from the API
const items = ref([])
const isLoading = ref(true)
const error = ref(null)

// This function runs when the component is mounted
onMounted(async () => {
  try {
    // Make a GET request to your backend endpoint
    const response = await axios.get('/api/items')
    items.value = response.data
  } catch (err) {
    error.value = 'Failed to fetch items.'
    console.error(err)
  } finally {
    isLoading.value = false
  }
})
</script>

<template>
  <div>
    <h2>Items from Database</h2>
    <div v-if="isLoading">Loading...</div>
    <div v-else-if="error">{{ error }}</div>
    <ul v-else-if="items.length > 0">
      <li v-for="item in items" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
    <div v-else>No items found.</div>
  </div>
</template>

<style scoped>
/* ... styles remain the same ... */
</style>

Note that we've also added basic loading and error handling. The template now shows a "Loading..." message while the data is being fetched. If an error occurs, it displays an error message. Otherwise, it renders the list of items from your MySQL database just like before.

With these pieces in place, you have a functional frontend application that communicates with your backend, completing the full-stack connection.

Speed Up Your App with a CDN

Your Vue.js application is built, your API is ready, and your server is running. Now, how do you make sure your app is fast for every user, no matter where they are in the world?

The answer is a Content Delivery Network, or CDN. Think of your server (the VPS we set up earlier) as your application's home base. If a user in another country wants to load your site, their request has to travel all the way to your server and back. This trip takes time, which we call latency.

A CDN solves this by creating a global network of servers, called Points of Presence (PoPs). It copies your application's static files—like the HTML, CSS, and JavaScript that make up your Vue app—and stores them on these servers worldwide.

When a user visits your site, the CDN serves the files from the PoP closest to them. This drastically reduces latency and makes your application feel much faster. It also takes a huge load off your origin server, because it no longer has to handle every single request for your app's assets.

Choosing a CDN Provider

There are many CDN providers, but for a Vue.js application, some are particularly well-suited because they combine CDN services with features designed for modern web development. Here are a few popular choices:

  • Cloudflare: A powerhouse in the industry, known for its massive global network, robust security features, and a generous free plan that's perfect for getting started.
  • Netlify & Vercel: These platforms are built for developers. They specialize in hosting static sites and frontend frameworks like Vue. They offer seamless integration with Git, automatic builds, and a powerful CDN, making deployment incredibly simple.
  • AWS CloudFront & Google Cloud CDN: These are great options if you're already in the AWS or Google Cloud ecosystem. They are highly scalable and powerful but can be more complex to configure.

For most Vue.js projects, Netlify, Vercel, or Cloudflare are excellent starting points due to their ease of use and developer-friendly features.

Deploying Your Application

Deploying your Vue app to a CDN is straightforward. Since your Vue project compiles down to a set of static HTML, CSS, and JavaScript files, the process involves getting those files onto the CDN's network.

First, you need to build your application for production. In your project's terminal, run:

npm run build

This command creates a dist folder in your project directory. This folder contains everything needed to run your application. You'll then upload the contents of this distfolder to your chosen CDN provider.

Modern platforms like Netlify and Vercel make this even easier by connecting directly to your Git repository (like GitHub). When you push new code, they automatically run the build command and deploy the new dist folder to their CDN. This creates a smooth, automated workflow.

This automated process is often called Continuous Integration/Continuous Deployment (CI/CD). It's a key practice in modern web development that saves time and reduces errors.

Connecting to Your API

Your Vue app is now served from a lightning-fast CDN, but it still needs to talk to your backend API to get data. This brings up an important configuration: your API requests are not static. They must go to your origin server, not a CDN cache.

You need to configure your CDN to handle this. The general approach is to set up a rule that says, "If a request is for a static file (like .js.css, or an image), serve it from the cache. If it's a request to /api, pass it through to the origin server."

This is often called a "proxy" or "rewrite" rule. For example, in a Netlify configuration file (netlify.toml), you might write a rule like this:

# netlify.toml

[[redirects]]
  from = "/api/*"
  to = "https://your-vps-ip-address/api/:splat"
  status = 200

This rule tells Netlify that any request starting with /api/ should be forwarded to your backend server. The user's browser only ever communicates with the CDN's domain, which simplifies security and avoids Cross-Origin Resource Sharing (CORS) issues.

Finally, always ensure your communication is secure. Your CDN provider should give you a free SSL certificate for your domain, which encrypts traffic between the user and the CDN. You should also have an SSL certificate on your VPS to encrypt the connection between the CDN and your backend API. This ensures data is secure every step of the way.

"By caching content closer to end-users, CDNs reduce the round-trip time (latency) required to retrieve data, resulting in faster load times and improved user experiences."— Demystifying Content Delivery Networks (CDNs): Boosting Performance and Global AccessibilityDEV Community

QUIZ 1

What is the primary problem that a Content Delivery Network (CDN) is designed to solve for a web application?

A To compile Vue.js components into static HTML and CSS.

B To reduce latency by caching static assets on servers closer to the user.

C To automatically fix bugs in JavaScript code before deployment.

D To provide a database for storing dynamic user data globally.

Response ⇒

QUIZ 2

Which command do you run in a typical Vue.js project to compile the application into a dist folder, ready for deployment to a CDN?

A npm run build

B npm install

C npm start

D npm deploy

Response ⇒

QUIZ 3

True or False: A CDN should cache and serve all requests for your application, including dynamic API calls to /api

A true

B false

Response ⇒

QUIZ 4

When a user makes a request to a Vue app hosted on a CDN, how does the CDN handle a request for /api/users?

A It runs the server-side code directly on the CDN's edge server to generate a response.

B It blocks the request for security reasons, as API calls are not allowed through a CDN.

C It serves a cached, static version of the API response from the nearest Point of Presence (PoP).

D It uses a proxy or rewrite rule to forward the request to the origin backend server.

Response ⇒

QUIZ 5

For a developer looking for a CDN solution with seamless Git integration and automatic builds for their Vue.js project, which of the following providers are highlighted as excellent starting points?

A AWS CloudFront and Google Cloud CDN

B Only Cloudflare

C Netlify and Vercel

Response ⇒

By integrating a CDN, you've made your application faster and more scalable for a global audience.

Final exam

1. What is the primary purpose of a firewall, such as ufw (Uncomplicated Firewall), when configuring a Virtual Private Server (VPS)?

A To optimize the performance of MySQL database queries.

B To serve web pages and static assets to users.

C To manage software packages and system updates.

D To control and filter incoming and outgoing network traffic based on predefined security rules.

Response ⇒

2. When setting up a backend API with Node.js and Express to interact with a MySQL database, what is the most effective way to prevent SQL injection attacks?

A Encrypting all data before it is stored in the database.

B Manually sanitizing user input by removing special characters like semicolons and quotes.

C Using prepared statements or parameterized queries.

D Concatenating user-provided values directly into the SQL query string.

Response ⇒

3. In a Vue.js application, which lifecycle hook is most commonly used for fetching data from a backend API after the component has been added to the DOM?

A beforeCreate

B created

C mounted

D updated

Response ⇒

4. What is the primary benefit of deploying a Vue.js frontend application to a Content Delivery Network (CDN)?

A To automatically secure the backend API endpoints.

B To reduce server load and improve content delivery speed to users by caching assets in geographically distributed locations.

C To compile the Vue.js single-file components into plain JavaScript.

D To directly connect the user's browser to the MySQL database.

Response ⇒

5. In a RESTful API, which HTTP method should be used to create a new resource, such as adding a new user to the database?

A POST

B DELETE

C GET

D PUT

Response ⇒

6. When setting up a Vue.js project, a tool like Axios is commonly added. What is the primary role of Axios in this context?

A To make promise-based HTTP requests from the browser to a backend API.

B To create and manage routing between different pages in the application.

C To style the Vue components with CSS.

D To manage the application's state across different components.

Response ⇒

7. True or False: Storing sensitive API keys directly in the source code of a public-facing Vue.js application is a secure practice.

A True

B False

Response ⇒

8. When deploying a Vue application to a CDN and communicating with a backend API on a different domain, you might encounter an error related to 'Cross-Origin Resource Sharing'. What is the standard way to resolve this on the backend server?

A Implementing the CORS protocol by adding specific HTTP headers (e.g., Access-Control-Allow-Origin) to the API's responses.

B Hardcoding the API's IP address in the Vue application.

C Including the user's IP address in every API request.

D Configuring the CDN to ignore the error.

Response ⇒

9. Which of the following is NOT a typical responsibility of a web server like Nginx or Apache in this stack?

A Acting as a reverse proxy to forward API requests to the backend application (e.g., a Node.js server).

B Executing database queries against the MySQL server.

C Serving the static HTML, CSS, and JavaScript files of the built Vue.js application.

D Implementing SSL/TLS to enable HTTPS for secure communication.

Response ⇒

10. In Vue.js, how do you typically display a list of items fetched from an API, such as a list of users?

A Manually creating a DOM element for each item using document.createElement().

B Using a v-if directive on a single <div> element.

C Using the v-for directive to iterate over an array in the component's data.

D Using a v-bind directive to link the entire array to a <ul> element.

Response ⇒