-
-
Notifications
You must be signed in to change notification settings - Fork 198
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
Error: .swagger() must be called after .ready() #758
Comments
The problem with Whats your actual bug? Could you add a reproduce? |
@mcollina I'm having the same issue with 8.10.1 the ready function takes a callback that will be called after all plugins are loaded. 8.10.0 handled this registration method correctly, it's only after the patch update that this error occurs. see: https://fastify.dev/docs/latest/Reference/Server/#ready |
downgrading to 8.10.0 resolves the issue for us |
Can you provide steps to reproduce? We often need a reproducible example, e.g. some code that allows someone else to recreate your problem by just copying and pasting it. If it involves more than a couple of different file, create a new repository on GitHub and add a link to that. |
@osbornm the following works correctly: 'use strict'
const fastify = require('fastify')()
fastify.register(require('../index'), {
openapi: {
info: {
title: 'Test swagger',
description: 'testing the fastify swagger api',
version: '0.1.0'
},
servers: [{
url: 'http://localhost'
}],
components: {
securitySchemes: {
apiKey: {
type: 'apiKey',
name: 'apiKey',
in: 'header'
}
}
}
},
hideUntagged: true,
exposeRoute: true
})
fastify.register(async function (fastify) {
fastify.put('/some-route/:id', {
schema: {
description: 'post some data',
tags: ['user', 'code'],
summary: 'qwerty',
security: [{ apiKey: [] }],
params: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'user id'
}
}
},
body: {
type: 'object',
properties: {
hello: { type: 'string' },
obj: {
type: 'object',
properties: {
some: { type: 'string' }
}
}
}
},
response: {
201: {
description: 'Succesful response',
type: 'object',
properties: {
hello: { type: 'string' }
}
},
default: {
description: 'Default response',
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
}, (req, reply) => { reply.send({ hello: `Hello ${req.body.hello}` }) })
fastify.post('/some-route/:id', {
schema: {
description: 'post some data',
summary: 'qwerty',
security: [{ apiKey: [] }],
params: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'user id'
}
}
},
body: {
type: 'object',
properties: {
hello: { type: 'string' },
obj: {
type: 'object',
properties: {
some: { type: 'string' }
}
}
}
},
response: {
201: {
description: 'Succesful response',
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}, (req, reply) => { reply.send({ hello: `Hello ${req.body.hello}` }) })
})
fastify.ready(err => {
if (err) throw err
console.log(fastify.swagger())
}) |
Error persists when fastify-swagger is loaded by @fastify/autoload:
Stack trace: [App] [14:02:08.165] ERROR (298847): .swagger() must be called after .ready() |
Can you provide steps to reproduce? We often need a reproducible example, e.g. some code that allows someone else to recreate your problem by just copying and pasting it. If it involves more than a couple of different file, create a new repository on GitHub and add a link to that. Note that if it throws that error, it means that your swagger document is incomplete or invalid. There is already a bug in your code, you just don't know about it. |
@mcollina I believe in order to reproduce the issue you would need to register the swagger plugin from within an encapsulated context (perhaps using the wrong terminology here). Your example would become: 'use strict'
const fastify = require('fastify')()
fastify.register(async function (fastify) {
fastify.register(require('../index'), {
openapi: {
info: {
title: 'Test swagger',
description: 'testing the fastify swagger api',
version: '0.1.0'
},
servers: [{
url: 'http://localhost'
}],
components: {
securitySchemes: {
apiKey: {
type: 'apiKey',
name: 'apiKey',
in: 'header'
}
}
}
},
hideUntagged: true,
exposeRoute: true
})
fastify.put('/some-route/:id', {
schema: {
description: 'post some data',
tags: ['user', 'code'],
summary: 'qwerty',
security: [{ apiKey: [] }],
params: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'user id'
}
}
},
body: {
type: 'object',
properties: {
hello: { type: 'string' },
obj: {
type: 'object',
properties: {
some: { type: 'string' }
}
}
}
},
response: {
201: {
description: 'Succesful response',
type: 'object',
properties: {
hello: { type: 'string' }
}
},
default: {
description: 'Default response',
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
}, (req, reply) => { reply.send({ hello: `Hello ${req.body.hello}` }) })
fastify.post('/some-route/:id', {
schema: {
description: 'post some data',
summary: 'qwerty',
security: [{ apiKey: [] }],
params: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'user id'
}
}
},
body: {
type: 'object',
properties: {
hello: { type: 'string' },
obj: {
type: 'object',
properties: {
some: { type: 'string' }
}
}
}
},
response: {
201: {
description: 'Succesful response',
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}, (req, reply) => { reply.send({ hello: `Hello ${req.body.hello}` }) })
fastify.ready(err => {
if (err) throw err
console.log(fastify.swagger())
})
}) |
Unfortunately your code is hitting the race condition that would generate invalid schemas. Here is an updated version fixed: 'use strict'
const fastify = require('fastify')()
fastify.register(async function (fastify) {
// await is necessary here to make sure its onReady hook is registered
// before the one of this module
await fastify.register(require('./index'), {
openapi: {
info: {
title: 'Test swagger',
description: 'testing the fastify swagger api',
version: '0.1.0'
},
servers: [{
url: 'http://localhost'
}],
components: {
securitySchemes: {
apiKey: {
type: 'apiKey',
name: 'apiKey',
in: 'header'
}
}
}
},
hideUntagged: true,
exposeRoute: true
})
fastify.put('/some-route/:id', {
schema: {
description: 'post some data',
tags: ['user', 'code'],
summary: 'qwerty',
security: [{ apiKey: [] }],
params: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'user id'
}
}
},
body: {
type: 'object',
properties: {
hello: { type: 'string' },
obj: {
type: 'object',
properties: {
some: { type: 'string' }
}
}
}
},
response: {
201: {
description: 'Succesful response',
type: 'object',
properties: {
hello: { type: 'string' }
}
},
default: {
description: 'Default response',
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
}, (req, reply) => { reply.send({ hello: `Hello ${req.body.hello}` }) })
fastify.post('/some-route/:id', {
schema: {
description: 'post some data',
summary: 'qwerty',
security: [{ apiKey: [] }],
params: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'user id'
}
}
},
body: {
type: 'object',
properties: {
hello: { type: 'string' },
obj: {
type: 'object',
properties: {
some: { type: 'string' }
}
}
}
},
response: {
201: {
description: 'Succesful response',
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}, (req, reply) => { reply.send({ hello: `Hello ${req.body.hello}` }) })
// Use onReady hook instead of ready event to make sure the hook from
// @fastify/swagger is triggered before this one
fastify.addHook('onReady', () => {
console.log(fastify.swagger())
})
})
// Load all the plugins
fastify.ready() This does:
|
I think we should update the README. |
Hi @mcollina , Thanks for the clarification. I'm not sure I got it completely though. Here is an example, based on what you've showcased here, but I put import Fastify from "fastify";
import fastifySwagger from "@fastify/swagger";
import fastifySwaggerUI from "@fastify/swagger-ui";
const fastify = Fastify()
fastify.post('/some-route/:id', {
schema: {
description: 'post some data',
tags: ['user', 'code'],
summary: 'qwerty',
security: [{ apiKey: [] }],
params: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'user id'
}
}
},
body: {
type: 'object',
properties: {
hello: { type: 'string' },
obj: {
type: 'object',
properties: {
some: { type: 'string' }
}
}
}
},
response: {
201: {
description: 'Succesful response',
type: 'object',
properties: {
hello: { type: 'string' }
}
},
default: {
description: 'Default response',
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
}, (req, reply) => { reply.send({ hello: `Hello ${req.body.hello}` }) })
fastify.register(async function (fastify) {
// await is necessary here to make sure its onReady hook is registered
// before the one of this module
await fastify.register(fastifySwagger, {
openapi: {
info: {
title: 'Test swagger',
description: 'testing the fastify swagger api',
version: '0.1.0'
},
servers: [{
url: 'http://localhost'
}],
components: {
securitySchemes: {
apiKey: {
type: 'apiKey',
name: 'apiKey',
in: 'header'
}
}
}
},
hideUntagged: true,
exposeRoute: true
})
fastify.put('/some-route/:id', {
schema: {
description: 'post some data',
tags: ['user', 'code'],
summary: 'qwerty',
security: [{ apiKey: [] }],
params: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'user id'
}
}
},
body: {
type: 'object',
properties: {
hello: { type: 'string' },
obj: {
type: 'object',
properties: {
some: { type: 'string' }
}
}
}
},
response: {
201: {
description: 'Succesful response',
type: 'object',
properties: {
hello: { type: 'string' }
}
},
default: {
description: 'Default response',
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
}, (req, reply) => { reply.send({ hello: `Hello ${req.body.hello}` }) })
await fastify.register(fastifySwaggerUI, {
routePrefix: '/documentation',
uiConfig: {
docExpansion: 'full',
deepLinking: false
},
uiHooks: {
onRequest: function (request, reply, next) { next() },
preHandler: function (request, reply, next) { next() }
},
staticCSP: true,
transformStaticCSP: (header) => header,
transformSpecification: (swaggerObject, request, reply) => { return swaggerObject },
transformSpecificationClone: true
})
// Use onReady hook instead of ready event to make sure the hook from
// @fastify/swagger is triggered before this one
fastify.addHook('onReady', () => {
console.log(fastify.swagger())
})
})
// Load all the plugins
fastify.ready()
// Load all the plugins
await fastify.listen({ port: parseInt(process.env.PORT) || 3030 }); When I run Other words, how can I properly register Thanks |
This is expected. Moving it outside of the plugin would make it impossible for swagger to listen to the events. It's what enable us to have multiple instance of swagger in the same app. |
@PavelPolyakov hi, you need to use Here is how I've setup my application (I use @scalar/fastify-api-reference for the UI):
import Fastify, { FastifyInstance } from "fastify";
import { privateContext } from "./modules/private";
import { publicContext } from "./modules/public";
import apiDocs from "./plugins/fastify-swagger/swagger";
export async function buildApp(): Promise<any> {
const app: FastifyInstance = Fastify({
trustProxy: true,
});
await app.register(apiDocs)
app.register(publicContext)
app.register(privateContext)
app.ready()
return app
}
import fp from 'fastify-plugin';
async function apiDocs(fastify: any, options: any) {
await fastify.register(require('@fastify/swagger'), {
swagger: {
info: {
title: 'Test swagger',
description: 'Swagger API',
version: '0.1.0'
},
externalDocs: {
url: 'https://swagger.io',
description: 'Find more info here'
},
host: 'localhost',
schemes: ['http'],
consumes: ['application/json'],
produces: ['application/json'],
tags: [
{ name: 'user', description: 'User related end-points' },
{ name: 'code', description: 'Code related end-points' }
],
definitions: {
User: {
type: 'object',
required: ['id', 'email'],
properties: {
id: { type: 'string', format: 'uuid' },
firstName: { type: 'string' },
lastName: { type: 'string' },
email: { type: 'string', format: 'email' }
}
}
},
securityDefinitions: {
apiKey: {
type: 'apiKey',
name: 'apiKey',
in: 'header'
}
}
}
})
await fastify.register(require('@scalar/fastify-api-reference'), {
routePrefix: '/reference',
configuration: {
theme: 'default',
},
})
fastify.addHook('onReady', () => {
console.log(fastify.swagger())
})
}
export default fp(apiDocs, { name: 'Swagger' });
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { businessStatistics } from "./statistics";
export async function publicContext(app: FastifyInstance) {
app.register(async function publicContext(app: any) {
app.register(businessStatistics)
})
} |
Prerequisites
Fastify version
4.23.2
Plugin version
8.10.1
Node.js version
18.17.1
Operating system
macOS
Operating system version (i.e. 20.04, 11.3, 10)
13.5.2
Description
Upon updating from 8.10.0 to 8.10.1, I'm now receiving this error when trying to start my Fastify server:
Steps to Reproduce
Register the fastify swagger plugin in it's own encapsulation context (not sure if this is the exact right term, basically just it's own function with other routes), and use the following code to register it:
Expected Behavior
Fastify swagger is registered after fastify is ready.
The text was updated successfully, but these errors were encountered: