Token导航 LogoToken导航

Agent Skill · 待分类

electrodb(electrodb 命令行)

electrodb 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

electrodb 是待分类类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在待分类类的 11,209 个 Skill 里排第 8,143。

总安装 318周安装 13GitHub Stars 公开资料未说明下载 103核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:electrodb(electrodb 命令行)
来源仓库:https://github.com/tejovanthn/rasikalife
仓库路径:skills/electrodb
安装命令:
npx skills add https://github.com/tejovanthn/rasikalife --skill electrodb
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/tejovanthn/rasikalife --skill electrodb

这个 Skill 是做什么的

这个 Skill 的分类还没最终确定,用法以来源仓库的说明为准。

electrodb 提供类型安全的 DynamoDB 应用开发模式,支持单表设计和高效查询。适用于需要高性能数据库操作的场景,支持实体定义、集合查询和复合属性处理。内置验证机制和 IntelliSense 支持,提升开发效率和数据一致性。安装前建议检查权限范围和是否涉及网络访问或数据持久化操作。

electrodb 属于待分类类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/electrodb

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/tejovanthn/rasikalife --skill electrodb;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

electrodb 怎么安装?

在终端执行 npx skills add https://github.com/tejovanthn/rasikalife --skill electrodb。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

electrodb 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

electrodb 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

electrodb 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

electrodb 用的人多吗?

公开数据是累计安装 318 次,近一周安装 13 次,下载 103 次。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

electrodb 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理待分类类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

待分类类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
infsh-cli

infsh CLI · Codex、Claude

4,374,576382权限未标注未明确未收录
ai-video-generation

AI 视频生成 · Codex、Claude

4,313,376382调用外部服务未明确有提醒
lark-openapi-explorer

云雀 OpenAPI 浏览器 · Codex、Claude

2,064,1209,011需要联网未明确有提醒
lark-workflow-standup-report

Lark 工作流程站立报告 · Codex、Claude

2,041,6328,978需要联网未明确通过
use-my-browser

使用我的浏览器 · Codex、Claude

1,848,49140操作浏览器未明确未通过
electrodb

electrodb 命令行 · 本页

318—需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

ElectroDB Patterns

This skill covers ElectroDB v2+ patterns for building type-safe, performant DynamoDB applications with single-table design.

Core Concepts

ElectroDB Benefits:

  • Type-safe queries and mutations
  • Automatic key generation
  • Collection queries (fetch related entities)
  • Composite attributes and computed keys
  • Built-in validation
  • Query building with IntelliSense

Key Terms:

  • Entity: Table schema definition
  • Service: Multiple entities working together
  • Collection: Group related entities for efficient queries
  • Access Pattern: How you retrieve data (GSI, queries)
  • Composite Attributes: Combine fields into keys

Installation

npm install electrodb

Entity Definition

Basic Entity

// src/entities/user.entity.ts
import { Entity } from "electrodb";
import { dynamoDBClient } from "../lib/db";

export const UserEntity = new Entity(
  {
    model: {
      entity: "user",
      version: "1",
      service: "myapp",
    },
    attributes: {
      userId: {
        type: "string",
        required: true,
      },
      email: {
        type: "string",
        required: true,
      },
      name: {
        type: "string",
        required: true,
      },
      role: {
        type: ["ADMIN", "TEACHER", "STUDENT"] as const,
        required: true,
        default: "STUDENT",
      },
      verified: {
        type: "boolean",
        default: false,
      },
      createdAt: {
        type: "string",
        required: true,
        default: () => new Date().toISOString(),
        readOnly: true,
      },
      updatedAt: {
        type: "string",
        required: true,
        default: () => new Date().toISOString(),
        set: () => new Date().toISOString(),
        watch: "*", // Update on any attribute change
      },
    },
    indexes: {
      primary: {
        pk: {
          field: "pk",
          composite: ["userId"],
        },
        sk: {
          field: "sk",
          composite: [],
        },
      },
      byEmail: {
        index: "gsi1",
        pk: {
          field: "gsi1pk",
          composite: ["email"],
        },
        sk: {
          field: "gsi1sk",
          composite: [],
        },
      },
      byRole: {
        index: "gsi2",
        pk: {
          field: "gsi2pk",
          composite: ["role"],
        },
        sk: {
          field: "gsi2sk",
          composite: ["createdAt"],
        },
      },
    },
  },
  { client: dynamoDBClient, table: "MyAppTable" }
);

