Skip to content

Build a Node.js app with MongoDB Flex

In this tutorial, you implement a real world scenario with MongoDB Flex. After finishing all the steps, you have a Node.js app, which stores its data into a MongoDB Flex database. You learn how to connect a Node.js application to MongoDB Flex and why MongoDB works so well with JavaScript applications. With the acquired knowledge you can for instance build a REST- or GraphQL-API which is backed up by a solid and flexible database.

This tutorial is split into the following sections:

  1. Create a MongoDB Flex instance with a user, proper rights and ACL settings.
  2. Initialize an environment to execute Node.js with the needed npm packages.
  3. Write a small Node.js application which tests the connection to the database.
  4. Create a Node.js application which is backed up by MongoDB Flex.
  • You have a STACKIT customer account: Create a customer account
  • You have a STACKIT user account: Create a user account
  • You have a STACKIT project: Create a project
  • On the development system you have access to a shell with Node.js available.
  • You can reach the STACKIT cloud from your development machine.
  • You know the external IPv4 address of your client system.

Create a MongoDB Flex instance with a user, proper rights and ACL settings

Section titled “Create a MongoDB Flex instance with a user, proper rights and ACL settings”

Initialize an environment to execute Node.js with the needed npm packages

Section titled “Initialize an environment to execute Node.js with the needed npm packages”

At first, check, if you have a recent version of Node.js installed.

Terminal window
node --version

The output should be something like this:

v24.4.1

The installed version should be at least equal or bigger than v20.0.0. If you have an older version installed, please consult the manual of your system to install a more recent version.

After checking this prerequisite, create a folder for this tutorial:

Terminal window
mkdir nodejs-with-mongodb-flex-tutorial
cd nodejs-with-mongodb-flex-tutorial
npm init -y
npm install mongodb dotenv

The output should be something like this:

added 13 packages, and audited 14 packages in 1s
1 package is looking for funding
run `npm fund` for details
found 0 vulnerabilities

Please note that you can make adjustments to the created package.json. E.g. to adjust the initial version number, license or the author. Now you are ready to write your small Node.js test application.

Write a small Node.js application which tests the connection to the database

Section titled “Write a small Node.js application which tests the connection to the database”

Open your code editor of choice and create a file called .env with the following content (insert the connection string which you saved before):

MONGODB_URI=mongodb://USERNAME:YOUR-SECRET-PASSWORD@s-f47ac10b-58cc-4372-a567-0e02b2c3d479-0.mongodb.eu01.onstackit.cloud:27017/default?authSource=default&tls=true&authMechanism=SCRAM-SHA-256

Create another file called test-connection.mjs:

import { MongoClient } from "mongodb";
import dotenv from "dotenv";
dotenv.config();
const uri = process.env.MONGODB_URI;
async function testConnection() {
if (!uri) {
console.error("MONGODB_URI not set!");
return;
}
const client = new MongoClient(uri);
try {
console.log("Connecting to MongoDB Flex...");
await client.connect();
await client.db("admin").command({ ping: 1 });
console.log("Succesfully conntected!");
const dbName = client.db().databaseName;
console.log(`Connected to: ${dbName}`);
} catch (error) {
console.error("Error while connecting to MongoDB Flex:", error.message);
} finally {
await client.close();
console.log("Connection closed.");
}
}
testConnection();

Now execute the mini application with your Node.JS interpreter:

Terminal window
node test-connection.mjs

Create a Node.js application which is backed up by MongoDB Flex

Section titled “Create a Node.js application which is backed up by MongoDB Flex”

Now that you set up everything correctly, you can begin with the implementation of the actual application. At first we need to install further dependencies:

Terminal window
npm install express

You should see something like:

added 67 packages, and audited 81 packages in 2s
15 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities

For the sake of simplicity, we will not be setting up the recommended best practice Node.js folder structure in this tutorial. We recommend to follow this best practice structure when creating a project for production usage.

Create a file called main.mjs:

import express from "express";
import { MongoClient } from "mongodb";
import dotenv from "dotenv";
// Route-Imports
import booksRoutes from "./books.mjs";
import searchRoutes from "./search.mjs";
// Loading .env file
dotenv.config();
const app = express();
const port = process.env.PORT || 3000;
const uri = process.env.MONGODB_URI;
// Middleware
app.use(express.json());
// MongoDB client
let db;
// Set up connection to database
async function connectToMongoDB() {
try {
const client = new MongoClient(uri);
await client.connect();
db = client.db("buchverwaltung");
console.log("Connected to MongoDB Flex");
app.locals.db = db;
} catch (error) {
console.error("Connection error with MongoDB", error);
process.exit(1);
}
}
// Routes
app.use("/api/books", booksRoutes);
// Health check
app.get("/health", (req, res) => {
res.json({
success: true,
message: "API läuft",
timestamp: new Date().toISOString(),
});
});
// 404 handler
app.use("*", (req, res) => {
res.status(404).json({
success: false,
message: "Endpoint nicht gefunden",
});
});
// Error handler
app.use((error, req, res, next) => {
console.error(error.stack);
res.status(500).json({
success: false,
message: "Interner Serverfehler",
});
});
// Start server
async function startServer() {
await connectToMongoDB();
app.listen(port, () => {
console.log(`🚀 Server läuft auf http://localhost:${port}`);
console.log(`📚 API Endpunkte:`);
console.log(` GET /api/books - All Books`);
console.log(` POST /api/books - Neues Buch`);
console.log(` GET /api/search?q=term - Bücher suchen`);
});
}
startServer().catch(console.error);

