What you’ll learn: JavaScript from the very beginning β€” variables, functions, classes, async/await, error handling β€” everything needed to understand EPICBackend and EPICFrontend code.
Assumes: You know basic programming concepts (what a loop is, what a condition is). Nothing more.


Section 1 β€” What Is JavaScript?

JavaScript started as a language for web browsers (making websites interactive). Today it also runs on servers using Node.js.

In EPIC:

  • EPICBackend = JavaScript running on servers (Node.js inside AWS Lambda)
  • EPICFrontend = JavaScript running in the browser (React)

Both use the same language β€” JavaScript β€” but in different environments.

Your Browser ──── loads ──── EPICFrontend (JavaScript/React)
                                    ↓ makes HTTP calls
AWS Lambda ──── runs ──── EPICBackend (JavaScript/Node.js)

Section 2 β€” Variables: Storing Data

In JavaScript, you store data in variables using const, let, or var:

// const = value never changes (use this most of the time)
const fleetId = "RIPE-NA";
const maxHosts = 500;

// let = value can change
let currentStatus = "Running";
currentStatus = "Completed";  // OK, let allows this

// var = old way, avoid it in modern code

In EPIC code you’ll see:

// From HOTW.js
const hotwOperations = new HOTWOperations();  // const: this object won't be reassigned
let body = {};                                 // let: body gets assigned later in try/catch
let eventId = '';                              // let: will be filled from request

Data types in JavaScript:

// String (text)
const eventId = "PrimeDay2024";

// Number (no difference between int and double like Java)
const peakTpm = 50000;
const ctFactor = 1.25;

// Boolean (true/false)
const isEmergent = true;

// null (intentionally empty)
const ticket = null;

// undefined (not set yet)
let result;  // result is undefined

// Object (key-value pairs, like a Map or a class instance)
const fleet = {
    FleetId: "RIPE-NA",
    PeakTPM: 50000,
    Region: "us-east-1"
};

// Array (list of items)
const asgList = ["RIPE/NA/PROD/1a", "RIPE/NA/PROD/1b", "RIPE/NA/PROD/1c"];

Section 3 β€” Functions: Reusable Code Blocks

// Regular function
function addNumbers(a, b) {
    return a + b;
}
addNumbers(10, 20);  // returns 30

// Arrow function (shorter syntax, very common in EPIC)
const addNumbers = (a, b) => a + b;
addNumbers(10, 20);  // same result: 30

// Arrow function with multiple lines
const calculateBauTpm = (peakTpm, ctFactor, bufferFactor) => {
    const result = peakTpm / (ctFactor * bufferFactor);
    return result;
};

Why arrow functions everywhere? They’re shorter and handle this keyword differently (important in callbacks β€” more on that later).


Section 4 β€” Classes: Organizing Code

JavaScript has classes similar to Java. In EPIC’s backend, almost every handler is a class:

// From EPICBackend β€” HOTWOperations.js
class HOTWOperations {
    constructor() {
        // constructor runs when you create a new HOTWOperations()
        this.db = new AuroraDB();  // 'this' means "this object"
    }

    async createOrUpdateHotwDashBoardTable(tableName, entries) {
        // method of the class
        const query = `INSERT INTO ${tableName} ...`;
        return await this.db.insertSingleQueryPromise(query, entries);
    }
}

// Using the class:
const hotwOps = new HOTWOperations();
hotwOps.createOrUpdateHotwDashBoardTable("hotw_dashboard", data);

Static methods (don’t need to create an object):

// From Fleet.js backend
class Fleet {
    static async createFleet(event) {
        // static = call it as Fleet.createFleet(event)
        // no need for new Fleet()
    }
}

// Used as:
Fleet.createFleet(event);     // NOT: new Fleet().createFleet(event)

The difference:

  • Regular method: const fleet = new Fleet(); fleet.doSomething();
  • Static method: Fleet.doSomething(); (directly on the class, no new)

Section 5 β€” The require and module.exports System

In Node.js, code is organized into files. Each file is a β€œmodule”. To use code from another file:

// In HOTWOperations.js β€” this is EXPORTING (making code available)
class HOTWOperations {
    // ...
}
module.exports = HOTWOperations;   // this line makes it available to others