// Type inference
export type User = typeof UserEntity.model.schema;
export type UserItem = ReturnType<typeof UserEntity.parse>;

Advanced Attributes

export const SessionEntity = new Entity({
  model: {
    entity: "session",
    version: "1",
    service: "myapp",
  },
  attributes: {
    sessionId: {
      type: "string",
      required: true,
    },
    userId: {
      type: "string",
      required: true,
    },
    courseId: {
      type: "string",
      required: true,
    },
    // Composite attribute (virtual)
    userCourse: {
      type: "string",
      hidden: true, // Not returned in queries
      readOnly: true,
      get: (_, item) => `${item.userId}#${item.courseId}`,
    },
    // Date as string
    scheduledDate: {
      type: "string",
      required: true,
      validate: (value) => {
        const date = new Date(value);
        if (isNaN(date.getTime())) {
          throw new Error("Invalid date format");
        }
      },
    },
    // Duration in minutes
    durationMinutes: {
      type: "number",
      required: true,
      validate: (value) => {
        if (value < 15 || value > 480) {
          throw new Error("Duration must be between 15 and 480 minutes");
        }
      },
    },
    // Enum-like status
    status: {
      type: ["SCHEDULED", "IN_PROGRESS", "COMPLETED", "CANCELLED"] as const,
      required: true,
      default: "SCHEDULED",
    },
    // Optional nested object
    metadata: {
      type: "map",
      properties: {
        zoomMeetingId: { type: "string" },
        recordingUrl: { type: "string" },
        notes: { type: "string" },
      },
    },
    // Array of strings
    tags: {
      type: "list",
      items: {
        type: "string",
      },
    },
    // Set attribute (for watch)
    watchedAttributes: {
      type: "set",
      items: "string",
    },
  },
  indexes: {
    primary: {
      pk: {
        field: "pk",
        composite: ["sessionId"],
      },
      sk: {
        field: "sk",
        composite: [],
      },
    },
    byCourse: {
      index: "gsi1",
      pk: {
        field: "gsi1pk",
        composite: ["courseId"],
      },
      sk: {
        field: "gsi1sk",
        composite: ["scheduledDate"],
      },
    },
    byUser: {
      index: "gsi2",
      pk: {
        field: "gsi2pk",
        composite: ["userId"],
      },
      sk: {
        field: "gsi2sk",
        composite: ["scheduledDate"],
      },
    },
  },
});

CRUD Operations

Create

// Create single item
const user = await UserEntity.create({
  userId: "user_123",
  email: "john@example.com",
  name: "John Doe",
  role: "STUDENT",
}).go();

// Create with custom options
const user = await UserEntity.create({
  userId: "user_123",
  email: "john@example.com",
  name: "John Doe",
}).go({
  response: "all_new", // Return all attributes
});

// Conditional create (fail if exists)
const user = await UserEntity.create({
  userId: "user_123",
  email: "john@example.com",
  name: "John Doe",
}).go({
  conditions: { exists: false }, // Only create if doesn't exist
});

Read (Get)

// Get single item
const user = await UserEntity.get({
  userId: "user_123",
}).go();

// user.data contains the item or null if not found
if (!user.data) {
  throw new Error("User not found");
}

// Get with specific attributes
const user = await UserEntity.get({
  userId: "user_123",
}).go({
  attributes: ["name", "email"], // Only fetch these fields
});

// Get with consistent read
const user = await UserEntity.get({
  userId: "user_123",
}).go({
  consistent: true, // Consistent read (costs more)
});

Update

// Update specific attributes
const updated = await UserEntity.update({
  userId: "user_123",
}).set({
  name: "Jane Doe",
  verified: true,
}).go();

// Add to number
await SessionEntity.update({
  sessionId: "session_123",
}).add({
  attendeeCount: 1, // Increment by 1
}).go();

// Remove attribute
await UserEntity.update({
  userId: "user_123",
}).remove(["temporaryToken"]).go();

// Conditional update
await UserEntity.update({
  userId: "user_123",
}).set({
  verified: true,
}).go({
  conditions: { verified: false }, // Only update if not already verified
});

// Update with custom condition expression
await UserEntity.update({
  userId: "user_123",
}).set({
  name: "New Name",
}).go({
  conditions: {
    attr: "role",
    eq: "ADMIN", // Only update if role is ADMIN
  },
});

