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();
}