Vehicle-rental-system: DriveHive: The Vanilla Full-Stack Rental App That Hides a Serious Systems Design

A vehicle-rental platform built with plain HTML, vanilla JavaScript, Node, MongoDB, JWT, Cloudinary, and SMTP fallbacks, where the real story is the resilience behind the UI.

9 min read • View on GitHub • More from Unknown-Dev5002

A wide editorial illustration of a roadside rental counter assembled from browser windows, server components, and vehicle cards. Multiple threaded paths branch to Cloudinary, a local image file, and a placeholder sign, showing how the system keeps working when dependencies are missing.
DriveHive is interesting because it treats fallback behavior as part of the product, not as an afterthought.
Key Takeaways

Most rental demos stop at listings and login screens. DriveHive keeps going, because the system has to survive real gaps: broken images, missing SMTP, cached sessions, and authenticated flows that still need to behave when the happy path is unavailable.

The app that keeps working when pieces are missing

That is the real hook. DriveHive is a full-stack app that behaves like a product team built it for operations, not for a portfolio. It uses plain HTML, vanilla JavaScript, Express, Mongoose, JWT, Cloudinary, and Nodemailer, but the more important detail is how it degrades gracefully when one of those pieces is absent.

Java-based project implementing a Vehicle Rental System, featuring a flexible class hierarchy for cars and motorcycles. The system, built using object-oriented principles, includes a central "RentalSystem" for inventory management and rental operations.

Unknown-Dev5002, Project Author/Maintainer · Vehicle-rental-system - GitHub

The real trick is not the rental flow. It is the fallback logic

Three choices define the repo’s design philosophy. First, vehicle photos can resolve from Cloudinary, a local path, or a placeholder when data is incomplete. Second, password resets can still be generated in development when SMTP is unavailable. Third, the frontend persists identity and cache state in localStorage so the app can pick up where the user left off.

The app’s architecture is easier to understand as a set of decision trees than as a single static stack diagram.

A close-up workshop scene where two hands assemble a checkout flow from a JWT key, a validation stamp, a booking ledger, and a browser card. The pieces sit on a bench like parts in a system that can be repaired and reconfigured.
DriveHive’s backend and frontend cooperate like modular pieces on a workbench, not like a monolithic app.

A backend that splits business rules from HTTP

The backend uses a service-controller split that keeps the messy parts in the right place. `authService.js` owns logic like registration validation and password reset workflows, while `authController.js` turns those outcomes into HTTP responses. That separation matters because the business rules stay testable even when the transport layer changes.

// authService.js
async function requestPasswordReset(email) {
  const user = await User.findOne({ email });
  if (!user) throw new AuthError('User not found', 404);

  const resetToken = generateResetToken(user._id);
  const resetLink = `${BASE_URL}/reset-password?token=${resetToken}`;

  if (!smtpConfigured()) {
    return { resetLink, devFallback: true };
  }

  await sendResetEmail(user.email, resetLink);
  return { success: true };
}

// authController.js
async function requestPasswordResetController(req, res) {
  try {
    const result = await authService.requestPasswordReset(req.body.email);
    return res.status(200).json(result);
  } catch (err) {
    return res.status(err.statusCode || 500).json({ message: err.message });
  }
}

The same pattern shows up in request validation. The server keeps rules like allowed vehicle years and other business constraints close to the entry point, which avoids turning the browser into a second source of truth.

LayerDriveHive approachWhy it matters
ValidationBackend checks request shape and business rulesThe browser cannot silently invent policy
WorkflowService layer handles logic, controller handles responsesCode stays easier to extend and test
IdentityJWT verification attaches the user to the requestProtected routes stay explicit
Failure modeMissing SMTP or image source triggers a fallbackThe app stays usable instead of collapsing

The frontend is a stateful machine, not just pages

The client side does real orchestration. `app.js` maintains a `vehiclesCache`, reads and writes `localStorage`, renders vehicle cards dynamically, and uses `escapeHtml` to reduce the risk of unsafe content reaching the DOM. Even without React or a build pipeline, the frontend is acting like a state manager.

const vehiclesCache = [];

function resolveVehicleImageUrl(vehicle) {
  if (vehicle.imageUrl && vehicle.imageUrl.startsWith('https://res.cloudinary.com')) {
    return vehicle.imageUrl;
  }
  if (vehicle.imageUrl && vehicle.imageUrl.startsWith('/')) {
    return vehicle.imageUrl;
  }
  return '/assets/img/placeholder-vehicle.jpg';
}

function renderRedesignVehicleCard(vehicle) {
  const imageUrl = resolveVehicleImageUrl(vehicle);
  return `
    <article class="glass-surface vehicle-card">
      <img src="${imageUrl}" alt="${escapeHtml(vehicle.name)}">
      <h3>${escapeHtml(vehicle.name)}</h3>
      <p>${formatINR(vehicle.pricePerDay)} per day</p>
    </article>
  `;
}
Frontend patternDriveHiveFramework-heavy app
StateManual cache plus localStorageCentralized component state
RenderingTemplate strings and direct DOM updatesVirtual DOM and component trees
ToolingScript tags and static assetsBundler, router, and build chain
Trade-offLess ceremony, more manual coordinationMore structure, more moving parts

The redesign files tell you this repo is mid-migration

The coexistence of legacy and redesigned files is not a flaw. It is evidence of an active product evolution. The backend keeps running while the visual layer changes shape, which is exactly how a real app moves when nobody gets to stop the world for a rewrite.

SignalWhat it suggestsEditorial read
Legacy and redesign files side by sideAn in-progress UI migrationThe app is alive, not archived
Heavy use of raw HTML and script tagsA lightweight deployment modelSpeed and simplicity over framework ceremony
Repeated utility functions in client codeA codebase that is still stabilizingUseful, but not yet fully hardened

That puts the project in an honest middle ground. It has the bones of a production system, but the raw structure, direct DOM injection, and migration artifacts still read as an advanced prototype.

How DriveHive compares to heavier rental stacks

ApproachStackComplexity centerBest fit
DriveHiveVanilla JS, Node, MongoDBOperational resilience and business rulesA focused web app that needs control without framework overhead
Framework-heavy MERN appReact, Express, MongoDB, NodeComponent architecture and client build toolingTeams that want a more standardized frontend system
Mobile-first rental productFlutter or native mobile stackApp distribution and device integrationMarketplace products optimized for phones and maps
CLI educational projectPure JavaOOP modeling and console I/OTeaching core logic without infrastructure

The trade-off is simple. DriveHive gives up some of the guardrails of a framework-heavy stack, but it gains speed, transparency, and a smaller dependency surface. That makes it a strong example of how far careful plain-JS architecture can go.


Sources and references