Best practices in naming and defining procedures when they don't fit into standard buckets?

Ddiz5/2/2023
I am loving TRPC and its type safety, but I feel like I'm struggling with the naming of procedures. I'm pretty confused with why I need to fit my procedures into one of the 3 buckets: query, mutation, or subscription. For example, I have items. They can be queried, mutated and subscribed to. And maybe a few more things - items can be generated, created, pulled from a remote source, deleted, renamed, aliased, etc. But let's try to fit this into these prescribed buckets.

I felt inclined to define items as a publicProcedure with all 3 verbs. But I have to choose only one, not all three. The api does not support chaining on multiple verbs. If I want to support all 3, I have to differentiate them as separate procedure definitions such as getItems, updateItems, and subscribeToItems... but then, what's the point of query/mutate/subscribe if I am already differentiating them as separate procedure names? Now the client api surface is weird:

I wanted the client code to read: client.items.query(), or client.items.mutate(...)
I'm fine with it if I can just have client.getItems() or client.updateItems(...)
Instead I have redundancy in the naming: client.getItems.query(), client.updateItems.mutate(...)

To make things more confusing, some procedures just don't fit into these buckets. I can have procedures which read nothing, write nothing, but perform some action like open a file in vscode. None of the above conventions work in this case.

My expectation when using it was simply to have an execute call, and let the author decide their naming conventions. If there is nothing special about a query or mutation, why introduce weird things that mess with the naming conventions and don't work in every case?
Nnlucas5/3/2023
Name procedures like you would name the function that does a thing
Nnlucas5/3/2023
So client.users.giveAccountAccess.mutate() or client.users.list or .getList
Nnlucas5/3/2023
It’s very much a case of naming is hard (in general) and you should do what feels best at the time
Ddiz5/3/2023
right, but my question is, why the need to have "mutate()" or "query()" at the end of the function?
Ddiz5/3/2023
(or rather, is there a way to avoid using a category, so that my client is just client.giveAccountAccess() which is nice and predictable?)
Nnlucas5/3/2023
This is a tRPC thing for sure
Nnlucas5/3/2023
Not really a way to avoid it
Ddiz5/3/2023
I can have a wrapper that abstracts the client returned from createTRPCProxyClient which removes the extra category, that's all i could come up with
Nnlucas5/3/2023
Under the hood it controls things like the HTTP verb to use, so it does matter
Ddiz5/3/2023
that's a good point. So I'm assuming query=get, mutation=post
Nnlucas5/3/2023
Essentially yes
Nnlucas5/3/2023
It can change how you frame your semantics too
Nnlucas5/3/2023
I often write my procedures in terms of resources, so prefer .list.query rather than .getList.query
Nnlucas5/3/2023
But it’s preference
Ddiz5/3/2023
in that case: what if your list can be queried and mutated and subscribed?
Ddiz5/3/2023
is there a way to provide all 3 for a single resource?
Ddiz5/3/2023
(i'm guessing that in that case "list" is a procedure)
Ddiz5/3/2023
so that we could have client.list.query(...), client.list.mutate(...)
Nnlucas5/3/2023
I actually don’t think that would be impossible to build
Nnlucas5/3/2023
Feel free to open a GitHub issue on it, but it’s not a current feature no
Ddiz5/3/2023
Sure thing. Thanks for the help Nick!
SSandvich5/3/2023
Sorry to hijack this thread but this looks like a cool idea.
I scaffolded out how that might look to create many procedures and this just looks really nice
const test = router({
  greeting: {
    query: publicProcedure
      .input(z.string())
      .query(({ input }) => {
        return `Hello ${input}!`;
    }),
    mutation: publicProcedure
      .input(z.object())
      .mutation(() => {}),
    subscription: publicProcedure.subscription(() => {
      // ...
    }),
  }
});

You could argue there's some duplication in key name and method name but I think it's fine
Nnlucas5/3/2023
Go ahead and create a GitHub issue! Can’t promise we’ll tackle it soon or at all, but PRs are especially welcome if they prove the concept
Ddiz5/3/2023
i'll post one