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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions