Platform.Function Approach

Create (Insert)

// Single row insert
var rowsAdded = Platform.Function.InsertData(
    "UserProfiles",
    ["SubscriberKey", "Email", "FirstName", "LastName", "CreatedAt", "Status"],
    [subscriberKey, email, firstName, lastName, Platform.Function.Now(), "active"]
);
// rowsAdded === 1 on success; throws on primary key conflict

Read (Lookup)

// Single field, single row
var email = Platform.Function.Lookup("UserProfiles", "Email", "SubscriberKey", subKey);

// Multiple rows
var rows = Platform.Function.LookupRows("Orders", "Status", "pending");
for (var i = 0; i < rows.length; i++) {
    var order = rows[i];
    Write(order.OrderId + ": " + order.Total + "<br>");
}

// Multiple rows with sort and limit
var recentOrders = Platform.Function.LookupOrderedRows(
    "Orders", 10, "CreatedAt desc",
    "SubscriberKey", subKey
);

Update

// Update rows matching a filter
var rowsUpdated = Platform.Function.UpdateData(
    "UserProfiles",
    ["SubscriberKey"],
    [subKey],
    ["Status", "UpdatedAt"],
    ["inactive", Platform.Function.Now()]
);
// rowsUpdated === 0 if no match (no error thrown)

Upsert

// Insert or update based on primary key
Platform.Function.UpsertData(
    "UserProfiles",
    ["SubscriberKey"],        // key columns
    [subKey],                 // key values
    ["Email", "FirstName", "Status", "UpdatedAt"], // data columns
    [email, firstName, "active", Platform.Function.Now()] // data values
);

Delete

var rowsDeleted = Platform.Function.DeleteData(
    "UserProfiles",
    ["SubscriberKey"],
    [subKey]
);

Show test script
<script runat="server">
/*
 * Chapter: Platform.Function Approach
 * Proves:
 *   1. InsertData/UpdateData/UpsertData/DeleteData typeof.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
assert("InsertData", typeof Platform.Function.InsertData, "clrmethodinfo");
assert("UpdateData", typeof Platform.Function.UpdateData, "clrmethodinfo");
assert("UpsertData", typeof Platform.Function.UpsertData, "clrmethodinfo");
assert("DeleteData", typeof Platform.Function.DeleteData, "clrmethodinfo");
</script>

Core Library Approach

Platform.Load("core", "1.1.5");
var de = DataExtension.Init("UserProfiles");

// Create
de.Rows.Add({
    SubscriberKey: subKey,
    Email: email,
    FirstName: firstName,
    Status: "active",
    CreatedAt: Platform.Function.Now()
});

// Read (with filter — required on CloudPages)
var rows = de.Rows.Retrieve({
    Property: "SubscriberKey",
    SimpleOperator: "equals",
    Value: subKey
});
var profile = rows.length > 0 ? rows[0] : null;

// Update
de.Rows.Update(
    { Status: "inactive", UpdatedAt: Platform.Function.Now() },
    ["SubscriberKey"],
    [subKey]
);

// Delete
de.Rows.Remove("SubscriberKey", subKey);

Show test script
<script runat="server">
/*
 * Chapter: Core Library Approach
 * Proves:
 *   1. DataExtension.Init after Load.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
Platform.Load("core", "1.1.5");
assert("DataExtension", typeOfThunk(function () { return typeof DataExtension; }), "object");
assert("Init typeof", typeOfThunk(function () { return typeof DataExtension.Init; }), "function");
</script>

Pattern: Safe Upsert with Existence Check

// String() first — a Lookup result throws on a truthiness test when the field is empty
var existing = String(Platform.Function.Lookup(
    "UserProfiles", "SubscriberKey", "SubscriberKey", subKey));

if (existing === "" || existing === "null") {
    // Insert
    Platform.Function.InsertData("UserProfiles",
        ["SubscriberKey", "Email", "CreatedAt"],
        [subKey, email, Platform.Function.Now()]
    );
} else {
    // Update
    Platform.Function.UpdateData("UserProfiles",
        ["SubscriberKey"], [subKey],
        ["Email", "UpdatedAt"],
        [email, Platform.Function.Now()]
    );
}

Show test script
<script runat="server">
/*
 * Chapter: Safe Upsert with Existence Check
 * Proves:
 *   1. Lookup miss → String null path then UpsertData typeof.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
var existing = String(null);
assert("miss path String(null)", existing, "null");
assert("UpsertData", typeof Platform.Function.UpsertData, "clrmethodinfo");
assert("Lookup", typeof Platform.Function.Lookup, "clrmethodinfo");
</script>

Pattern: Bulk Insert from Array

var submissions = Platform.Function.ParseJSON(rawBody + "");

var inserted = 0;
var errors = [];
for (var i = 0; i < submissions.length; i++) {
    var sub = submissions[i];
    if (!Platform.Function.IsEmailAddress(sub.email)) {
        errors.push({ index: i, reason: "invalid email" });
        continue;
    }
    try {
        Platform.Function.UpsertData("Leads",
            ["Email"], [sub.email],
            ["FirstName", "Source", "CreatedAt"],
            [sub.firstName || "", sub.source || "api", Platform.Function.Now()]
        );
        inserted++;
    } catch(e) {
        errors.push({ index: i, reason: e.message });
    }
}

Write(Platform.Function.Stringify({ inserted: inserted, errors: errors }));

Show test script
<script runat="server">
/*
 * Chapter: Bulk Insert from Array
 * Proves:
 *   1. Loop over array preparing rows (no DE write).
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
var rows = [{ email: "a@x.com" }, { email: "b@x.com" }];
var prepared = [];
for (var i = 0; i < rows.length; i++) prepared.push(rows[i].email);
assert("prepared join", prepared.join(","), "a@x.com,b@x.com");
</script>

Pattern: Soft Delete (Status Flag)

Prefer marking records inactive over deleting them:

// Soft delete — sets Status = "deleted", preserves record
Platform.Function.UpdateData(
    "Orders",
    ["OrderId"], [orderId],
    ["Status", "DeletedAt"],
    ["deleted", Platform.Function.Now()]
);

// When reading, filter out deleted records
var active = Platform.Function.LookupRows("Orders", "Status", "active");

Show test script
<script runat="server">
/*
 * Chapter: Soft Delete Status Flag
 * Proves:
 *   1. UpdateData typeof for status flag pattern.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
assert("UpdateData", typeof Platform.Function.UpdateData, "clrmethodinfo");
var status = "inactive";
assert("status flag", status, "inactive");
</script>

Pattern: Pagination with LookupOrderedRows

var PAGE_SIZE = 25;
var pageNum = parseInt(Platform.Request.GetQueryStringParameter("page") || "1", 10);
var offset = (pageNum - 1) * PAGE_SIZE;

// SSJS has no OFFSET support — get more rows and slice
var all = Platform.Function.LookupOrderedRows(
    "Products",
    offset + PAGE_SIZE, // get enough rows
    "Name asc",
    "Status", "active"
);

var page = [];
for (var i = offset; i < Math.min(offset + PAGE_SIZE, all.length); i++) {
    page.push(all[i]);
}

Write(Platform.Function.Stringify({
    page: pageNum,
    pageSize: PAGE_SIZE,
    items: page,
    hasMore: all.length > offset + PAGE_SIZE
}));
Show test script
<script runat="server">
/*
 * Chapter: Pagination with LookupOrderedRows
 * Proves:
 *   1. LookupOrderedRows typeof.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
assert("LookupOrderedRows", typeof Platform.Function.LookupOrderedRows, "clrmethodinfo");
</script>

See Also