New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suggestion: Directly exporting service actions #213
Comments
@simonbuchan Each service exports a service client, as well as For example, using a Kinesis node.js service client, you could write code like this today: import {PutRecordCommand} from '@aws-sdk/client-kinesis-node/commands/PutRecordCommand';
import {KinesisClient} from '@aws-sdk/client-kinesis-node/KinesisClient';
export async function handler() {
const client = new KinesisClient({
region: 'us-west-2'
});
try {
const command = new PutRecordCommand({
StreamName: 'simple-example',
PartitionKey: `partition-${Date.now()}`,
Data: JSON.stringify({
payload: 'sample!'
})
});
const result = await client.send(command);
console.log(`Put data to shard ${result.ShardId} with sequence number ${result.SequenceNumber}`);
} catch (err) {
console.error(err);
throw err;
}
} This results in a substantially smaller bundle than if you just imported the I would need to think what this means as far as mocking goes, but you could update the I'm sure the team would love to hear your feedback on the above, and any suggestions on how to improve on it, or where it's doing well! |
Ah, so that's what the I think the above design is pretty nice, but having the easy / default path pull in everything is not so great. As an example, import { Observable } from "rxjs"; // pulls in everything without specific steps
Observable.fromEvent(target, "event").switchMap(...); // can directly use nearly everything from Observable and after a few iterations were suggesting a very similar approach as above for the users that wanted a better bundle size: import { fromEvent } from "rjxs/observables/fromEvent";
import { switchMap } from "rxjs/operators/switchMap";
fromEvent(target, "event").pipe(switchMap(...)); // weird syntax is to simulate |> proposal, for chaining. but this, while it works, has some bad developer ergonomics - it's far to easy for your IDE to pull in They finally settled on just exporting the free functions as the only public way to use them after webpack added support for // using namespace import to illustrate that sideEffects removes the need for lots of imports
import * as rx from "rxjs"; // all types, factories, etc...
import * as ops from "rxjs/operators"; // operators in a separate package
rx.fromEvent(target, "event").pipe(ops.switchMap(...)); The current design also has the fact that TL;DR: this is a great option, but I think you should at least consider making it the only one, or at least the default one, for ecosystem reasons. |
@simonbuchan Just to summarize then, would you expect to see something like this then to be the default scenario? // only exports the base client without service-specific methods attached
import {Client} from '@aws-sdk/client-kinesis-node';
import {PutRecordCommand} from '@aws-sdk/client-kinesis-node/commands'; Then if someone wanted the V2-equivalent client, they would import that from a different sub-module? I haven't had a chance to play with the sideEffects feature, but I'm also wondering if that would result in smaller bundle sizes with the SDK in its current state if you import the specific commands and the FooClient from the root package ( |
Strictly you don't need to have Looks like rollup doesn't support To fix this, you have to build twice: with typescript From my understanding the most popular option to implement this is to pick a specific set of importable paths to support, commonly just the package root, in This won't work with This is digressing even further, but if you're messing with package contents for size concerns, you might also want to consider building less downleveled code: it can have a noticable impact on code size, performance and ease of debugging (particularly with native You can simply do this by default ( Sorry for the dump, the ecosystem has gotten pretty complex around this area! |
Thanks for all the info! Yes, there are definitely some changes that can be made with regards to what version of EcmaScript the SDK gets transpiled down to, and also supporting multiple import strategies. Looking forward to the day ES2015 imports are supported by default in all runtime environments! |
Greetings! We’re closing this issue because it has been open a long time and hasn’t been updated in a while and may not be getting the attention it deserves. We encourage you to check if this is still an issue in the latest release and if you find that this is still a problem, please feel free to comment or open a new issue. |
Reopening as needs-discussion label is added |
The title is a bit outdated given you do export actions in a different way that I was expecting, and the decision on #364 implies that nothing's going to be changing around this. Not sure exactly what @AllanFly120 added the label for, but it seems it's about the bundling/downleveling situation, which I suspect is quite different now. I'm happy with this being closed, assuming there's nothing I missed? |
Closing as discussion has been addressed |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread. |
At the moment the packages export a similar structure to v2, a service class with methods for each action. In theory, you can do better at tree-shaking and possibly make test mocking easier with a direct function export for each action taking a configuration or context as a parameter, e.g., instead of:
doing something like:
I'm not sure exactly how much impact this could have, but it looks like it could let an optimiser remove all of the unused command/* and model/* classes. Since the usages I've seen in our company have been very commonly a small handful of actions spread over multiple services, it seems like this could be a pretty good win.
Obviously, this means testing can't just pass a mock client object to the tested methods, but it's pretty standard usage to init the client in the module of the unit to be tested (like above), which means your test mocking would already need to handle mocking the client creation and the method already, so this would be about the same.
Using a fake mocking library for illustration, this would require a particularly nosy test to change from:
to:
That is, it allows decoupling mocking the client from mocking the action at the cost of now requiring module mocking for users that were passing the client as an arg before. In my experience, this would be a good trade-off, but it would be good to get more feedback.
A compromise would be to document the effect of the action function on the client (currently,
send()
?), so the test could only mock the client and assert the expected calls, but that seems like it could be restrictive?This might also allow creating and sharing the middleware directly between multiple services, if they could be compatible: e.g. multiple JSON-based services in the same region, enforced by the typing system, but I'm not sure if that's reasonable?
The text was updated successfully, but these errors were encountered: