Skip to main content
Version: 0.13.0 (Previous)

🆔 Entity Utilities

The Nodeblocks SDK provides essential utilities for creating and managing database entities with automatic field generation. These utilities ensure consistent entity structure across your application with automatic timestamps and unique identifiers.


🎯 Overview

Entity utilities provide standardized functions for creating and updating database entities with automatic field generation. They ensure consistency in entity structure and reduce boilerplate code.

Key Features

  • Automatic ID Generation: UUID v4 generation for entity identifiers (via the uuid package)
  • Timestamp Management: Automatic createdAt and updatedAt field generation
  • Consistent Structure: Standardized entity creation and update patterns
  • Type Safety: Generic input Record<string, unknown> with exported BaseEntity type

The module exports three symbols: createBaseEntity, updateBaseEntity, and BaseEntity.


🆔 Entity Creation

createBaseEntity

Creates a new entity with automatic field generation.

import { utils } from '@nodeblocks/backend-sdk';

const { createBaseEntity } = utils;

const userData = {
email: 'user@example.com',
name: 'John Doe',
role: 'user'
};

const user = createBaseEntity(userData);

Generated fields:

  • id: UUID v4 string
  • createdAt: ISO timestamp string
  • updatedAt: ISO timestamp string

Parameters:

  • data: Entity payload (Record<string, unknown>)

Behavior:

  • Returns { ...data, createdAt, id, updatedAt }
  • Generated fields always overwrite same-named keys in data (e.g. a client-supplied id is replaced)
  • ID generation uses uuid v4 directly (equivalent to generateUUID, but not the same function call)

Result:

{
email: 'user@example.com',
name: 'John Doe',
role: 'user',
id: '550e8400-e29b-41d4-a716-446655440000',
createdAt: '2024-01-15T10:30:00.000Z',
updatedAt: '2024-01-15T10:30:00.000Z'
}

Usage in Handlers

Handler examples below are illustrative — ok, mergeData, and RouteHandlerPayload come from other SDK modules (neverthrow, handler utilities, primitives).

import { ok } from 'neverthrow';
import { utils, handlers } from '@nodeblocks/backend-sdk';
import { primitives } from '@nodeblocks/backend-sdk';

const { createBaseEntity } = utils;
const { mergeData } = handlers;
type RouteHandlerPayload = primitives.RouteHandlerPayload;

const createUserHandler = async (payload: RouteHandlerPayload) => {
const { params } = payload;

// Create entity with base fields
const userEntity = createBaseEntity(params.requestBody);

// Save to database
const result = await payload.context.db.users.insertOne(userEntity);

return ok(mergeData(payload, { userId: userEntity.id }));
};

🔄 Entity Updates

updateBaseEntity

Updates an existing entity with automatic timestamp management.

import { utils } from '@nodeblocks/backend-sdk';

const { updateBaseEntity } = utils;

const updateData = {
name: 'Jane Doe',
email: 'jane@example.com'
};

const updatedUser = updateBaseEntity(updateData);

Generated fields:

  • updatedAt: Current ISO timestamp string

Parameters:

  • data: Fields to include in the update (Record<string, unknown>)

Behavior:

  • Returns { ...data, updatedAt } — does not fetch or merge an existing document
  • Does not add id or createdAt; callers pass the fields they want in a MongoDB $set
  • Pass an empty object to touch updatedAt only: updateBaseEntity({})

Touch updatedAt only:

import { utils } from '@nodeblocks/backend-sdk';

const { updateBaseEntity } = utils;

// Bump updatedAt without changing other fields (common SDK pattern)
await collection.updateOne(
{ id: entityId },
{ $set: updateBaseEntity({}) }
);

Result:

{
name: 'Jane Doe',
email: 'jane@example.com',
updatedAt: '2024-01-15T11:45:00.000Z'
}

Usage in Update Handlers

import { ok } from 'neverthrow';
import { utils, handlers } from '@nodeblocks/backend-sdk';
import { primitives } from '@nodeblocks/backend-sdk';

const { updateBaseEntity } = utils;
const { mergeData } = handlers;
type RouteHandlerPayload = primitives.RouteHandlerPayload;

