trpc / trpc/trpc

feat: overwrite input in middleware

Open
#6,892 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
40.6k
Forks
1.7k
Avg merge
1d 22h
Merged PRs (30d)
22

Description

Describe the feature you'd like to request

The middlewares can currently extend/overwrite the context. They can technically modify the input with return next({ input: newInput }) in the same manner, but that's not reflected in types in the downstream procedures.

I would like the input type change to be reflected in types, the same way as it happens with the context.

Consider the example:

const orgProcedure = procedure
  .input(v.object({
    orgId: v.pipe(v.string(), v.uuid()),
    orgAccessKey: v.string(), // Imaginary data
  }))
  .use(async ({ input: { orgId, orgAccessKey, ...input }, next }) => {
    const org = await db.org.findOptional(orgId).select("id", "name")
      .where({ orgAccessKey }) // Imaginary access check
    assertHttp404NotFound(org, "Org not found")
    return next({
      ctx: { org }, // Add org object to context
      input, // Remove orgId and orgAccessKey from input
    })
  })

const editAccount = orgProcedure
  .input(v.object({
    accountId: v.pipe(v.string(), v.uuid()),
    name: v.string(),
    balance: v.number(),
  }))
  .mutation(async ({ ctx: { org }, input: { accountId, ...input } }) => {
    // Here input is { name, balance } in runtime,
    // but it's typed as { orgId, orgAccessKey, name, balance }.
    //
    // The query builder errors because of the extra fields in the type.
    await db.account.find(accountId).where({ orgId: org.id }).update(input)
  })

Currently, doing the above is not possible (it works in runtime, but it leads to type errors). As such, currently user can not really alter the input alongside the middleware chain; they must always deal with all input fields.

Describe the solution you'd like to see

In the example above, I would like the editAccount input to properly infer its new shape as coming from the upstream middleware.

Describe alternate solutions

Currently, all procedures must deal with the entire input object. That means two alternatives:

1. Extract parent input as unused vars
const editAccount = orgProcedure
  .input(v.object({
    accountId: v.pipe(v.string(), v.uuid()),
    name: v.string(),
    balance: v.number(),
  }))
  .mutation(async ({ ctx: { org }, input: { orgId, orgAccessKey, accountId, ...input } }) => {
    await db.account.find(accountId).where({ orgId: org.id }).update(input)
  })

Problems:

  • Unused vars are not cool.
  • If I add a new field to the upstream middleware, I will have to update ALL derived procedures that use the whole input.
2. Explicitly extract all needed fields
const editAccount = orgProcedure
  .input(v.object({
    accountId: v.pipe(v.string(), v.uuid()),
    name: v.string(),
    balance: v.number(),
  }))
  .mutation(async ({ ctx: { org }, input: { accountId, name, balance } }) => {
    await db.account.find(accountId).where({ orgId: org.id }).update({ name, balance })
  })

This is more reliable, but then each field must be repeated thrice:

  1. in the validator
  2. in input extractor
  3. in update call

So for 20 fields, that gives 40 extra words.

Additional information

No response

👨‍👧‍👦 Contributing
  • 🙋‍♂️ Yes, I'd be down to file a PR implementing this feature!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No files or tests are named. Start by tracing the TypeScript middleware typing for next({ input: newInput }) and compare it with context extension; use the orgProcedure and editAccount example as the target behavior. Done means downstream procedure input types reflect middleware-overwritten fields without requiring unused or repeated extraction.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.