Delete

// Delete item
await UserEntity.delete({
  userId: "user_123",
}).go();

// Conditional delete
await UserEntity.delete({
  userId: "user_123",
}).go({
  conditions: { role: "STUDENT" }, // Only delete if student
});

// Return deleted item
const deleted = await UserEntity.delete({
  userId: "user_123",
}).go({
  response: "all_old", // Return the deleted item
});

Queries

Basic Queries

// Query by primary key
const users = await UserEntity.query
  .primary({
    userId: "user_123",
  })
  .go();

// users.data is an array of items

// Query with begins_with
const sessions = await SessionEntity.query
  .byCourse({
    courseId: "course_123",
  })
  .begins({
    scheduledDate: "2025-01", // All sessions in January 2025
  })
  .go();

// Query with between
const sessions = await SessionEntity.query
  .byCourse({
    courseId: "course_123",
  })
  .between(
    { scheduledDate: "2025-01-01" },
    { scheduledDate: "2025-01-31" }
  )
  .go();

// Query with gt/gte/lt/lte
const sessions = await SessionEntity.query
  .byCourse({
    courseId: "course_123",
  })
  .gt({ scheduledDate: "2025-01-01" }) // Greater than
  .go();

Query Options

// Limit results
const users = await UserEntity.query
  .byRole({
    role: "STUDENT",
  })
  .go({
    limit: 10, // Only return 10 items
  });

// Pagination
const firstPage = await UserEntity.query
  .byRole({
    role: "STUDENT",
  })
  .go({
    limit: 10,
  });

// Get next page using cursor
if (firstPage.cursor) {
  const secondPage = await UserEntity.query
    .byRole({
      role: "STUDENT",
    })
    .go({
      limit: 10,
      cursor: firstPage.cursor,
    });
}

// Scan index forward/backward
const latest = await SessionEntity.query
  .byCourse({
    courseId: "course_123",
  })
  .go({
    order: "desc", // Most recent first (ScanIndexForward: false)
  });

// Filter after query
const activeSessions = await SessionEntity.query
  .byCourse({
    courseId: "course_123",
  })
  .where(
    ({ status }, { eq }) => eq(status, "IN_PROGRESS")
  )
  .go();

// Select specific attributes
const users = await UserEntity.query
  .byRole({
    role: "STUDENT",
  })
  .go({
    attributes: ["userId", "name", "email"],
  });

Complex Filters

// Multiple conditions (AND)
const sessions = await SessionEntity.query
  .byCourse({
    courseId: "course_123",
  })
  .where(
    ({ status, durationMinutes }, { eq, gte }) => `
      ${eq(status, "COMPLETED")} AND ${gte(durationMinutes, 60)}
    `
  )
  .go();

// OR conditions
const sessions = await SessionEntity.query
  .byCourse({
    courseId: "course_123",
  })
  .where(
    ({ status }, { eq }) => `
      ${eq(status, "SCHEDULED")} OR ${eq(status, "IN_PROGRESS")}
    `
  )
  .go();

// NOT condition
const users = await UserEntity.query
  .byRole({
    role: "STUDENT",
  })
  .where(
    ({ verified }, { eq }) => `NOT ${eq(verified, true)}`
  )
  .go();

// Contains (for strings)
const users = await UserEntity.scan
  .where(
    ({ email }, { contains }) => contains(email, "@gmail.com")
  )
  .go();

// Between (in filter)
const sessions = await SessionEntity.query
  .byCourse({
    courseId: "course_123",
  })
  .where(
    ({ durationMinutes }, { between }) =>
      between(durationMinutes, 30, 120)
  )
  .go();

Scan Operations

// Scan entire table (use sparingly!)
const allUsers = await UserEntity.scan.go();

// Scan with filter
const verifiedUsers = await UserEntity.scan
  .where(
    ({ verified }, { eq }) => eq(verified, true)
  )
  .go();

// Scan with pagination
const firstPage = await UserEntity.scan.go({
  limit: 100,
});

if (firstPage.cursor) {
  const secondPage = await UserEntity.scan.go({
    cursor: firstPage.cursor,
  });
}

// Parallel scan for large tables
const segment1 = await UserEntity.scan.go({
  segments: { total: 4, segment: 0 },
});
const segment2 = await UserEntity.scan.go({
  segments: { total: 4, segment: 1 },
});
// ... segments 2 and 3

Batch Operations

Batch Get

// Batch get multiple items
const results = await UserEntity.get([
  { userId: "user_1" },
  { userId: "user_2" },
  { userId: "user_3" },
]).go();

// results.data is an array of items (nulls for not found)

// Batch get with options
const results = await UserEntity.get([
  { userId: "user_1" },
  { userId: "user_2" },
]).go({
  unprocessed: "raw", // Return unprocessed keys
  consistent: true,
});

Batch Write

// Batch put
await UserEntity.put([
  {
    userId: "user_1",
    email: "user1@example.com",
    name: "User 1",
    role: "STUDENT",
  },
  {
    userId: "user_2",
    email: "user2@example.com",
    name: "User 2",
    role: "STUDENT",
  },
]).go();

// Batch delete
await UserEntity.delete([
  { userId: "user_1" },
  { userId: "user_2" },
]).go();

// Note: Batch operations support up to 25 items
// For more, chunk them:
const chunks = chunk(items, 25);
for (const chunk of chunks) {
  await UserEntity.put(chunk).go();
}

Transactions

import { Entity, Service } from "electrodb";

// Define entities first
const service = new Service({
  user: UserEntity,
  session: SessionEntity,
});

// Transactional write
await service
  .transaction
  .write(({ user, session }) => [
    user.create({
      userId: "user_123",
      email: "john@example.com",
      name: "John Doe",
      role: "STUDENT",
    }),
    session.create({
      sessionId: "session_456",
      userId: "user_123",
      courseId: "course_789",
      scheduledDate: "2025-01-15",
      durationMinutes: 60,
      status: "SCHEDULED",
    }),
  ])
  .go();

// Conditional transaction
await service
  .transaction
  .write(({ user, session }) => [
    user.update({ userId: "user_123" })
      .set({ verified: true })
      .commit({ conditions: { verified: false } }),
    session.create({
      sessionId: "session_456",
      userId: "user_123",
      courseId: "course_789",
      scheduledDate: "2025-01-15",
      durationMinutes: 60,
    }).commit({ conditions: { exists: false } }),
  ])
  .go();

// Transactional get (requires primary keys)
const results = await service
  .transaction
  .get([
    { user: { userId: "user_123" } },
    { session: { sessionId: "session_456" } },
  ])
  .go();

Collections

Collections allow querying multiple entity types together:

export const UserEntity = new Entity({
  model: {
    entity: "user",
    version: "1",
    service: "myapp",
  },
  attributes: {
    userId: { type: "string", required: true },
    email: { type: "string", required: true },
    name: { type: "string", required: true },
  },
  indexes: {
    primary: {
      pk: { field: "pk", composite: ["userId"] },
      sk: { field: "sk", composite: [] },
    },
    byOrg: {
      index: "gsi1",
      pk: { field: "gsi1pk", composite: ["orgId"] },
      sk: { field: "gsi1sk", composite: ["userId"] },
      collection: "organization", // Collection name
    },
  },
});

export const CourseEntity = new Entity({
  model: {
    entity: "course",
    version: "1",
    service: "myapp",
  },
  attributes: {
    courseId: { type: "string", required: true },
    orgId: { type: "string", required: true },
    title: { type: "string", required: true },
  },
  indexes: {
    primary: {
      pk: { field: "pk", composite: ["courseId"] },
      sk: { field: "sk", composite: [] },
    },
    byOrg: {
      index: "gsi1",
      pk: { field: "gsi1pk", composite: ["orgId"] },
      sk: { field: "gsi1sk", composite: ["courseId"] },
      collection: "organization", // Same collection
    },
  },
});

// Create service
const service = new Service({
  user: UserEntity,
  course: CourseEntity,
});

// Query collection (gets both users and courses for an org)
const orgData = await service.collections
  .organization({ orgId: "org_123" })
  .go();

// orgData.data contains { user: [...], course: [...] }

Service Patterns

// Create a service with multiple entities
const AppService = new Service(
  {
    user: UserEntity,
    session: SessionEntity,
    course: CourseEntity,
    enrollment: EnrollmentEntity,
  },
  { client: dynamoDBClient, table: "MyAppTable" }
);

// Use service for transactions
await AppService.transaction.write(({ user, enrollment }) => [
  user.update({ userId: "user_123" }).set({ verified: true }),
  enrollment.create({
    enrollmentId: "enroll_456",
    userId: "user_123",
    courseId: "course_789",
    enrolledAt: new Date().toISOString(),
  }),
]).go();

// Collections query
const userData = await AppService.collections
  .userCourses({ userId: "user_123" })
  .go();

Advanced Patterns

Optimistic Locking

// Add version attribute
export const UserEntity = new Entity({
  model: { entity: "user", version: "1", service: "myapp" },
  attributes: {
    userId: { type: "string", required: true },
    name: { type: "string", required: true },
    version: {
      type: "number",
      required: true,
      default: 0,
      watch: "*", // Increment on any change
      set: (_, item) => (item.version ?? 0) + 1,
    },
  },
  indexes: {
    primary: {
      pk: { field: "pk", composite: ["userId"] },
      sk: { field: "sk", composite: [] },
    },
  },
});

// Update with version check
const user = await UserEntity.get({ userId: "user_123" }).go();

await UserEntity.update({ userId: "user_123" })
  .set({ name: "New Name" })
  .go({
    conditions: { version: user.data!.version }, // Only update if version matches
  });

Soft Delete

export const UserEntity = new Entity({
  model: { entity: "user", version: "1", service: "myapp" },
  attributes: {
    userId: { type: "string", required: true },
    name: { type: "string", required: true },
    deletedAt: { type: "string" }, // ISO timestamp or null
  },
  indexes: {
    primary: {
      pk: { field: "pk", composite: ["userId"] },
      sk: { field: "sk", composite: [] },
    },
    active: {
      index: "gsi1",
      pk: { field: "gsi1pk", composite: ["deletedAt"] },
      sk: { field: "gsi1sk", composite: ["userId"] },
    },
  },
});

// Soft delete
await UserEntity.update({ userId: "user_123" })
  .set({ deletedAt: new Date().toISOString() })
  .go();

// Query only active users (where deletedAt is not set)
// Use sparse index - items without gsi1pk won't appear
const activeUsers = await UserEntity.query
  .active({ deletedAt: "__ACTIVE__" }) // Special marker
  .go();

Sparse Indexes

// Only index published posts
export const PostEntity = new Entity({
  model: { entity: "post", version: "1", service: "myapp" },
  attributes: {
    postId: { type: "string", required: true },
    title: { type: "string", required: true },
    published: { type: "boolean", default: false },
    publishedAt: { type: "string" }, // Only set when published
  },
  indexes: {
    primary: {
      pk: { field: "pk", composite: ["postId"] },
      sk: { field: "sk", composite: [] },
    },
    published: {
      index: "gsi1",
      pk: {
        field: "gsi1pk",
        composite: [], // No composite, just a constant
        template: "PUBLISHED#POST", // All published posts
      },
      sk: {
        field: "gsi1sk",
        composite: ["publishedAt"],
      },
    },
  },
});

// Only items with gsi1pk set will appear in the index
await PostEntity.create({
  postId: "post_123",
  title: "My Post",
  published: true,
  publishedAt: new Date().toISOString(),
}).go();

// Query only published posts
const publishedPosts = await PostEntity.query
  .published({})
  .go();

Time-to-Live (TTL)

export const SessionTokenEntity = new Entity({
  model: { entity: "sessionToken", version: "1", service: "myapp" },
  attributes: {
    token: { type: "string", required: true },
    userId: { type: "string", required: true },
    expiresAt: {
      type: "number", // Unix timestamp in seconds
      required: true,
      default: () => Math.floor(Date.now() / 1000) + 86400, // 24 hours
    },
  },
  indexes: {
    primary: {
      pk: { field: "pk", composite: ["token"] },
      sk: { field: "sk", composite: [] },
    },
  },
});

// Configure TTL on the table (do this once)
// Table attribute: expiresAt
// DynamoDB will automatically delete expired items

Type Helpers

// Extract types from entity
type User = typeof UserEntity.model.schema;
type UserItem = ReturnType<typeof UserEntity.parse>;
type UserKeys = Parameters<typeof UserEntity.get>[0];

// Infer return types
type QueryResult = Awaited<ReturnType<typeof UserEntity.query.byRole>>;
type CreateResult = Awaited<ReturnType<typeof UserEntity.create>>;

// Custom typed helpers
export async function getUser(userId: string): Promise<UserItem | null> {
  const result = await UserEntity.get({ userId }).go();
  return result.data;
}

export async function requireUser(userId: string): Promise<UserItem> {
  const user = await getUser(userId);
  if (!user) {
    throw new Error("User not found");
  }
  return user;
}

Error Handling

import { ElectroError } from "electrodb";

try {
  await UserEntity.create({
    userId: "user_123",
    email: "john@example.com",
    name: "John Doe",
    role: "STUDENT",
  }).go();
} catch (error) {
  if (error instanceof ElectroError) {
    // ElectroDB-specific error
    console.error("ElectroDB error:", error.message);

    // Check error codes
    if (error.code === 4001) {
      // Validation error
    } else if (error.code === 5003) {
      // Item already exists (conditional check failed)
    }
  } else {
    // DynamoDB SDK error
    console.error("DynamoDB error:", error);
  }
}

// Common error codes:
// 4001: Invalid/missing required attribute
// 4002: Invalid attribute value
// 5001: DynamoDB operation failed
// 5003: Conditional check failed

Best Practices

1. Use Type Exports

// Export types for reuse
export type User = typeof UserEntity.model.schema;
export type UserItem = ReturnType<typeof UserEntity.parse>;

2. Validate Complex Data

attributes: {
  email: {
    type: "string",
    required: true,
    validate: (email) => {
      if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
        throw new Error("Invalid email format");
      }
    },
  },
}

3. Use Readonly for Timestamps

createdAt: {
  type: "string",
  required: true,
  default: () => new Date().toISOString(),
  readOnly: true, // Cannot be updated
}

4. Watch for Auto-Updates

updatedAt: {
  type: "string",
  required: true,
  default: () => new Date().toISOString(),
  set: () => new Date().toISOString(),
  watch: "*", // Update on any attribute change
}

5. Use Hidden Attributes for Computed Values

fullName: {
  type: "string",
  hidden: true, // Not stored in DB
  get: (_, item) => `${item.firstName} ${item.lastName}`,
}

6. Design Access Patterns First

Before creating entities, list your access patterns:

  1. Get user by ID
  2. Get user by email
  3. List users by role
  4. List sessions by course
  5. List sessions by user

Then design indexes to support these patterns.

7. Use Collections for Related Entities

Group related entities under the same partition key for efficient queries.

8. Check.data Before Using

const result = await UserEntity.get({ userId }).go();
if (!result.data) {
  throw new Error("Not found");
}
const user = result.data; // Now TypeScript knows it's not null

9. Use Services for Related Operations

const service = new Service({
  user: UserEntity,
  session: SessionEntity,
});

// Better for transactions and collections

10. Leverage Composite Attributes

sk: {
  field: "sk",
  composite: ["courseId", "userId"], // Creates: COURSE#123#USER#456
}

Common Gotchas

  1. Batch size limit: Max 25 items per batch operation
  2. Transaction limit: Max 100 items per transaction (across all tables)
  3. Cursor is opaque: Don't parse or modify cursors
  4. Consistent reads: Cost more, use sparingly
  5. Scan is expensive: Avoid scanning large tables
  6. Index projections: Consider what attributes you need in GSIs
  7. Type inference: Let TypeScript infer types from entity definitions
  8. Hidden attributes: Not stored in DB, computed on read
  9. ReadOnly attributes: Can't be updated after creation
  10. Watch attribute: Triggers on specified attribute changes

Testing

import { describe, test, expect, beforeAll } from "vitest";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { UserEntity } from "./user.entity";

// Use local DynamoDB for tests
const localClient = new DynamoDBClient({
  endpoint: "http://localhost:8000",
});

describe("UserEntity", () => {
  beforeAll(async () => {
    // Create test table
    await createTestTable();
  });

  test("creates user", async () => {
    const result = await UserEntity.create({
      userId: "user_123",
      email: "test@example.com",
      name: "Test User",
      role: "STUDENT",
    }).go();

    expect(result.data).toMatchObject({
      userId: "user_123",
      email: "test@example.com",
      name: "Test User",
    });
  });

  test("queries users by role", async () => {
    const result = await UserEntity.query
      .byRole({ role: "STUDENT" })
      .go();

    expect(result.data.length).toBeGreaterThan(0);
  });
});

Resources

平台分布

Codex

35.74%
按下载量换算37

Claude

28.54%
按下载量换算29

Cursor

16.9%
按下载量换算17

Gemini CLI

8.93%
按下载量换算9

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills