I’m making a second hand bookstore website that uses Aiven as the database and Vercel to deploy. It’s rather simple because it’s a school project, and it uses vanilla JS. I am trying to fetch some data from some APIs but they don’t work at all.
Here’s my server.js
const express = require('express');
const mysql = require('mysql2');
const path = require('path');
const cors = require('cors');
const fs = require('fs');
const fetch = (...args) =>
import("node-fetch").then(({ default: fetch }) => fetch(...args));
const app = express();
const PORT = process.env.PORT || 3001;
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
const corsOptions = {
origin: 'http://localhost:3001', // Your frontend origin
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
};
app.use(cors(corsOptions));
require('dotenv').config();
const connection = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: process.env.DB_PORT,
ssl: {
rejectUnauthorized: false,
ca: fs.readFileSync(process.env.SSL_CA),
},
});
console.log('Connecting to database:', {
host: process.env.DB_HOST,
user: process.env.DB_USER,
database: process.env.DB_NAME,
port: process.env.DB_PORT,
});
connection.connect(err => {
if (err) {
console.error('Database connection error:', err);
return;
}
console.log('Connected to the database');
});
app.get('/test-db', (req, res) => {
connection.query('SELECT 1', (err, results) => {
if (err) {
return res.status(500).json({ error: 'Database connection error' });
}
res.json({ message: 'Database is connected', results });
});
});
app.get('/books', (req, res) => {
const query = 'SELECT * FROM books';
connection.query(query, (err, results) => {
if (err) {
return res.status(500).json({ error: 'Error fetching books' });
}
res.json(results);
});
});
console.log('Database Host:', process.env.DB_HOST);
console.log('Database Name:', process.env.DB_NAME);
connection.connect(err => {
if (err) {
console.error('Database connection error:', err);
return;
}
console.log('Connected to the database');
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
console.log('Using SSL CA file:', process.env.SSL_CA);
console.log('Reading CA:', fs.readFileSync(process.env.SSL_CA).toString());
app.use((req, res, next) => {
console.log(`Incoming Request: ${req.method} ${req.url}`);
console.log('Request Headers:', req.headers);
next();
});
and here’s the html file where the books are fetched
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inventory</title>
<link rel="stylesheet" href="istyle.css">
</head>
<body>
<div id="navbar">
<img src="images/BookbackICON.png" alt="">
<label class="hamburger-menu">
<input type="checkbox" />
</label>
<aside class="sidebar">
<nav>
<a href="index.html">Home</a>
<a href="inventory.html">Inventory</a>
</nav>
</aside>
</div>
<section id="sec1">
<span id="search">
<input type="text" id="search-input" placeholder="Search for a book by name">
<button id="search-button">Search</button>
</span>
<ul id="book-list"></ul>
</section>
<script>
let books = [];
let uniqueBooks = [];
let duplicateCounts = {};
function deduplicateBooks(data) {
const uniqueBooksMap = {};
duplicateCounts = {};
data.forEach(book => {
const isbnFull = book.id.split('-')[0]; // Use the full ISBN without trimming the last 5 digits
const grade = book.grade; // Get the grade
const uniqueKey = `${isbnFull}-${grade}`;
if (duplicateCounts[uniqueKey]) {
duplicateCounts[uniqueKey]++;
} else {
duplicateCounts[uniqueKey] = 1;
uniqueBooksMap[uniqueKey] = book; // Store unique book information
}
});
return Object.values(uniqueBooksMap);
}
function fetchBooks() {
fetch('https://bookback-i1o4juwqu-mohammed-aayan-pathans-projects.vercel.app/books', {
method: 'GET', // Change as needed
headers: {
'Content-Type': 'application/json', // Change as needed
// Any other headers you might need
},
credentials: 'include', // Include this if your API requires credentials
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok: ' + response.statusText);
}
return response.json();
})
.then(data => {
console.log('Fetched books:', data);
})
.catch(error => {
console.error('Fetch error:', error);
});
}
function displayBooks(booksToDisplay) {
const bookList = document.getElementById('book-list');
bookList.innerHTML = ''; // Clear the current list
const listItems = [];
booksToDisplay.forEach(book => {
const isbn = book.id.split('-')[0]; // Extract full ISBN part
const li = document.createElement('li');
li.style.cursor = 'pointer'; // Change cursor to pointer for better UX
fetch(`https://www.googleapis.com/books/v1/volumes?q=isbn:${isbn}`)
.then(response => response.json())
.then(bookData => {
const img = document.createElement('img');
img.style.borderRadius = '4px';
const title = book.name || 'Title not available';
// Handle Google Books data
if (bookData.items && bookData.items.length > 0) {
const bookInfo = bookData.items[0].volumeInfo;
const imageLinks = bookInfo.imageLinks;
const coverImage = imageLinks?.large || imageLinks?.medium || imageLinks?.thumbnail || 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/No_image_available.svg/1200px-No_image_available.svg.png';
img.src = coverImage;
} else {
img.src = `https://images-na.ssl-images-amazon.com/images/P/${isbn}.L.jpg`;
}
img.alt = title;
img.style.maxWidth = '200px'; // Adjust the image size as needed
// Handle price safely (fallback if not a number or missing)
let priceDisplay = 'Price not available';
if (book.price && !isNaN(book.price)) {
priceDisplay = `AED ${parseFloat(book.price).toFixed(2)}`;
}
// Display the book information, title, image, and stock
li.appendChild(img);
li.appendChild(document.createTextNode(title));
li.appendChild(document.createElement('br'));
li.appendChild(document.createTextNode(`Grade: ${book.grade}`));
li.appendChild(document.createElement('br'));
li.appendChild(document.createTextNode(`${priceDisplay}`));
// Fix the duplicate count display logic
const countKey = `${book.id.split('-')[0]}-${book.grade}`;
const countDisplay = `No. in stock: ${duplicateCounts[countKey] || 1}`;
li.appendChild(document.createElement('br'));
li.appendChild(document.createTextNode(countDisplay));
const isbnInfo = document.createElement('span');
isbnInfo.textContent = `ISBN: ${book.id}`;
isbnInfo.style.display = 'none'; // Hide it initially
li.appendChild(isbnInfo); // Append the ISBN info to the list item
li.onclick = (event) => {
event.stopPropagation(); // Prevent click event from bubbling up to the document
listItems.forEach(item => {
item.style.filter = 'none'; // Remove blur
const isbnSpan = item.querySelector('span');
if (isbnSpan) {
isbnSpan.style.display = 'none'; // Hide ISBN
}
});
if (isbnInfo.style.display === 'none') {
isbnInfo.style.display = 'inline'; // Show ISBN
li.style.height = 'auto'; // Expand the clicked item
li.style.filter = 'none'; // Remove blur from the clicked item
} else {
isbnInfo.style.display = 'none'; // Hide ISBN
}
li.style.filter = 'none'; // Ensure the clicked item is not blurred
listItems.forEach(item => {
if (item !== li) {
item.style.filter = 'blur(5px)'; // Blur the other items
}
});
};
listItems.push(li); // Add the current item to the listItems array
bookList.appendChild(li);
})
.catch(error => {
console.error('Error fetching book image from Google:', error);
const img = document.createElement('img');
img.src = `https://images-na.ssl-images-amazon.com/images/P/${isbn}.L.jpg`; // Fallback Amazon image
img.alt = 'No cover image available';
img.style.maxWidth = '200px'; // Adjust the image size as needed
let priceDisplay = 'Price not available';
if (book.price && !isNaN(book.price)) {
priceDisplay = `AED ${parseFloat(book.price).toFixed(2)}`;
}
li.appendChild(img);
li.appendChild(document.createTextNode(` Title: ${book.name}, ISBN: ${book.id}, Grade: ${book.grade}, ${priceDisplay}`));
bookList.appendChild(li);
});
});
document.addEventListener('click', () => {
listItems.forEach(item => {
item.style.height = '260px'; // Reset height to auto
item.style.filter = 'none'; // Remove blur from all items
const isbnSpan = item.querySelector('span');
if (isbnSpan) {
isbnSpan.style.display = 'none'; // Hide ISBN
}
});
});
}
function searchBooks() {
const searchTerm = document.getElementById('search-input').value.toLowerCase();
const filteredBooks = uniqueBooks.filter(book => book.name.toLowerCase().includes(searchTerm));
displayBooks(filteredBooks);
}
document.getElementById('search-button').addEventListener('click', searchBooks);
fetchBooks();
</script>
</body>`your text`
</html>
I tried doing all sorts of things to mitigate this issue, but this will not budge. If you need anything else, I’ll provide it as promptly as I can.