Task manager
In this example we will be modeling a task manager app. The app will associate employees to tasks, projects, and teams
Table Definition
Entities
Employee
import moment from "moment";
import { v4 as uuid } from "uuid";
import { Entity, EntityItem, QueryResponse, CreateEntityItem } from "electrodb";
import { table, client } from "../config";
export const employee = new Entity(
{
model: {
entity: "employee",
version: "1",
service: "taskmanager",
},
attributes: {
employee: {
type: "string",
default: () => uuid(),
},
firstName: {
type: "string",
required: true,
},
lastName: {
type: "string",
required: true,
},
office: {
type: "string",
required: true,
},
title: {
type: "string",
required: true,
},
team: {
type: [
"development",
"marketing",
"finance",
"product",
"cool cats and kittens",
] as const,
required: true,
},
salary: {
type: "string",
required: true,
},
manager: {
type: "string",
},
dateHired: {
type: "string",
validate: (date: string) => {
if (!moment(date).isValid) {
throw new Error("Invalid date format");
}
},
},
birthday: {
type: "string",
validate: (date: string) => {
if (!moment(date).isValid) {
throw new Error("Invalid date format");
}
},
},
},
indexes: {
employee: {
pk: {
field: "pk",
composite: ["employee"],
},
sk: {
field: "sk",
composite: [],
},
},
coworkers: {
index: "gsi1pk-gsi1sk-index",
collection: "workplaces",
pk: {
field: "gsi1pk",
composite: ["office"],
},
sk: {
field: "gsi1sk",
composite: ["team", "title", "employee"],
},
},
teams: {
index: "gsi2pk-gsi2sk-index",
pk: {
field: "gsi2pk",
composite: ["team"],
},
sk: {
field: "gsi2sk",
composite: ["dateHired", "title"],
},
},
employeeLookup: {
collection: "assignments",
index: "gsi3pk-gsi3sk-index",
pk: {
field: "gsi3pk",
composite: ["employee"],
},
sk: {
field: "gsi3sk",
composite: [],
},
},
roles: {
index: "gsi4pk-gsi4sk-index",
pk: {
field: "gsi4pk",
composite: ["title"],
},
sk: {
field: "gsi4sk",
composite: ["salary"],
},
},
directReports: {
index: "gsi5pk-gsi5sk-index",
pk: {
field: "gsi5pk",
composite: ["manager"],
},
sk: {
field: "gsi5sk",
composite: ["team", "office"],
},
},
},
},
{ table, client },
);
export type EmployeeItem = EntityItem<typeof employee>;
export type CreateEmployeeItem = CreateEntityItem<typeof employee>;
export type EmployeeQueryResponse = QueryResponse<typeof employee>;
Office
import { Entity, EntityItem, QueryResponse, CreateEntityItem } from "electrodb";
import { table, client } from "../config";
export const office = new Entity(
{
model: {
entity: "office",
version: "1",
service: "taskmanager",
},
attributes: {
office: {
type: "string",
},
country: {
type: "string",
},
state: {
type: "string",
},
city: {
type: "string",
},
zip: {
type: "string",
},
address: {
type: "string",
},
},
indexes: {
locations: {
pk: {
field: "pk",
composite: ["country", "state"],
},
sk: {
field: "sk",
composite: ["city", "zip", "office"],
},
},
office: {
index: "gsi1pk-gsi1sk-index",
collection: "workplaces",
pk: {
field: "gsi1pk",
composite: ["office"],
},
sk: {
field: "gsi1sk",
composite: [],
},
},
},
},
{ table, client },
);
export type OfficeItem = EntityItem<typeof office>;
export type CreateOfficeItem = CreateEntityItem<typeof office>;
export type OfficeQueryResponse = QueryResponse<typeof office>;
Task
import { Entity, EntityItem, QueryResponse, CreateEntityItem } from "electrodb";
import { table, client } from "../config";
export const task = new Entity(
{
model: {
entity: "task",
version: "1",
service: "taskmanager",
},
attributes: {
task: {
type: "string",
required: true,
},
project: {
type: "string",
required: true,
},
employee: {
type: "string",
required: true,
},
description: {
type: "string",
},
status: {
type: ["open", "in-progress", "closed"] as const,
default: "open",
},
points: {
type: "number",
required: true,
},
comments: {
type: "any",
},
},
indexes: {
task: {
pk: {
field: "pk",
composite: ["task"],
},
sk: {
field: "sk",
composite: ["project", "employee"],
},
},
project: {
index: "gsi1pk-gsi1sk-index",
pk: {
field: "gsi1pk",
composite: ["project"],
},
sk: {
field: "gsi1sk",
composite: ["employee", "status"],
},
},
assigned: {
collection: "assignments",
index: "gsi3pk-gsi3sk-index",
pk: {
field: "gsi3pk",
composite: ["employee"],
},
sk: {
field: "gsi3sk",
composite: ["project", "status"],
},
},
statuses: {
index: "gsi4pk-gsi4sk-index",
pk: {
field: "gsi4pk",
composite: ["status"],
},
sk: {
field: "gsi4sk",
composite: ["project", "employee"],
},
},
},
},
{ table, client },
);
export type TaskItem = EntityItem<typeof task>;
export type CreateTaskItem = CreateEntityItem<typeof task>;
export type TaskQueryResponse = QueryResponse<typeof task>;
Service
export const taskManager = new Service({
employee,
task,
office,
});
Access Patterns
Find office and staff information an office
Use Collections to query across entities.
const workplace = await taskManager.collections
.workplaces({ office: "portland" })
.go();
Get employee details and all assigned
const { data, cursor } = await taskManager.collections
.assignments({ employee: "tyler.walch" })
.go();
Find Junior Developers making more than 100,000
Use Entity queries to drill into specific entities.
const title = "Junior Software Engineer";
const salary = "100000";
const developers = await taskManager.entities.employee.query
.roles({ title })
.gt({ salary })
.go();
Find all open tasks for a given project less than or equal to 13 points
const status = "open";
const project = "135-53";
const tasks = await taskManager.entities.task.query
.statuses({ status, project })
.where(({ points }, { lte }) => lte(points, 13))
.go();
Find marketing team members who were hired in between two and five years ago:
const team = "marketing";
const twoYearsAgo = moment.utc().subtract(2, "years").format("YYYY-MM-DD");
const fiveYearsAgo = moment.utc().subtract(5, "years").format("YYYY-MM-DD");
const recentHires = await taskManager.entities.employee.query
.teams({ team })
.between({ dateHired: fiveYearsAgo }, { dateHired: twoYearsAgo })
.go();
Create New Task
The type CreateEntityItem<typeof YOUR_ENTITY_INSTANCE>
is exported to help you type items as they are used for creation
type CreateTaskItem = CreateEntityItem<typeof task>;
function createNewTask(item: CreateTaskItem) {
return taskManager.entities.task.put(item).go();
}