Skip to content

Dynamic procedures #4

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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