You might be thinking, a month of work for a single changelog item?!
Yes, indeed! But not only was Request Chaining the most-requested feature on the roadmap, it required an entire new concept (template functions) to be introduced, as well as a large restructuring of how plugins communicate with the application.
Let's first dig into Request Chaining, then talk about some of the changes that had to happen to get there.
When working with APIs, there are often dependencies between requests. For example, you
might
POST /users
to create a new user, then PATCH /users/<id>
to update the user's email
address.
But how do you get the ID of the created user into the update? You'd better not be copy/pasting! There's now a better way.
Just like how environment variables allow dynamic reuse of values across requests, the new
response(…)
function allows something like PATCH /users/<id>
to directly reference the
id
from the response body of POST /users
. Here's how.
Step 1 → Select the response()
function from the autocomplete menu in any text
field.
Step 2 → Click the blue template tag to configure the function arguments.
Step 3 → Select the request you want to depend on.
Step 4 → Enter either JSONPath or XPath query to select just the value you want.
Step 5 → Send the request!
Sending the request will render the template and inject the referenced value -- just like using regular variables. If no response exists yet for that request, it will be sent automatically.
So why did this take a month to ship? Let's cover that now.
Fixes Reference Response Values in other requests (chaining)
Template functions could have been implemented in a more direct way. However, I really wanted them to be extendable by the community. This meant having the necessary plugin APIs to provide dynamic template functions.
Here's a very simplified version of how the response()
function plugin is implemented
(or view the
full source on GitHub).
// Psuedo-code for the response() template function plugin
export const plugin = {
templateFunctions: [{
name: 'response',
args: [
{type: 'http_request', name: 'request'},
{type: 'text', name: 'path'},
],
async onRender(ctx, args) {
const requestId = args.values.request;
const responses = await ctx.httpResponse.find({ requestId });
return jsonPath.apply(responses[0].body, args.values.path);
},
}],
}
The big lift in this release was implementing the APIs for the ctx
object (that you see
above) to allow plugins to do useful things like show dialogs, copy text, fetch responses,
and send requests. It's this plugin→app communication that needed to be implemented for
this release.
I'm saving the full details for a dedicated blog post, but the gist is that there's now a bidirectional RPC API between the app (Rust) and plugin runtime (NodeJS). A functional interface is then implemented on top of this using strongly typed Request/Response events.
For example, the ctx.httpResponse.find({ requestId })
function emits a
FindHttpRequestRequest{event_id: 123, ...}
event to the app, then waits for a FindHttpRequestResponse{reply_id: 123, ...}
to come
back.
All event types are defined in Rust and TypeScript equivalents are generated using
ts-rs
, meaning plugin development is as easy as
Ctrl+Spacing your way to success!
That's enough details for today, though. Look forward to a future blog post outlining the full plugin stack once the architecture has fully settled.
$
messing up syntax highlightingsqlx
to rusqlite