fastify / help

Need help with Fastify? File an Issue here.
https://www.fastify.io/
65 stars 8 forks source link

How to use ajv8 and ajv-errors in fastify 4 project within lerna monorepo? #715

Open radiorz opened 2 years ago

radiorz commented 2 years ago

You have already researched for similar issues?

It's not uncommon that somebody already opened an issue or in best case it's already fixed but not merged. That's the reason why you should search at first before submitting a new one.

What are you trying to achieve, or the steps to reproduce?

i try to update fastify 3 to fastify 4. and my old ajv settings is this:

/**
 * 功能: ajv 设置后才能使用errorMessage自定义错误信息
 * TODO:
 * -
 * DONE:
 * -
 */
const localize = require('ajv-i18n') // ajv 国际化
const errorProperties = require('../constants/errorProperties.json') // 错误提示信息 用于schema自动生成错误
module.exports = {
  ajv: {
    customOptions: { removeAdditional: true, allErrors: true, jsonPointers: true },
    plugins: [require('ajv-errors')]
  },
  schemaErrorFormatter: (errors, dataVar) => {
    // params 转中文
    errors.map((error) => {
      if (errorProperties[error.params.missingProperty]) {
        error.params.missingProperty = errorProperties[error.params.missingProperty]
      }
      if (error.dataPath) {
        // 删除斜杠
        let value = error.dataPath.substr(1)
        error.dataPath = errorProperties[value]
      }
      return error
    })
    localize.zh(errors)
    const myErrorMessage = errors
      .map((err) => {
        return err.dataPath ? `${err.dataPath.trim()} ${err.message.trim()}` : err.message.trim()
      })
      .join(', ')
    return new Error(myErrorMessage)
  }
}

how to make ajv-errors and ajv8 works in new version?

What was the result you received?

What did you expect?

Context

Please read this entire template before posting any issue. If you ignore these instructions and post an issue here that does not follow the instructions, your issue might be closed, locked, and assigned the missing discussion label.

radiorz commented 2 years ago

image

climba03003 commented 2 years ago

I would check the schema of route first.

radiorz commented 2 years ago

@climba03003 thanks for your advice. when i add errorMessage to schema it will return error. like this:

loginSchema: {
    description: '用户名和密码登录',
    tags,
    summary: '登录用户',
    body: {
      type: 'object',
      additionalProperties: false,
      properties: {
        parameters: {
          type: 'string',
          errorMessage:{
             type: 'paramters should be string'
           }
        },
      },
      required: ['parameters'],
      errorMessage: {
        required: {
          parameters: 'parameters is needed',
        },
      },
    },

  },
climba03003 commented 2 years ago

Can you log which exact version of node, fastify and ajv are you using? Even better is provide the code that I can run directly. The schema is good when testing with ajv only.

The error seems to me there are unsupported syntax by node or some where calling JSON.parse.

radiorz commented 2 years ago

@climba03003 environment:

node v16.13.1
windows 11
fastify ^4.2.0
ajv ^8.11.0
ajv-errors ^3.0.0

my example is this:

const app = require('fastify')({
  logger: true,
  ajv: {
    customOptions: {
      // 可能会遭受 dos 攻击,不过 ajv-errors 需要
      allErrors: true,
      // 删除多余字段
      removeAdditional: true,
    },
    plugins: [require('ajv-errors')],
  },
})

app.post(
  '/',
  {
    schema: {
      body: {
        type: 'object',
        additionalProperties: false,
        properties: {
          parameters: {
            type: 'string',
            errorMessage: '123',
          },
          // username: $properties.username.schema,
          // password: $properties.password
        },
        required: ['parameters'],
      },
    },
  },
  (request, reply) => {
    return request.body
  },
)
app.listen({ port: 3000 })

error is :

