Skip to content

Commit 54e7050

Browse files
committed
Remove direct qs dependency
- As suggested here: #5783 (comment) - See also #6647, #5723, #6374, #3230, #3272, https://github.com/expressjs/express/pulls?q=is%3Apr+qs+is%3Aclosed - This doesn't remove `qs` from `body-parser`
1 parent 64e7373 commit 54e7050

File tree

4 files changed

+22
-22
lines changed

4 files changed

+22
-22
lines changed

History.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
unreleased
2+
==========
3+
4+
* Remove `app.set('query parser', 'extended')` option; users should pass a custom parser function instead
5+
- To replicate old `'extended'` behavior: `app.set('query parser', str => require('qs').parse(str, { allowPrototypes: true }))`
6+
- This allows removing `qs` from the dependencies for users who don't configure it explicitly
7+
18
5.1.0 / 2025-03-31
29
========================
310

lib/utils.js

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ var contentType = require('content-type');
1717
var etag = require('etag');
1818
var mime = require('mime-types')
1919
var proxyaddr = require('proxy-addr');
20-
var qs = require('qs');
2120
var querystring = require('node:querystring');
2221
const { Buffer } = require('node:buffer');
2322

@@ -154,7 +153,7 @@ exports.compileETag = function(val) {
154153
/**
155154
* Compile "query parser" value to function.
156155
*
157-
* @param {String|Function} val
156+
* @param {String|Function|Boolean} val
158157
* @return {Function}
159158
* @api private
160159
*/
@@ -174,8 +173,7 @@ exports.compileQueryParser = function compileQueryParser(val) {
174173
case false:
175174
break;
176175
case 'extended':
177-
fn = parseExtendedQueryString;
178-
break;
176+
throw new TypeError("query parser 'extended' is no longer supported; use: `app.set('query parser', str => qs.parse(str, { allowPrototypes: true }))` to replicate the old behavior");
179177
default:
180178
throw new TypeError('unknown value for query parser function: ' + val);
181179
}
@@ -255,17 +253,3 @@ function createETagGenerator (options) {
255253
return etag(buf, options)
256254
}
257255
}
258-
259-
/**
260-
* Parse an extended query string with qs.
261-
*
262-
* @param {String} str
263-
* @return {Object}
264-
* @private
265-
*/
266-
267-
function parseExtendedQueryString(str) {
268-
return qs.parse(str, {
269-
allowPrototypes: true
270-
});
271-
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
"once": "^1.4.0",
5353
"parseurl": "^1.3.3",
5454
"proxy-addr": "^2.0.7",
55-
"qs": "^6.14.0",
5655
"range-parser": "^1.2.1",
5756
"router": "^2.2.0",
5857
"send": "^1.1.0",
@@ -76,6 +75,7 @@
7675
"morgan": "1.10.1",
7776
"nyc": "^17.1.0",
7877
"pbkdf2-password": "1.2.1",
78+
"qs": "^6.14.0",
7979
"supertest": "^6.3.0",
8080
"vhost": "~3.0.2"
8181
},

test/req.query.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
var assert = require('node:assert')
4+
var qs = require('qs');
45
var express = require('../')
56
, request = require('supertest');
67

@@ -22,24 +23,32 @@ describe('req', function(){
2223
.expect(200, '{"user[name]":"tj"}', done);
2324
});
2425

25-
describe('when "query parser" is extended', function () {
26+
describe('when "query parser" is set to qs (formerly "extended")', function () {
2627
it('should parse complex keys', function (done) {
27-
var app = createApp('extended');
28+
var app = createApp(qs.parse);
2829

2930
request(app)
3031
.get('/?foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!')
3132
.expect(200, '{"foo":[{"bar":"baz","fizz":"buzz"},"done!"]}', done);
3233
});
3334

3435
it('should parse parameters with dots', function (done) {
35-
var app = createApp('extended');
36+
var app = createApp(qs.parse);
3637

3738
request(app)
3839
.get('/?user.name=tj')
3940
.expect(200, '{"user.name":"tj"}', done);
4041
});
4142
});
4243

44+
describe('when "query parser" is set to extended', function () {
45+
it('should throw with a helpful error message', function () {
46+
assert.throws(() => createApp('extended'),
47+
new TypeError("query parser 'extended' is no longer supported; use: `app.set('query parser', str => qs.parse(str, { allowPrototypes: true }))` to replicate the old behavior")
48+
);
49+
});
50+
});
51+
4352
describe('when "query parser" is simple', function () {
4453
it('should not parse complex keys', function (done) {
4554
var app = createApp('simple');

0 commit comments

Comments
 (0)