Skip to content
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

Add support for options.qs #160

Closed
wants to merge 2 commits into from
Closed

Conversation

jamesplease
Copy link
Collaborator

Just a quick proof-of-concept implementation of #159.

README.md Outdated
### `options.qs`

A value to be transformed into a query string. This library does not provide
a parser for `options.qs` out of the box: you must define it yourself as
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a parser. I would call it a query string builder

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.

index.js Outdated
var qsStringifyDefined = isFunction(createXHR.queryStringStringify);

if (options.qs && !qsStringifyDefined) {
throw new Error("You passed a 'qs' option, but did not define an 'xhr.queryStringStringify' function.\nYou must either omit the 'qs' option, or define 'xhr.queryStringStringify'.")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll shorten this message if you don't mind.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem. Go for it.

@naugtur
Copy link
Owner

naugtur commented Apr 7, 2017

Looks good.

@reqshark @gsf Any comments? I'd merge it into v3 as is.

Copy link
Contributor

@reqshark reqshark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my only nit is about term queryStringStringify. When we're talking about uri encoding it doesn't serve developers to use the same terms as JSON operations. Regardless of what popular libs do, uri encoding looks very different from JSON

README.md Outdated

A value to be transformed into a query string. This library does not provide
a parser for `options.qs` out of the box: you must define it yourself as
`xhr.queryStringStringify`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we call this xhr.qs ?

README.md Outdated
a parser for `options.qs` out of the box: you must define it yourself as
`xhr.queryStringStringify`.

If `options.qs` is defined, and `xhr.queryStringStringify` is not, then an
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xhr.qs please

README.md Outdated
assumptions about how to handle them, and does not support the `qs` option out
of the box.

To support the `qs` option, you must define an `xhr.queryStringStringify`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another xhr.qs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls also remove you must, it reads better as:

"To support a qs option, define an xhr... function"

README.md Outdated
and returns a string that is appended to the URL.

You do not need to include a leading "?" in the value that you return from
`xhr.queryStringStringify`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment

README.md Outdated
var xhr = require('xhr')
var qs = require('qs')

xhr.queryStringStringify = qs.stringify
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xhr.qs = qs.stringify

index.js Outdated
@@ -7,6 +7,7 @@ var xtend = require("xtend")
module.exports = createXHR
createXHR.XMLHttpRequest = window.XMLHttpRequest || noop
createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window.XDomainRequest
createXHR.queryStringStringify = null // Define this as a function to support the `qs` option
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createXHR.qs = null

index.js Outdated
throw new Error("You passed a 'qs' option, but did not define an 'xhr.queryStringStringify' function.\nYou must either omit the 'qs' option, or define 'xhr.queryStringStringify'.")
}

var qs = options.qs && qsStringifyDefined ? '?' + createXHR.queryStringStringify(options.qs) : '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment

@@ -60,6 +60,7 @@ type XhrOptions = String | {
withCredentials: Boolean?,
responseType: String?,
beforeSend: Function?
qs: Any?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep consistent w/ readme and avoid confusion of a non-type, i.e. qs: Function? here

To support the qs option, you must define an xhr.queryStringStringify function. This function accepts the value of options.qs as its first argument, and returns a string that is appended to the URL.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree here, the type of qs is unknown until you specify the xhr.queryStringStringify or xhr.qs function.
You can specify a function accepting an array or an object or a specific structure or a Map...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave it as-is for now. Y'all can hash this out in the v3 branch post-merge?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@naugtur @jmeas yea you guys are right about that, have to agree, leave it as Any?

README.md Outdated
assumptions about how to handle them, and does not support the `qs` option out
of the box.

To support the `qs` option, you must define an `xhr.queryStringStringify`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls also remove you must, it reads better as:

"To support a qs option, define an xhr... function"

@jamesplease
Copy link
Collaborator Author

jamesplease commented Apr 7, 2017

@reqshark , i'm surprised you so strongly associate the word "stringify" with JSON. We don't have to use the term, but I think it is the best decision. I don't these other libs have made a mistake using it. Anything can be "stringified" – it's basically just a synonym for serializing, right?

Either way, I think using "qs" as both the option you pass when building an xhr, and as the builder/stringifier, could potentially cause confusion. Users have to deal with learning that "the xhr.qs function accepts options.qs and returns it as string." Not as straightforward as it could be imo.

How would you feel about any of these alternatives:

  • buildQs
  • qsBuild
  • serializeQs
  • qsSerialize

Another verb you used was "encode," but this function is not encoding anything imo, so i don't think we should consider that option.

Thanks for the review, you two! Feel free to push directly to this branch; otherwise, I'll update based on your comment tomorrow.

@reqshark
Copy link
Contributor

reqshark commented Apr 7, 2017

Anything can be "stringified" – it's basically just a synonym for serializing, right?

I think using "qs" as both the option you pass when building an xhr, and as the builder/stringifier, could potentially cause confusion

ok fair points @jmeas, what about qsFn or just xhr.querystring?

@reqshark
Copy link
Contributor

reqshark commented Apr 7, 2017

Another verb you used was "encode," but this function is not encoding anything imo, so i don't think we should consider that option.

the qs lib's stringify method encodes by default, and accepts encode options for encoding.

when forming XHR requests browser's encodeURIComponent() is helpful per RFC3986

the fact that you don't think "this function is encoding anything" gets precisely to my point about JSON terminology confusing people

@naugtur
Copy link
Owner

naugtur commented Apr 7, 2017

I'd support qsSerialize as my first choice.

naming things is hard. :)