Create a file called books.mjs:

import express from "express";
import { ObjectId } from "mongodb";
const router = express.Router();
// Helper function for validation
function validateBook(book) {
const errors = [];
if (!book.titel || book.titel.trim() === "") {
errors.push("Titel ist erforderlich");
}
if (!book.autor || book.autor.trim() === "") {
errors.push("Autor ist erforderlich");
}
if (book.seiten && (isNaN(book.seiten) || book.seiten <= 0)) {
errors.push("Seitenzahl muss eine positive Zahl sein");
}
return errors;
}
// GET /api/books - get all books
router.get("/", async (req, res) => {
try {
const db = req.app.locals.db;
const books = await db.collection("books").find({}).toArray();
res.json({
success: true,
count: books.length,
data: books,
});
} catch (error) {
res.status(500).json({
success: false,
message: "Fehler beim Abrufen der Bücher",
error: error.message,
});
}
});
// POST /api/books - Add new book
router.post("/", async (req, res) => {
try {
const db = req.app.locals.db;
const bookData = req.body;
// Validation
const errors = validateBook(bookData);
if (errors.length > 0) {
return res.status(400).json({
success: false,
message: "Validierungsfehler",
errors: errors,
});
}
// Create book object
const newBook = {
titel: bookData.titel.trim(),
autor: bookData.autor.trim(),
isbn: bookData.isbn || null,
seiten: bookData.seiten || null,
genre: bookData.genre || null,
erscheinungsjahr: bookData.erscheinungsjahr || null,
beschreibung: bookData.beschreibung || null,
erstelltAm: new Date(),
aktualisiertAm: new Date(),
};
const result = await db.collection("books").insertOne(newBook);
res.status(201).json({
success: true,
message: "Buch erfolgreich hinzugefügt",
data: { _id: result.insertedId, ...newBook },
});
} catch (error) {
res.status(500).json({
success: false,
message: "Fehler beim Hinzufügen des Buchs",
error: error.message,
});
}
});
export default router;

Create a file called search.mjs:

import express from "express";
const router = express.Router();
// GET /api/search?q=suchbegriff - search for books
router.get("/", async (req, res) => {
try {
const db = req.app.locals.db;
const { q } = req.query;
if (!q || q.trim() === "") {
return res.status(400).json({
success: false,
message: "Suchbegriff ist erforderlich",
});
}
const searchRegex = new RegExp(q.trim(), "i"); // case-insensitive search
const books = await db
.collection("books")
.find({
$or: [
{ titel: searchRegex },
{ autor: searchRegex },
{ genre: searchRegex },
{ beschreibung: searchRegex },
],
})
.toArray();
res.json({
success: true,
count: books.length,
searchTerm: q.trim(),
data: books,
});
} catch (error) {
res.status(500).json({
success: false,
message: "Fehler bei der Suche",
error: error.message,
});
}
});
// GET /api/search/genre/:genre - filter books for genre
router.get("/genre/:genre", async (req, res) => {
try {
const db = req.app.locals.db;
const { genre } = req.params;
const genreRegex = new RegExp(genre, "i");
const books = await db.collection("books").find({ genre: genreRegex }).toArray();
res.json({
success: true,
count: books.length,
genre: genre,
data: books,
});
} catch (error) {
res.status(500).json({
success: false,
message: "Fehler beim Filtern nach Genre",
error: error.message,
});
}
});
// GET /api/search/autor/:autor - filter books for author
router.get("/autor/:autor", async (req, res) => {
try {
const db = req.app.locals.db;
const { autor } = req.params;
const autorRegex = new RegExp(autor, "i");
const books = await db.collection("books").find({ autor: autorRegex }).toArray();
res.json({
success: true,
count: books.length,
autor: autor,
data: books,
});
} catch (error) {
res.status(500).json({
success: false,
message: "Fehler beim Filtern nach Autor",
error: error.message,
});
}
});
// GET /api/search/jahr/:jahr - filter books for release date
router.get("/jahr/:jahr", async (req, res) => {
try {
const db = req.app.locals.db;
const { jahr } = req.params;
const jahrNum = parseInt(jahr);
if (isNaN(jahrNum)) {
return res.status(400).json({
success: false,
message: "Ungültiges Jahr",
});
}
const books = await db.collection("books").find({ erscheinungsjahr: jahrNum }).toArray();
res.json({
success: true,
count: books.length,
jahr: jahrNum,
data: books,
});
} catch (error) {
res.status(500).json({
success: false,
message: "Fehler beim Filtern nach Jahr",
error: error.message,
});
}
});
export default router;