// In HOTW.js β€” this is IMPORTING (using code from another file)
const HOTWOperations = require('../operations/HOTWOperations');
// Now HOTWOperations is available in this file

Real EPIC example from HOTW.js:

// Top of HOTW.js β€” all imports
const HOTWConstants = require('../common/HOTWConstants');
const FleetOperations = require('../operations/FleetOperations');
const ServiceOperations = require('../operations/ServiceOperations');
const OtherConstants = require('../common/OtherConstants');
const Util = require('../common/Util');

// This means this file uses code from all these other files

Path conventions:

  • ./filename = same folder
  • ../filename = one folder up
  • ../../filename = two folders up

Section 6 β€” Objects and Destructuring

Objects in JavaScript are like JSON. This is the most common data format in EPIC:

// Creating an object
const fleet = {
    FleetId: "RIPE-NA",
    ServiceId: "RIPE",
    PeakTPM: 50000,
    Region: "us-east-1"
};

// Accessing properties β€” TWO WAYS:
fleet.FleetId;           // dot notation: "RIPE-NA"
fleet["FleetId"];        // bracket notation: "RIPE-NA" (same result)

// Adding a new property
fleet.EventId = "PD2024";

// Spreading (copying all properties) β€” common in EPIC
const original = { FleetId: "RIPE-NA", PeakTPM: 50000 };
const copy = { ...original };          // copy has same properties
const withExtra = { ...original, Region: "us-east-1" };  // copy + new property

In HOTW.js you see this pattern:

body = JSON.parse(event[OtherConstants.BODY]);    // parse JSON string β†’ object
const entryToInsert = { ...body };                // copy the body object
entryToInsert.EventIndexId = eventIndexId;        // add one more property

Destructuring β€” extract multiple properties at once:

const fleet = { FleetId: "RIPE-NA", Region: "us-east-1", PeakTPM: 50000 };

// Instead of:
const fleetId = fleet.FleetId;
const region = fleet.Region;

// Destructuring does both in one line:
const { FleetId, Region } = fleet;

Section 7 β€” Arrays and Their Methods

Arrays (lists) are everywhere in EPIC:

const services = ["RIPE", "FORTRESS", "PayStation"];
const fleets = [
    { FleetId: "RIPE-NA", PeakTPM: 50000 },
    { FleetId: "RIPE-EU", PeakTPM: 30000 }
];

// Loop over array
services.forEach(service => {
    console.log(service);  // prints each service
});

// Transform array (map) β€” returns new array
const serviceIds = fleets.map(fleet => fleet.FleetId);
// serviceIds = ["RIPE-NA", "RIPE-EU"]

// Filter array β€” keep only matching items
const naFleets = fleets.filter(fleet => fleet.FleetId.includes("NA"));
// naFleets = [{ FleetId: "RIPE-NA", PeakTPM: 50000 }]

// Find one item
const ripeFleet = fleets.find(fleet => fleet.FleetId === "RIPE-NA");
// ripeFleet = { FleetId: "RIPE-NA", PeakTPM: 50000 }

// Check if any match (returns true/false)
const hasNAFleet = fleets.some(fleet => fleet.FleetId.includes("NA"));

// Reduce β€” combine all items into one value
const totalTpm = fleets.reduce((sum, fleet) => sum + fleet.PeakTPM, 0);
// totalTpm = 80000

Real EPIC example from HOTW.js:

// Building aggregateFleetIds from all services
let aggregateFleetIds = [];
for (let service of allRegisteredServices) {
    aggregateFleetIds = aggregateFleetIds.concat(
        serviceOperations.getFormattedFleetsBasedOnRegionList(
            service[SERVICE_CONSTANTS.FLEET], regionList
        )
    );
}

Section 8 β€” Promises and async/await

This is the MOST IMPORTANT concept for EPIC backend code. All database and API calls are asynchronous β€” they take time to complete.

The Problem Without async/await:

// This would FAIL β€” it doesn't wait for the database!
const result = database.query("SELECT * FROM fleets");  // starts query
console.log(result);  // runs IMMEDIATELY, before query finishes β€” result is undefined!

The Solution β€” async/await:

