serviejs / popsicle

Simple HTTP requests for node and the browser
MIT License
246 stars 19 forks source link

Not clear what the upgrade path is from 10.x to 11.x #126

Closed rgrove closed 4 years ago

rgrove commented 5 years ago

Howdy! We've been using Popsicle for lots of stuff in Cake for a few years now and it's been great. But today I sat down to upgrade from 10.0.1 to 11.0.0 and found myself completely stumped.

I was prepared for a major version upgrade to require some work, but I can't seem to figure out what work is actually required. As far as I can tell, all the pre-11.x interfaces we relied on no longer exist, and it's not clear to me what their 11.x equivalents are, or if there even are equivalents. I'm not really sure where to start.

For instance, in one case, we currently import Popsicle like this:

import Popsicle from 'popsicle';

Then we use it like this:

let res = await Popsicle({
  // ... request options like `body`, `headers`, `method`, `timeout`, and `url` ...
}).use([
  // ... various middleware ...
]);

if (res.statusType() !== 2) {
  throw new Error( ... );
}

The current Popsicle readme doesn't offer much information about what the popsicle module exports beyond transport and request, neither of which have familiar interfaces. I looked at Servie's readme hoping to find more information, but it's not clear to me what relationship Servie's API has to Popsicle's, what parts of it I can or should use via Popsicle, or even how middleware works in Servie.

I get the impression that this new approach is potentially more modular and more powerful, but I'm not sure how to learn how to use it, how it's meant to be used, or how to teach the other developers on my team what's changed. Can anyone help me get started?

blakeembrey commented 5 years ago

@rgrove Sorry for the delayed response! Let me try to help out and get those docs up-to-date with what we learn together!

So https://github.com/serviejs/servie is an approach at making global HTTP interfaces that work across front-end, back-end, etc in JavaScript. It's looks a lot like Request (aka fetch) in browsers, with some small exception I hope to iron out over time. You can expect some updates on this layer, but not too many - hopefully one more aligning with browsers fetch and then it's done. The API now has been stable for around a year and used for building server-side frameworks. Why is this useful? It's now a single interface for building server-side and client-side applications.

Now, the popsicle@11.x is a pretty large change because it moved to these interfaces. In terms of changes, that's actually the only major internal one (minus HTTP/2 support and connection pooling in node.js). Of course, this meant a lot of changes to the API. Why? Mostly just to make things a bit more type safe and functional. The old use can be done in the same way as before with https://github.com/serviejs/throwback. E.g. compose([plugin, plugin, plugin]). The request function builds a servie Request class that you can give to the middleware or the transport layer at the end of the middleware (this is how popsicle worked internally before). So, refactoring your example above would be something like:

import { send } from 'popsicle'

const use = compose([
  // various middleware...
])

const req = request('', { /* request options */ })
const res = await use(req, send)

if (!res.ok) { // https://github.com/serviejs/servie#properties-2
  throw new Error(...) // This could actually be a middleware like https://github.com/blakeembrey/popsicle-status
}

I'll be working on moving over my older plugins and hope to improve the documentation based on your feedback!

blakeembrey commented 5 years ago

In summary, I learned a bunch over the past three years about the best middleware approach with popsicle - it turned out the most flexible is with promises and Request => Promise<Response> signature. I realised this exact same signature could be used server-side with no changes needed - it's actually very liberating writing a node.js server as Request => Promise<Response>. I worked a bit on unifying this model through public interfaces and have not done a good enough writing about my learnings yet. I'll try to do this over before the New Year, I think it's an extremely interesting topic (having written popsicle, servie-http and servie-lambda with this interface now).

rgrove commented 5 years ago

@blakeembrey Thanks! This is helpful. I definitely look forward to reading more.

From an outside perspective, servie and popsicle 11.x seem really cool but also fairly abstract. I think a higher level overview of your thinking combined with a few more examples demonstrating real-world use cases would be super helpful in clarifying how things fit together and how the new interfaces map to what users of older versions of popsicle (or users of other tools) are familiar with.

blakeembrey commented 5 years ago

Just an FYI/update, I definitely took this feedback to heart but unfortunately only just got around to the changes I wanted to make here. The new version is similar to 11.x under the hood in terms of middleware, but I've made fetch the default export now. The primary export is now just a simple compose() of middleware: https://github.com/serviejs/popsicle/blob/master/src/node.ts. Does you think this makes things easier or clearer to use? I'm hoping it's a bit of the best of both worlds here, and it's largely based on the browser fetch interface so should be more familiar.

rgrove commented 5 years ago

@blakeembrey I love it! So much clearer. Elapsed time from glancing at the readme to understanding exactly how to get started is about 3 seconds. 😄

Really appreciate you taking this feedback to heart!