Part of umbrella issue #16.
Cannot be started until #107 is done.
This should hopefully be a straightforward (and even quick) update.
Problem
One of the biggest wins we can get for the Coder plugin is making it so that using the plugin doesn't feel so walled-off. We still intend to ship polished UI experiences for our main components, but users will inevitably have use cases that we can't reasonably account for in the UI. So, in that case, why not give them access to the full Coder API, and give them the power to wire things up themselves?
Requirements
A method defined on the CoderClient class that lets users call any Coder API endpoint that they want, but that still has some restrictions and niceties
The method will, by necessity, have to return the any type, but beyond that, it should be as type-safe as possible
It should only make the user pass in what is absolutely necessary. We should do as much behind the scenes as possible
The user shouldn't have to worry about handling auth logic themselves
If preflight checks ever become necessary, the user shouldn't need to worry about those either
All the Coder types from NPM should be easy to plug into the method as type parameters
Method should be defined in a way to ensure that it can't ever lose its this context when passed around as a value in the React UI
It should be easy to use this method as a building block for a useCoderApiFunction hook
That is, this method should be designed in a way that it provides the core logic for the hook out of the box, just without any types. Then it will be the hook's responsibility to provide those types, and turn the method into something type-safe
Possible solution
Let's say that we have a general-purpose ReadonlyJsonValue type that represents any valid JSON-serializable value:
type ReadonlyJsonValue =
| string
| number
| boolean
| null
| readonly ReadonlyJsonValue[]
| Readonly<{ [key: string]: ReadonlyJsonValue }>;
In that case, we can define a method like this:
type ArbitraryApiInput <
TBody extends ReadonlyJsonValue = ReadonlyJsonValue,
> = Readonly<{
// Very niche syntax, but this means that 'method' will have type 'string',
// but the other predefined methods will still show up in autocomplete
method: "GET" | "POST" | "PUT" | "DELETE" | (string & {});
// Ensures that each endpoint at least starts with a '/'
endpoint: `/${string}`;
// Genericized body type parameter
body: TBody;
// Lets user override default request init (within reason); should not
// let the user accidentally override things like Coder auth token
init?: Partial<RequestInit>;
}>;
type CoderClient = {
// <-- Other existing properties/methods go here -->
arbitraryApiCall<
TReturn = any
TBody extends ReadonlyJsonValue = ReadonlyJsonValue,
> = (input: ArbitraryApiInput<TBody>) => Promise<TReturn>;
}
Code example
// Unsafe version
function CustomComponent () {
// Custom hook will be made as part of issue #107
const client = useCoderClient();
const onSubmit = async (event) => {
// Pretend that data is retrieved via the FormData API
const newWorkspace = await client.arbitraryApiCall({
method: "POST",
endpoint: `/organizations/${organizationId}/members/${memberId}/workspaces`,
body: {
name: newWorkspaceName,
// Other properties go here
},
});
// Do something new newly-created workspace here; workspace
// is of type any
};
return (
<form onSubmit={onSubmit}>
// Form elements go here
</form>
);
}
// "Safer" version is virtually identical, but this time, the user has used type
// parameters
const newWorkspace = await client.arbitraryApiCall<Workspace>({
// Same runtime arguments as before
});
// At this point, newWorkspace is of type Workspace, which may or may not be
// accurate. It's up to the user to wire things up correctly
Part of umbrella issue #16. Cannot be started until #107 is done.
This should hopefully be a straightforward (and even quick) update.
Problem
One of the biggest wins we can get for the Coder plugin is making it so that using the plugin doesn't feel so walled-off. We still intend to ship polished UI experiences for our main components, but users will inevitably have use cases that we can't reasonably account for in the UI. So, in that case, why not give them access to the full Coder API, and give them the power to wire things up themselves?
Requirements
CoderClient
class that lets users call any Coder API endpoint that they want, but that still has some restrictions and nicetiesany
type, but beyond that, it should be as type-safe as possiblethis
context when passed around as a value in the React UIuseCoderApiFunction
hookPossible solution
Let's say that we have a general-purpose
ReadonlyJsonValue
type that represents any valid JSON-serializable value:In that case, we can define a method like this:
Code example