jmanuelrosa
jmanuelrosa

Reputation: 336

Use ajv-formats with fastify

Playing with fastify and the schema object on routes, I'm trying to add more validations. fastify use (for body validations) ajv. Following fastify documentation and code, to add a ajv plugin I need to do this:

const fastify = require('fastify')({
  ajv: {
    plugins: [
      require('ajv-merge-patch')
    ]
  }
})

The documentation is here

The problem is that when we try to add this plugin, I receive this error: Cannot read property 'code' of undefined" and this is the stack error:

TypeError: Cannot read property 'code' of undefined
  at addFormats (/test-api/node_modules/ajv-formats/dist/index.js:30:26)
  at Array.formatsPlugin (/test-api/node_modules/ajv-formats/dist/index.js:15:5)
  at ValidatorCompiler (/test-api/node_modules/fastify/lib/schema-compilers.js:42:16)
  at buildCompilerFromPool (/test-api/node_modules/fastify/lib/schema-compilers.js:20:22)
  at Boot.<anonymous> (/test-api/node_modules/fastify/lib/route.js:269:39)
  at Object.onceWrapper (events.js:421:28)
  at Boot.emit (events.js:327:22)
  at /test-api/node_modules/avvio/boot.js:153:12
  at /test-api/node_modules/avvio/plugin.js:269:7
  at done (/test-api/node_modules/avvio/plugin.js:201:5)"

I get this error with this piece of code:

const server: FastifyInstance<Server, IncomingMessage, ServerResponse > = fastify({
  ignoreTrailingSlash: true,
  logger: true,
  ajv: {
    plugins: [
      require('ajv-formats')
    ]
  }
})

async function run (): Promise<string> {
  server.addHook('onRoute', (options) => console.log(options))

  await server.register(helmet)
  await server.register(cors)

  return server.listen(3000, 'localhost')
}

Has anyone been able to use ajv-formats with fastify or could you help me, please?

Thanks in advance!

Upvotes: 3

Views: 3400

Answers (4)

Blind Kai
Blind Kai

Reputation: 524

Had the same issue so I decided to use Ajv version 8.x.x and use other plugins within.

import Ajv from "ajv";
import ajvFormats from "ajv-formats";
import ajvErrors from "ajv-errors";

const ajv = new Ajv({
  removeAdditional: true,
  useDefaults: true,
  coerceTypes: true,
  allErrors: true
});

ajvFormats(ajv);
ajvErrors(ajv, { singleError: true });

export { ajv };

After that, I've just used this Ajv instance for schema compilation in the Fastify server as suggested in the the docs.

fastify.setValidatorCompiler((opt) => ajv.compile(opt.schema));

It works without any problems. The same result is achievable with regular JavaScript.

Upvotes: 2

CaptainRuhrpott
CaptainRuhrpott

Reputation: 70

I had the same issue and decided to look into the cause. The problem is that ajv-formats depends on ajv 8.x.x while fastify depends on ajv 6.x.x. These two are incompatible.

There are multiple solutions

  • Wait for fastify to support ajv 8.x.x. This is a work in progress
  • Copy out the required validators, bundle them up in a custom ajv-Plugin and add that to fastify. A plugin is a function that takes the ajv instance as first parameter1. There you can add the formats, take a look at ajv-formats to see how it is done there.
  • Use an older version of ajv-formats. Sadly the last version of ajv-formats to support ajv 6.x.x is 0.2.0 (take a look at package.json). Not a good option IMO

1Plugins can also take options but there are no Typescript typings to add such plugins to fastify.

Upvotes: 1

jmanuelrosa
jmanuelrosa

Reputation: 336

Solved! Seems to be that my problems come from another part of my code. Creating a new project form 0 and using format seems to works well and I don't need to install ajv-format, because fastify has the option format to use in your schemas!

Thanks to all!

Upvotes: 0

Manuel Spigolon
Manuel Spigolon

Reputation: 12900

Here a working example you may customize:

    "ajv-merge-patch": "^4.1.0",
    "fastify": "^3.9.2",
    "fastify-cors": "^5.1.0",
    "fastify-helmet": "^5.1.0",
const fastify = require('fastify')({
  ignoreTrailingSlash: true,
  logger: true,
  ajv: {
    plugins: [
      require('ajv-merge-patch')
    ]
  }
})

fastify.register(require('fastify-cors'))
fastify.register(require('fastify-helmet'))

fastify.addSchema({
  $id: 'mySchema.json#',
  type: 'object',
  properties: {
    foo: { type: 'string' },
    bar: { $ref: '#' }
  },
  additionalProperties: false
})

fastify.post('/', {
  schema: {
    body: {
      $id: 'mySchemaExtended.json#',
      $merge: {
        source: { $ref: 'mySchema.json#' },
        with: {
          properties: {
            baz: { type: 'number' }
          }
        }
      }
    }
  }
}, (req, reply) => { reply.send({ echo: req.body }) })

fastify.inject({
  method: 'POST',
  url: '/',
  payload: {
    foo: 'foo',
    bar: {
      foo: 'foo2',
      evit: 'this'
    },
    baz: 42,
    evict: 'this too'
  }
}, (err, res) => {
  console.log(res.json());
  // shows
  // { echo: { foo: 'foo', bar: { foo: 'foo2' }, baz: 42 } }
})

Upvotes: 0

Related Questions