Error compiling schema, function code: const schema16 = scope.schema[10];return function validate14(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;if(data && typeof data == "object" && !Array.isArray(data)){if(data.parameters === undefined){const err0 = {instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: "parameters"},message:"must have required property '"+"parameters"+"'"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}for(const key0 in data){if(!(key0 === "parameters")){delete data[key0];}}if(data.parameters !== undefined){let data0 = data.parameters;if(typeof data0 !== "string"){let dataType0 = typeof data0;let coerced0 = undefined;if(dataType0 == 'object' && Array.isArray(data0) && data0.length == 1){data0 = data0[0];dataType0 = typeof data0;if(typeof data0 === "string"){coerced0 = data0;}}if(!(coerced0 !== undefined)){if(dataType0 == "number" || dataType0 == "boolean"){coerced0 = "" + data0;}else if(data0 === null){coerced0 = "";}else {const err1 = {instancePath:instancePath+"/parameters",schemaPath:"#/properties/parameters/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}}if(coerced0 !== undefined){data0 = coerced0;if(data !== undefined){data["parameters"] = coerced0;}}}if(errors > 0){for(const err2 of vErrors){if((((({"str":"err2"}.keyword !== "errorMessage") && (!{"str":"err2"}.emUsed)) && (({"str":"err2"}.instancePath === instancePath+{"_items":["\"/parameters\""]}) || (({"str":"err2"}.instancePath.indexOf(instancePath+{"_items":["\"/parameters\""]}) === 0) && ({"str":"err2"}.instancePath[instancePath+{"_items":["\"/parameters\""]}.length] === "/")))) && ({"str":"err2"}.schemaPath.indexOf("#/properties/parameters") === 0)) && ({"str":"err2"}.schemaPath["#/properties/parameters".length] === "/")){{"str":"emErrs0"}.push({"str":"err2"});{"str":"err2"}.emUsed = true;}}if({"str":"emErrs0"}.length){if(vErrors === null){vErrors = [{"str":"err3"}];}else {vErrors.push({"str":"err3"});}errors++;}const emErrs1 = [];for(const err4 of vErrors){if(!{"str":"err4"}.emUsed){{"str":"emErrs1"}.push({"str":"err4"});}}vErrors = emErrs1;errors = {"str":"emErrs1"}.length;}}}else {const err5 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}validate14.errors = vErrors;return errors === 0;}

node_modules\fastify\lib\route.js:317
              throw new FST_ERR_SCH_VALIDATION_BUILD(opts.method, url, error.message)
                    ^
FastifyError [Error]: Failed building the validation schema for POST: /, due to error Unexpected token ':'
    at Boot.<anonymous> (\node_modules\fastify\lib\route.js:317:21)
    at Object.onceWrapper (node:events:509:28)
    at Boot.emit (node:events:402:35)
    at node_modules\avvio\boot.js:160:12
    at \node_modules\avvio\plugin.js:275:7
    at done (node_modules\avvio\plugin.js:200:5)
    at check (node_modules\avvio\plugin.js:224:9)
    at node:internal/process/task_queues:141:7
    at AsyncResource.runInAsyncScope (node:async_hooks:199:9)
    at AsyncResource.runMicrotask (node:internal/process/task_queues:138:8) {
  code: 'FST_ERR_SCH_VALIDATION_BUILD',
  statusCode: 500
}
climba03003 commented 2 years ago

I can not reproduce the error with the exact same version of node, fastify, ajv and ajv-errors.

Have you try to reinstall all the dependencies?

radiorz commented 2 years ago

@climba03003 I found that the error only occurs in the lerna monorepo. When I use lerna to install dependencies, sometimes ajv ajv-errors will appear in packages/app, then there will be errors. Works fine without ajv and ajv-errors in node_modules in packages/app. Please check out my example repo: https://github.com/radiorz/fastify-lerna-ajv-error-demo

climba03003 commented 2 years ago

Sorry, I have no knowledge on lerna.

rolandkawka commented 1 year ago

@radiorz Have you figured out how to fix this error?

radiorz commented 1 year ago

@rolandkawka I just cd into the backend folder and then use npm to install the dependencies, which will stay in node_modules in the backend folder, so it works fine. If you use lerna and yarn to install, it will install ajv under the project root directory, and the result will be wrong. I didn't find the reason for the error, but this is a workaround.