@jamesplease
Copy link
Collaborator Author

I'll name it qsSerialize for now. Feel free to make changes in the v3 branch.

Would you like me to add tests in addition to these other changes you requested? Also, I made all of the suggested changes in the fixup PR (I think). Let me know if I missed anything.

I'll squash the commits when you give this PR your final 👍

var xhr = require('xhr')
var qs = require('qs')

xhr.qsSerialize = qs.stringify
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ this line is very convincing towards naming it qsStringify after all.
I'll keep thinking about it after I merge this PR into v3 branch

var qsStringifyDefined = isFunction(createXHR.qsSerialize);

if (options.qs && !qsStringifyDefined) {
throw new Error("To use the 'qs' option, first define an 'xhr.qsSerialize' function.")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, I don't have to redact it anymore - exactly what I wanted to do.

@naugtur
Copy link
Owner

naugtur commented Apr 7, 2017

I'm merging this manually to v3development.

@jamesplease
Copy link
Collaborator Author

Closing as this is merged into v3development: 807745f

@jamesplease jamesplease closed this Apr 7, 2017
@jamesplease jamesplease mentioned this pull request Apr 7, 2017
@gsf
Copy link
Collaborator

gsf commented Apr 8, 2017

Sorry I'm late to the party. To align with Node, qs, query-string, and request, the function reference should be qsStringify. That's the nomenclature. And aside from some creative edge cases, options.qs should be an Object, as it is in request.

Looking at the bigger picture, do we want to keep extending the API surface on the createXhr object? That moves us further away from a guiding factor in the simplicity of request: all configuration goes in that options object.

In a comment on #159, @jmeas makes a point that it's "a hard sell to use this lib for universal code, as the Node code would absolutely use the qs option." It's worth considering how committed we are to the "subset of request" goal. We could stay fully within the request API by pulling in qs as a dependency, but that obviously adds bloat that many may not need.

The qsStringify function could instead be set on options, similar to qsStringifyOptions in request. To avoid including it in every xhr call, we could add the rather cool request.defaults method. And if we add the undocumented requester argument to request.defaults, that solves the multiple signatures issue, making it just a few lines of code for anyone to add their own qs option:

var req = xhr.defaults(function (options, callback) {
  options.uri = (options.uri || options.url) + '?' + qs.stringify(options.qs);
  return xhr(options, callback);
});

@naugtur
Copy link
Owner

naugtur commented Apr 8, 2017

Valid points. Would you mind starting a new issue with this and tagging v3 milestone?
I think this also aligns with the goal of the most useful subset of request we can deliver on a tiny package.

@jamesplease
Copy link
Collaborator Author

jamesplease commented Apr 8, 2017

@gsf, all of that sounds 👌 to me. Does the defaults option, as you've described it, require that all future requests be built from the req object?

@gsf
Copy link
Collaborator

gsf commented Apr 8, 2017

Issue #163 created. And yes, in that example, the req function would be used for all future calls, just like a full wrapper.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants