Skip to content

Dynamic procedures #4

@hashwarp

Description

@hashwarp

I tried to get this working but couldn't get it.

import { z as zod, ZodTypeAny, ZodObject, ZodRawShape } from 'zod';
import {
  initTRPC,
  inferRouterInputs,
  inferRouterOutputs,
  procedure as TRPCProcedure,
  ProcedureBuilder,
} from '@trpc/server';
import { customErrorFormatter, hasRole } from '../../util/rpc';
import { dateFromString } from '../../util/zod';
import type { RouterContext } from '../../types';
import { Query, getQueryInput, inferRouterOutputs } from '../../schema';

// Define available entity names (capitalize first letter as used in dynamic names)
type EntityName = keyof typeof Entities;
type CapitalizedEntityName = Capitalize<EntityName>;
// Define possible service method names based on the entity name
type ServiceMethods<N extends string> = `get${N}` | `get${N}s` | `create${N}` | `update${N}`;

// Define the service type
type CoreService = {
  [K in ServiceMethods<CapitalizedEntityName>]: (input: any, ctx: RouterContext) => any;
};

// Utility type for ensuring the return type matches a ProcedureBuilder
type ProcedureType = ProcedureBuilder<RouterContext, object, object, any, any, any, any, boolean>;

// Define a type for entity procedures using template literal types
type EntityProcedures<N extends CapitalizedEntityName> = {
  [K in ServiceMethods<N>]: ProcedureType;
};

// Utility function to create standard procedures for an entity
function createEntityProcedures<N extends CapitalizedEntityName>(
  entity: ZodObject<ZodRawShape>,
  entityName: N
): EntityProcedures<N> {
  return {
    [`get${entityName}`]: procedure
      .use(hasRole('guest', t))
      .use(customErrorFormatter(t))
      .input(getQueryInput(entity))
      .output(entity)
      .query(({ input, ctx }) => (ctx.app.service.Core[`get${entityName}` as keyof CoreService] as any)(input, ctx)),

    [`get${entityName}s`]: procedure
      .use(hasRole('guest', t))
      .use(customErrorFormatter(t))
      .input(getQueryInput(entity))
      .output(entity.array())
      .query(({ input, ctx }) => (ctx.app.service.Core[`get${entityName}s` as keyof CoreService] as any)(input, ctx)),

    [`create${entityName}`]: procedure
      .use(hasRole('admin', t))
      .use(customErrorFormatter(t))
      .input(getQueryInput(entity))
      .output(entity.pick({ id: true }))
      .mutation(({ input, ctx }) =>
        (ctx.app.service.Core[`create${entityName}` as keyof CoreService] as any)(input, ctx)
      ),

    [`update${entityName}`]: procedure
      .use(hasRole('admin', t))
      .use(customErrorFormatter(t))
      .input(getQueryInput(entity))
      .output(entity.pick({ id: true }))
      .mutation(({ input, ctx }) =>
        (ctx.app.service.Core[`update${entityName}` as keyof CoreService] as any)(input, ctx)
      ),
  } as EntityProcedures<N>;
}

// Dynamically create all procedures using the utility function
const entityProcedures = Object.keys(Entities).reduce((acc, entityName) => {
  const entitySchema = Entities[entityName as keyof typeof Entities];
  if (!entitySchema || !(entitySchema instanceof z.ZodObject)) return acc; // Ensure the schema is a ZodObject
  const capitalizedEntityName = (entityName.charAt(0).toUpperCase() + entityName.slice(1)) as CapitalizedEntityName;
  return { ...acc, ...createEntityProcedures(entitySchema, capitalizedEntityName) };
}, {} as Record<string, ProcedureType>);


export const createRouter = () =>
  router({
    ...entityProcedures
  })

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions