koajs / koa

Expressive middleware for node.js using ES2017 async functions

Home Page:https://koajs.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Issue with reassigning ctx.query and type preservation

wjonesusna2012 opened this issue · comments

Describe the bug

Node.js version:
16.15.1
OS version:
Windows 11
Description:

Per the Koa docs, you can reassign to the ctx.query object. Whenever I do so, any typing that I have, is not preserved in future middleware or future calls to ctx.query (i.e. a value of [ 2 ] in the object just becomes '2' or the number 5 becomes the string '5'), although the actual assignment does seem to occur as indicated below by the fact that id becomes 4 after assignment, indicating something has changed.

Actual behavior

INITIAL QUERY OBJECT [Object: null prototype] { id: '2', arr: '5' }
QUERY FROM JOI VALIDATION [Object: null prototype] { id: 2, arr: [ 5 ] }
number
CTX.QUERY AFTER ASSIGNMENT [Object: null prototype] { id: '4', arr: '5' }
QUERY IN 2ND MIDDLEWARE [Object: null prototype] { id: '4', arr: '5' }
string

Expected behavior

This is what I would expect from code example below.

INITIAL QUERY OBJECT [Object: null prototype] { id: '2', arr: '5' }
QUERY FROM JOI VALIDATION [Object: null prototype] { id: 2, arr: [ 5 ] }
number
CTX.QUERY AFTER ASSIGNMENT [Object: null prototype] { id: 4, arr: [ 5 ] }
QUERY IN 2ND MIDDLEWARE [Object: null prototype] { id: 4, arr: [ 5 ] }
number

Code to reproduce

I've included a stack overflow entry and code sandbox that replicates this issue in addition to my project on my Windows 11 computer.

https://stackoverflow.com/questions/75368044/unable-to-reassign-ctx-query-in-a-koa-middleware

const Koa = require("koa");
const app = new Koa();
const Joi = require("joi");
const PORT = 3000;
const validSchema = Joi.object({
  name: Joi.string().optional(),
  id: Joi.number().required().min(0),
  arr: Joi.array().items(Joi.number()).optional().single(),
});
// #1
app.use(async (ctx, next) => {
  console.log("INITIAL QUERY OBJECT", ctx.query);
  const { error, value } = validSchema.validate(ctx.query);
  if (!!error) ctx.throw(400, error);
  console.log("QUERY FROM JOI VALIDATION", value);
  value.id += 2;
  console.log(typeof value.id);
  ctx.query = value;
  console.log("CTX.QUERY AFTER ASSIGNMENT", ctx.query);
  await next();
});
// #2
app.use(async (ctx, next) => {
  console.log("QUERY IN 2ND MIDDLEWARE", ctx.query);
  console.log(typeof ctx.query.id);
  await next();
});

app.use(async (ctx, next) => {
  ctx.body = `test koa`;
});

app.listen(PORT);
console.log(`http://localhost:${PORT}`);
commented

ctx.query uses querystring to parse query parameters internally.
when you set ctx.query = value,It is equivalent to ctx.query = querystring.stringify(value);
when you get ctx.query, It will return querystring.parse(value)

https://github.com/koajs/koa/blob/master/lib/request.js in 172 line

This isn't a bug but a side effect of how delegation works in Context.

The return value from accessing ctx.query is actually coming from context delegating access to ctx.request.query. As @senfish wrote the result you're seeing is the evaluated queryparser result. I would recommend you instead took the result from Joi and assigned it to something under e.g. ctx.state.validatedQuery.

I'm closing this ticket as this is not a bug. If you still have questions you can re-open this or a new ticket.