Powership / Modules
Powership
All-in-one full-stack package for managing complex web applications.
Powership is the full-stack package of choice for creating, managing, and scaling complex web applications with support
for single-table design.
Using Powership you can quickly create types that can be easily extended, transformed into GraphQL, TypeScript, and used in both frontend and backend applications.
Table of Contents
- Typescript Infer
- Extending Types
- Static typescript Infer
- Printing Types as Typescript
- Validation
- Entity & CRUD
- GraphQL
Creating and extending types
import { createGraphQLSchema, createResolver, createType, createEntity, Infer } from 'powership';
const AddressType = createType('Address', {
object: {
street: 'string',
number: {
union: ['string', 'int?'],
},
},
});
const UserType = createType(
{
object: {
name: 'string',
email: 'email?',
age: 'int?',
notes: '[int]?',
unionField: {
union: ['string?', '[int]?'],
},
letter: {
enum: ['a', 'b', 'c'],
},
letterOptionalList: {
enum: ['x', 'y', 'z'],
optional: true,
list: true,
},
addresses: {
type: AddressType,
list: true,
},
deliveryAddress: {
object: {
street: 'string',
number: 'int?',
},
},
},
} as const
);
Extending types
const StoreType = UserType.clone((it) =>
it.exclude(['addresses']).extendObjectDefinition({ storeId: 'ID', ownerId: 'string' }).graphType('Store')
);
Static typescript infer
type TStoreType = Infer<typeof StoreType>;
Printing types as Typescript
const storeTS = await StoreType.typescriptPrint();
expect(storeTS.split('\n')).toEqual([
'export interface Store {',
' name: string;',
' email?: Email;',
' age?: number;',
' notes?: number[];',
' unionField?: string | number[];',
' letter: "a" | "b" | "c";',
' letterOptionalList?: ("x" | "y" | "z")[];',
' deliveryAddress: {',
' street: string;',
' number?: number;',
' };',
' storeId: ID;',
' ownerId: string;',
'}',
'',
]);
Validation
try {
const validStoreData = StoreType.parse({});
console.log(validStoreData);
} catch (e) {
}
Entity and CRUD
const StoreEntity = createEntity({
name: 'Store',
type: StoreType,
indexes: [
{
name: 'id1',
PK: ['.storeId'],
},
{
name: 'id2',
PK: ['.ownerId', '.storeId'],
},
],
});
const findStoreResolver = createResolver({
name: 'findStore',
type: StoreEntity.edgeType,
args: {
storeId: 'string',
},
}).resolver(async (_, { storeId }, requestContext) => {
const filter = {
storeId,
};
return StoreEntity.findOne({ filter, context: requestContext });
});
GraphQL
const graphqlSchema = createGraphQLSchema([findStoreResolver]);
const schemaTXT = graphqlSchema.utils.print();
expect(schemaTXT.split('\n')).toEqual([
'type Query {',
' findStore(storeId: String!): Store_Edge!',
'}',
'',
'type Store_Edge {',
' cursor: String!',
' node: StoreEntity!',
'}',
'',
'type StoreEntity {',
' createdAt: Date!',
' createdBy: String',
' id: String!',
' ulid: Ulid!',
' updatedAt: Date!',
' updatedBy: String',
'',
' """',
' The full string value of the first index following the RegExp format "^store⋮id1⋮.*"',
' """',
' _id: String!',
'',
' """',
' The id1PK field in the RegExp format "^store⋮id1⋮.*"',
' """',
' id1PK: String!',
'',
' """',
' The id2PK field in the RegExp format "^store⋮id2⋮.*"',
' """',
' id2PK: String!',
' name: String!',
' email: String',
' age: Int',
' notes: [Int]',
' unionField: StoreEntity_unionField',
' letter: StoreEntity_letter!',
' letterOptionalList: [StoreEntity_letterOptionalList]',
' deliveryAddress: StoreEntity_deliveryAddress!',
' storeId: ID!',
' ownerId: String!',
'}',
'',
'scalar Date',
'',
'scalar Ulid',
'',
'"""',
'Union of { optional:true, type: string } | { list:true, optional:true, type: int }',
'"""',
'scalar StoreEntity_unionField',
'',
'enum StoreEntity_letter {',
' a',
' b',
' c',
'}',
'',
'enum StoreEntity_letterOptionalList {',
' x',
' y',
' z',
'}',
'',
'type StoreEntity_deliveryAddress {',
' street: String!',
' number: Int',
'}',
]);