NukeBase

Complete Server Example

Here's a minimal but complete server setup:

Complete server configuration example
module.exports = ({
  // Data
  get, set, update, increment, remove, query, data,
  // Files
  getFile, setFile, removeFile, listFiles, fileStat, fileUrlFor, FILES_DIR,
  // Extension points
  addDbTrigger, addFileTrigger, addCallable, addConnectionTrigger,
  // Server
  addDomain, startDB, checkAuth, withBody, hashToken, generateRequestId
}) => {

// Set up a domain
const nukebase = addDomain({
  authPath: ["users"],  // Path where user authentication data is stored
  host: "127.0.0.1", // optional
  port: 3000 // optional
});

// Enable username/password auth (/login, /createuser, /changepassword).
// Omit it and the app is passwordless — /logout and session validation
// are in core either way.
require("./extensions/auth")({
  app: nukebase.app,
  authPath: nukebase.authPath,
  get, set, update, remove, query, generateRequestId, hashToken
});

// Configure middleware for serving static files
const path = require('path');
nukebase.app.serveStatic("/*", path.join(__dirname, "../public"),
  (res, req) => { return true; }
);

// Add a database trigger for important changes
addDbTrigger("value", ["orders", "$orderId"], function(context) {
  // Only trigger if data has actually changed
  if (JSON.stringify(context.dataAfter) !== JSON.stringify(context.dataBefore)) {
    set(["logs", generateRequestId()], {
      path: context.path,
      timestamp: Date.now(),
      oldValue: context.dataBefore,
      newValue: context.dataAfter,
      change: "Important data changed"
    });
  }
});

// Add a callable for client calculations
addCallable("addNumbers", function(data, admin, sessionId) {
  // Extract numbers from the request
  const { num1, num2 } = data;
  // Perform the calculation on the server
  const sum = num1 + num2;
  // Return the result to the client
  return sum;
});

// Atomic counter — no read-modify-write race
addDbTrigger("set", ["orders", "$orderId"], function(context) {
  increment(["stats", "ordersPlaced"], 1);
});

// Keep a per-user storage ledger the file rules can read.
// reserve books the cost before the bytes; release refunds every path
// that doesn't commit. See File Storage for why both are needed.
addFileTrigger("reserve", ["uploads"], (ctx) => {
  increment(["usage", ctx.path[1], "bytes"], ctx.file.size);
});
addFileTrigger("release", ["uploads"], (ctx) => {
  increment(["usage", ctx.path[1], "bytes"], -ctx.file.size);
});

// Track user connections
addConnectionTrigger("open", function(admin, sessionId) {
  // Record when user connects
  update(["sessions", admin.uid, sessionId], {
    start: Date.now()
  });

  // Update user status
  update(["users", admin.uid], {
    online: true,
    lastSeen: Date.now()
  });
});

// Handle user disconnections
addConnectionTrigger("close", function(admin, sessionId) {
  // Record when user disconnects
  update(["sessions", admin.uid, sessionId], {
    end: Date.now()
  });

  // Update user status
  update(["users", admin.uid], {
    online: false,
    lastSeen: Date.now()
  });
});

startDB(nukebase);
console.log("🚀 NukeBase server running on http://127.0.0.1:3000");
};

Note: This example demonstrates best practices including:

  • Domain setup with authPath, host, and port configuration
  • Static file serving with serveStatic
  • Real-time database triggers
  • Custom WebSocket functions
  • Connection tracking
  • Server initialization with startDB(nukebase)