const updateUserHandler = async (payload: RouteHandlerPayload) => {
const { params, context } = payload;
const userId = params.requestParams?.userId;

// Prepare update data with timestamp
const updateData = updateBaseEntity(params.requestBody);

// Update in database
const result = await context.db.users.updateOne(
{ id: userId },
{ $set: updateData }
);

return ok(mergeData(payload, { updated: true }));
};

🔧 Advanced Usage

The examples below are illustrative patterns built on SDK utilities — they are not additional SDK exports.

Nested Sub-Entities

The SDK also uses createBaseEntity for nested sub-documents (attachments, images, follow records, one-time tokens), not only top-level database documents:

import { utils } from '@nodeblocks/backend-sdk';

const { createBaseEntity, updateBaseEntity } = utils;

// Push a nested entity with its own id and timestamps
await productsCollection.updateOne(
{ id: productId },
{
$push: { images: createBaseEntity({ url: imageUrl, alt: 'Product photo' }) },
$set: updateBaseEntity({}),
}
);

Custom Entity Creation

import { utils } from '@nodeblocks/backend-sdk';

const { createBaseEntity } = utils;

const createProduct = (productData: ProductData) => {
const baseEntity = createBaseEntity(productData);

// Add custom fields
return {
...baseEntity,
status: 'active',
category: productData.category || 'general'
};
};

const product = createProduct({
name: 'Sample Product',
price: 29.99,
description: 'A sample product'
});

Batch Entity Creation

import { utils } from '@nodeblocks/backend-sdk';

const { createBaseEntity } = utils;

const createMultipleUsers = (usersData: UserData[]) => {
return usersData.map(userData => createBaseEntity(userData));
};

const users = createMultipleUsers([
{ name: 'User 1', email: 'user1@example.com' },
{ name: 'User 2', email: 'user2@example.com' }
]);

Conditional Updates

import { utils } from '@nodeblocks/backend-sdk';

const { updateBaseEntity } = utils;

const updateUserConditionally = (userId: string, updateData: Partial<User>) => {
const baseUpdate = updateBaseEntity(updateData);

// Add conditional logic
if (updateData.status === 'inactive') {
return {
...baseUpdate,
deactivatedAt: new Date().toISOString()
};
}

return baseUpdate;
};

📊 Types

BaseEntity

Standard shape for entities with generated base fields. Used as a generic constraint in blocks (e.g. chat messages, products):

import { utils } from '@nodeblocks/backend-sdk';

type BaseEntity = utils.BaseEntity;

// Equivalent to:
interface BaseEntity {
id: string;
createdAt: string;
updatedAt: string;
}

createBaseEntity(data) returns data merged with these three fields. Extend BaseEntity when typing collections or block functions:

type Product = BaseEntity & {
name: string;
price: number;
};

📊 Field Specifications

Generated Fields

FieldTypeDescriptionGenerated By
idstringUUID v4 identifiercreateBaseEntity
createdAtstringISO timestamp of creationcreateBaseEntity
updatedAtstringISO timestamp of last updateBoth functions

Field Format

  • ID: UUID v4 format (550e8400-e29b-41d4-a716-446655440000)
  • Timestamps: ISO 8601 format (2024-01-15T10:30:00.000Z)

📐️ Best Practices

1. Consistent Usage

// ✅ Good: Always use entity utilities
const user = createBaseEntity(userData);
const updatedUser = updateBaseEntity(updateData);

// ❌ Avoid: Manual field generation — use createBaseEntity instead
const user = {
...userData,
id: generateUUID(), // createBaseEntity handles id/timestamps internally
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};

See Common Utilities for standalone UUID generation when you need extra unique fields beyond the base entity.

2. Type Safety

// ✅ Good: Use proper typing
interface UserData {
email: string;
name: string;
}

const userData: UserData = { email: 'user@example.com', name: 'John' };
const user = createBaseEntity(userData);

3. Database Integration

// ✅ Good: Use in database operations
const createUser = async (userData: UserData) => {
const entity = createBaseEntity(userData);
return await db.users.insertOne(entity);
};

const updateUser = async (userId: string, updateData: Partial<UserData>) => {
const update = updateBaseEntity(updateData);
return await db.users.updateOne({ id: userId }, { $set: update });
};

🔗 See Also