microsoft / kiota-typescript

TypeScript libraries for Kiota-generated API clients.
https://aka.ms/kiota/docs
MIT License
34 stars 26 forks source link

Option to generate Client Class per endpoint/path #1354

Closed DriLLFreAK100 closed 1 day ago

DriLLFreAK100 commented 1 week ago

Good day and thanks a lot for this amazing project!

Query

I would like to check if there is any configuration that allows us to generate Client Class at individual path level.

Background

The default behavior seems to be generating a single fat ApiClient class at the root of the output directory. This poses a problem for tree-shaking. Supposed the target OpenAPI file contains 100 paths and in our client application, we are only consuming 2 out of the 100, the rest of the unused codes will end up in the bundle unnecessarily.

I understand that this might be something to align with the higher philosophy of Kiota as a whole across languages. However, it might still be something worth considering for size-important applications like a frontend web application.

andrueastman commented 1 week ago

Thanks for raising this @DriLLFreAK100

At the moment, you can configure your generation to only include the paths that you are interested in by using the include path parameter. See the link below for the option. This will generate the client with the models and interfaces needed for calling the paths you are interested in without generating everything included in the reference document.

https://learn.microsoft.com/en-us/openapi/kiota/using#--include-path--i

This can also be combined with the exclude path option.

https://learn.microsoft.com/en-us/openapi/kiota/using#--exclude-path--e

Any chance this is what you are looking for?

DriLLFreAK100 commented 1 week ago

@andrueastman While this can be a workaround, I think it does incur some additional overheads. Suppose we are still generating the entire openapi paths, it will mean

Personally, I felt that those should be part of a code generator's scope. Wdyt?

andrueastman commented 1 week ago

So, if I understand correctly, you'd prefer to still have generation for the entire path collection but then benefit from a smaller client using tree-shaking.

I believe this was looked into for TS but I'd like to @baywet to comment on this one specifically as he will most probably have more context here...

baywet commented 1 week ago

Here is a bit of additional context: the same team working on Kiota is also responsible for the Microsoft Graph client SDKs. We faced challenges with trimming and SDK size while working on the TypeScript SDK, having over 20000 operations. While we spent a great deal of time optimizing the generated code itself, it wasn't enough. The fluent API being a huge tree of all the API surface and the operations, making it really hard for bundlers to understand what was needed and what wasn't.

This is why we resulted to modular augmentation, this way consumers can selectively import the main API path segment (and everything underneath it) they need. How does it work?

  1. the main module only contains models + a handcrafted client (effectively the generated client stripped down from all API path segments)
  2. each module has a handcrafted module augmentation which adds its path segments
  3. additionally each module has its own handcrafted client (this is an alternative to modular augmentation, a little like what you're requesting)

How did we achieve that? There's a script which during the generation kicks in after we've generated "a regular client" and moves the models to the main module, and the fluent API declaration to each root path segment module (admin, users, etc...)

Why is it not a functionality of kiota directly? Dividing the Microsoft Graph API surface in smaller surfaces is a discussion we've had with customers for years now (starting with PowerShell). Effectively there's no way of slicing the API surface that makes a majority of people happy and does not result in some significant level of duplication. (should the email and files stuff be with the user stuff since they all belong to users? etc...)

If we extrapolate to any REST API out there, we'll get in the same arguments, people might want any mix of "all the endpoints I've selected in the same client" to "one client per endpoint I've selected" with in between arbitrary groups. That effectively means we need to support arbitrary groups, with the minimal number of endpoint per group being one, and the maximum being all. Let's assume we provided additional parameter --group "GroupName:GroupGlob1/**;GroupGlob2/**" This already makes for a complex parameter and mostly degrades the user experience. Configuration files would not be better.

Then, if the selection results in a single group, great, this is already implemented. However if it results in multiple groups, this will probably result in levels of duplication, which will increase the application size, making the whole exercise moot to begin with. An example of that would be group1 containing /foo/xyz/bar and group2 /foo/xyz/baz. The request builders for /foo and /xyz need to be duplicated.

What's our recommendation? Generate a single client for all the endpoints you care about and use it as it. If this doesn't fit your scenario, manually handcraft additional clients and/or evaluate modular augmentation (especially if you're distributing multiple packages).

I hope this lengthy post helps clarifying out decisions here. Let us know if you have any additional comments or questions.

DriLLFreAK100 commented 2 days ago

Hi @baywet,

Thanks for the detailed explanation. It’s a bit different from what I would have expected from a generator tool as this would mean that we now gotta craft a whole bunch of scripts and stuffs just to get things truly generated into a desired state. Nevertheless, I got your point that we can't be covering arbitrary use cases here.

It would be fantastic if we could document or blog about this approach, especially for those interested in the tree-shakeability of the generated code. I supposed any large enough projects using kiota will inevitably run into this problem and benefit from this info.

Thanks again for your insights!

baywet commented 1 day ago

I'm going ahead and closing this issue since there's no additional action to be taken here. If you feel like this is a gap in our documentation, please go ahead and create a new issue here. https://github.com/MicrosoftDocs/openapi-docs/