tywalch/electrodb

Feature Request: Helper Type to Extract Entity Types from Schema object

bestickley opened this issue · 9 comments

Given the following schema I'd like to have a helper types that's exported from electrodb that allows me to extract types so that I can use them in my frontend:

import { Putable, Getable, Schema, Updateable } from "electrodb";

const userSchema: Schema<string, string, string> =  {
    model: {
      entity: "user",
      version: "1",
      service: "main",
    },
    attributes: {
      userId: {
        type: "string",
        readOnly: true,
        required: true,
      },
      email: {
        type: "string",
        required: true,
      },
      firstName: {
        type: "string",
        required: true,
      },
      lastName: {
        type: "string",
        required: true,
      },
      createdDate: {
        type: "string",
        default: () => new Date().toISOString(),
        readOnly: true,
      },
      updatedDate: {
        type: "string",
        readOnly: true,
        set: () => new Date().toISOString(),
        watch: "*",
      },
    },
    indexes: {
      primary: {
        pk: {
          field: "pk",
          composite: [],
        },
        sk: {
          field: "sk",
          composite: [],
        },
      },
    },
  };

export type User = Getable<typeof userSchema>;
export type CreateUser = Putable<typeof userSchema>;
export type UpdateUser = Updateable<typeof userSchema>;

Requested types: Getable, Putable, Updateable.

Inspiration from kysely:

import type { ColumnType, Insertable, Selectable, Updateable } from "kysely";
import type { ManagementRoleName } from "./management-role.js";
import type { ProductRoleName } from "./product-role.js";
import type { NullableColumn } from "./helper-types.js";

export interface UserTable {
  accessExpirationDate: string;
  adOid: string; // PK UUID
  altEmail: NullableColumn<string>;
  createdDate: ColumnType<string, never, never>;
  department: string;
  edipi: string;
  email: string;
  firstName: string;
  iaCompletionDate: string;
  jobTitle: string;
  lastName: string;
  middleInitial: NullableColumn<string>;
  organization: string;
  phone: string;
  updatedDate: ColumnType<string, never, never>;
}

export type User = Selectable<UserTable>;
export type CreateUser = Insertable<UserTable>;
export type UpdateUser = Updateable<UserTable>;

Earlier issue: #165

Hi @bestickley, 2.4.0 introduces createSchema which will allow you to create (and validate) a schema outside of Entity creation

@tywalch, that's great news! Do you have any examples similar to my initial request above so that I can see how it works? I checked the docs here but couldn't find anything.

Awesome, thanks @tywalch.

Is it possible to have something like below? My key question is: is it possible to get the interface of a create, update, etc entity from the schema? It seems like you have to have the entity defined first.

/* Model queries, see results, share with friends */

import { Entity, createSchema, CreateEntityItem } from "electrodb";

const table = "your_table_name";

const schema = createSchema({
  model: {
    entity: "task",
    version: "1",
    service: "taskapp"
  },
  attributes: {
    team: {
      type: "string",
      required: true
    },
    task: {
      type: "string",
      required: true
    },
    project: {
      type: "string",
      required: true
    },
    user: {
      type: "string",
      required: true
    },
    title: {
      type: "string",
      required: true,
    },
    description: {
      type: "string"
    },
    status: {
      // use an array to type an enum
      type: ["open", "in-progress", "on-hold", "closed"] as const,
      default: "open"
    },
  },
  indexes: {
    projects: {
      pk: {
        field: "pk",
        composite: ["team"]
      },
      sk: {
        field: "sk",
        // create composite keys for partial sort key queries
        composite: ["project", "task"]
      }
    }
  }
});

// this errors out because it wants an entity. how can I create a `CreateEntityType` from schema?
type CreateEntityType = CreateEntityItem<typeof schema>;

const task = new Entity(
  schema,
  { table }
);

task.query.projects({team: 'abc'}).go();

The docs say:

The createSchema function allows you to define a schema independent of instantiating a new entity. This can be useful if the model definition needs to be portable or dynamic. This function will also validate your schema, and will throw if it is not a valid format.

I would think a portable model definition is only useful if you can get the interface or type of the entity from it. When I say entity, I don't mean electrodb's entity, but an entity like:

inteface User {
  id: string;
  firstName: string;
  lastName: string;
}

I cannot create the Entity in my common package where I define my types because I don't know the table name there (only at run time). If I try I end up getting: https://electrodb.dev/en/reference/errors/#missing-table

You can also add table to your execution options in .go()

const results = await task.query
    .projects({team: 'abc'})
    .go({table: 'your_table_name'});

I didn't know entity's have .setTableName method! This is a working example of what I was looking for. Thank you!