// This WORKS correctly
const result = await database.query("SELECT * FROM fleets");  // WAITS for query to finish
console.log(result);  // runs AFTER query is done β€” result has real data

Rules:

  1. Use await before any call that takes time (DB queries, HTTP calls, file reads)
  2. Any function that uses await MUST be marked async

Full EPIC example:

// From HOTW.js
static async createOrUpdateHotwRunDetails(event) {
    //             ↑ must be async because we use await inside
    
    const eventOperations = new EventOperations();
    
    try {
        body = JSON.parse(event[OtherConstants.BODY]);
        eventId = body[EventConstants.EVENT_ID];
    } catch (err) {
        return Util.handleErr(400, `Error parsing`, err);
    }
    
    try {
        let eventIndexId = await eventOperations.getEventIndex(eventId);
        //                 ↑ WAIT for this DB call to finish before continuing
        
        const result = await hotwOperations.updateRunTable(tableName, entries, eventIndexId);
        //                   ↑ WAIT for this insert to finish
        
        return Util.handleResponse(200, JSON.stringify(result.insertId));
    } catch (err) {
        return Util.handleErr(503, `Error inserting`, err);
    }
}

Why try/catch with async? If an await fails (network error, DB down), it throws an error. catch catches it.


Section 9 β€” Template Literals (String Interpolation)

Much better than string concatenation:

const fleetId = "RIPE-NA";
const eventId = "PD2024";
const runId = 5;

// Old way (ugly)
const msg1 = "Fleet " + fleetId + " for event " + eventId + " run " + runId;

// Template literal (modern, used everywhere in EPIC)
const msg2 = `Fleet ${fleetId} for event ${eventId} run ${runId}`;
// result: "Fleet RIPE-NA for event PD2024 run 5"

// Multiline
const query = `
    INSERT INTO hotwdashboard
    (FleetId, EventId)
    VALUES (${fleetId}, ${eventId})
`;

Real EPIC example:

// From EventPlan.js
return Util.handleResponse(
    400,
    `Invalid Request. Fleet: ${fleetId} for Event: ${eventId} doesn't exist.`
);

Section 10 β€” Error Handling (try/catch/finally)

try {
    // Code that might fail
    const result = await database.query("...");
    return { success: true, data: result };
} catch (err) {
    // Runs if anything in try{} throws an error
    console.log("Error:", err);
    return { success: false, error: err.message };
} finally {
    // ALWAYS runs, whether error or not
    // Used for cleanup
    await database.closeConnection();
}

EPIC pattern β€” you’ll see this exact structure everywhere:

static async createFleet(event) {
    // Step 1: Parse input (might fail if body is malformed)
    try {
        body = JSON.parse(event[OtherConstants.BODY]);
        fleetId = body[FleetConstants.FLEET_ID];
    } catch (err) {
        return Util.handleErr(400, `Error parsing request`, err);
    }
    
    // Step 2: Database operations (might fail if DB is down)
    try {
        await fleetOperations.createFleet(fleetId, body);
        return Util.handleResponse(200, "Success");
    } catch (err) {
        return Util.handleErr(503, `DB error for fleet: ${fleetId}`, err);
    }
}

Why two separate try/catch blocks? Because different errors need different response codes:

  • Parsing error β†’ 400 (client sent bad data)
  • DB error β†’ 503 (server problem)

Section 11 β€” JSON: The Data Format of EPIC

JSON (JavaScript Object Notation) is how data moves between frontend β†’ backend β†’ database:

// JavaScript object
const fleet = { FleetId: "RIPE-NA", PeakTPM: 50000 };

// Convert object β†’ JSON string (for sending over network)
const jsonString = JSON.stringify(fleet);
// Result: '{"FleetId":"RIPE-NA","PeakTPM":50000}'

// Convert JSON string β†’ object (for receiving from network)
const parsed = JSON.parse(jsonString);
// Result: { FleetId: "RIPE-NA", PeakTPM: 50000 }

In EPIC Lambda handlers:

// Incoming request has body as JSON string
body = JSON.parse(event[OtherConstants.BODY]);  // string β†’ object, now usable

// Sending response with JSON
return Util.handleResponse(200, JSON.stringify(result));  // object β†’ string, sendable

Section 12 β€” Maps and Sets

// Map β€” like a dictionary with any key type
const batchEventPlanMap = new Map();
batchEventPlanMap.set("RIPE-NA", { status: "completed" });
batchEventPlanMap.has("RIPE-NA");    // true
batchEventPlanMap.get("RIPE-NA");    // { status: "completed" }

// Iterating a Map
for (let [key, value] of batchEventPlanMap) {
    console.log(key, value);
}

// Set β€” list of UNIQUE values
const processedFleets = new Set();
processedFleets.add("RIPE-NA");
processedFleets.add("RIPE-NA");  // duplicate β€” ignored!
processedFleets.size;            // 1 (not 2)
processedFleets.has("RIPE-NA"); // true

Real EPIC example from HOTW.js:

// Create a hashmap to store batch event plan response by FleetId
const batchEventPlanMap = new Map();
batchEventPlanResponse.forEach(batchEventPlan => {
    const fleetId = batchEventPlan.FleetId;
    batchEventPlanMap.set(fleetId, batchEventPlan);  // key: fleetId, value: plan data
});

Section 13 β€” Modules Pattern in EPIC

Every EPIC backend file follows the same pattern:

File structure:
1. require() imports at top
2. class definition
3. static async methods
4. module.exports at bottom

Template:

// 1. IMPORTS
const SomeOtherClass = require('../path/to/SomeOtherClass');
const Constants = require('../common/Constants');

// 2. CLASS DEFINITION
class MyHandler {
    
    // 3. METHOD
    static async myMethod(event) {
        // parse input
        // validate
        // call operations
        // return response
    }
}

// 4. EXPORT
module.exports = MyHandler;

Section 14 β€” Common JavaScript Patterns in EPIC

Pattern 1: Validate required fields

// Util.validateKeys checks if all required fields exist in the body
if (!Util.validateKeys(body, [SERVICE_CONSTANTS.SERVICE_ID, FleetConstants.FLEET_ID])) {
    throw TypeError('Required fields are not present in body');
}

Pattern 2: Null/undefined guard

// Use || to provide a default value if something is null/undefined
const alias = queryParameters && queryParameters.alias 
    ? queryParameters.alias 
    : '';
// If queryParameters is null or doesn't have alias β†’ use empty string ''

Pattern 3: Chaining array methods

const finalList = services
    .filter(s => s.status === 'active')    // keep only active
    .map(s => s.serviceId)                  // extract just IDs
    .sort();                                // alphabetically sort

Pattern 4: Spread to merge objects

const body = { FleetId: "RIPE-NA", PeakTPM: 50000 };
const entryToInsert = { ...body };            // copy
entryToInsert.EventIndexId = eventIndexId;   // add new field
// entryToInsert = { FleetId: "RIPE-NA", PeakTPM: 50000, EventIndexId: 42 }

Pattern 5: async/await with database transaction

const auroraMysqlClient = new AuroraMysqlClient();
try {
    await auroraMysqlClient.startTransaction();      // begin transaction
    await hotwOps.insertRecord(data);                // insert
    await hotwOps.updateAnotherRecord(data);         // update
    await auroraMysqlClient.commitTransaction();     // commit (save both)
} catch (err) {
    await auroraMysqlClient.rollbackTransaction();   // undo BOTH if either failed
}

Section 15 β€” Quick Reference Card

Concept Syntax Example
Variable const x = 5 const eventId = "PD2024"
Function const f = (a) => a + 1 const double = x => x * 2
Async func async (a) => await db.get(a) See handlers
Class class MyClass { method() {} } class HOTW { static async get() {} }
Import const X = require('./X') const Util = require('../common/Util')
Export module.exports = MyClass Last line of every file
Try/catch try { await op() } catch(e) {} See all handlers
Template literal `Hello ${name}` `Fleet: ${fleetId}`
Spread { ...obj, newKey: val } { ...body, EventId: id }
Array map arr.map(x => x.id) services.map(s => s.ServiceId)
Array filter arr.filter(x => x.active) fleets.filter(f => f.region === 'NA')
Null guard x && x.prop ? x.prop : '' alias ? alias : ''