I'm having trouble with response serialization.
My controller might return differing values based on the user's state. The schema that I wrote uses anyOf
and definition of those schemas that the controller might return.
But for some reason 1 of them fails and empty object is returned. For others, correct response value is serialized and returned.
I have created reproduceable repo at this link. To reproduce run npm start
and from a terminal (or browser) use:
curl http://localhost:3000
Above command returns correct data, however below doesn't
http://localhost:3000?id=1
Code from the link:
// Require the framework and instantiate it
const fastify = require("fastify")({ logger: false });
// Declare a route
fastify.route({
method: "POST",
schema: {
response: {
200: {
type: "object",
properties: {
code: { type: "number", default: 1234 },
data: {
anyOf: [
{
type: "object",
properties: {
token: { type: "string" },
user: {
type: "object",
properties: {
id: { type: "number" },
phoneNumber: { type: "string" },
},
},
},
},
{
type: "object",
properties: {
tempToken: { type: "string" },
multipleChoice: {
type: "object",
properties: {
usernames: { type: "array", items: { type: "string" } },
locationAddresses: {
type: "array",
items: { type: "string" },
},
},
},
},
},
{
type: "object",
properties: {
token: { type: "string" },
user: {
type: "object",
properties: {
id: { type: "number" },
username: { type: "string" },
},
},
},
},
],
},
},
},
},
},
url: "/",
handler: async (request, reply) => {
if (request.query.id === "1") {
console.log("matching 1");
return {
code: 1000,
data: {
tempToken: "1234567890",
multipleChoice: {
usernames: ["user1", "user2"],
locationAddresses: ["location1", "location2"],
},
},
};
}
return {
code: 1000,
data: {
token: "1234567890",
user: { id: 1, username: "user1" },
},
};
},
});
// Run the server!
fastify.listen({ port: 3000 }, (err) => {
console.log("server started");
if (err) {
fastify.log.error(err);
process.exit(1);
}
});
Please, help me figure out what is causing the issue.
Fastify uses fast-json-stringify for the serialization of a response's body.
From the docs on anyOf:
The different schemas will be tested in the specified order. The more schemas stringify has to try before finding a match, the slower it will be.
This description tells us that schemas will tried (for match) in the order they are listed in the anyOf
. So, in a sense it will be tested in a manner similar to the way if ... else if ... else if ....
works.
For that reason, you need to make sure that only the right schema (other schemas should not match) is matched against the response returned by fastify server application.
Hence, to fix the problem in question, you must keep these points in mind:
Here is the fixed code (I have added required
arrays for all properties):
data: {
anyOf: [
{
type: "object",
properties: {
token: { type: "string" },
user: {
type: "object",
properties: {
id: { type: "number" },
phoneNumber: { type: "string" },
},
required: ["id", "phoneNumber"], // Use Required
},
},
required: ["token", "user"], // Use Required
},
{
type: "object",
properties: {
tempToken: { type: "string" },
multipleChoice: {
type: "object",
properties: {
usernames: { type: "array", items: { type: "string" } },
locationAddresses: {
type: "array",
items: { type: "string" },
},
},
required: ["usernames", "locationAddresses"], // Use Required
},
},
required: ["tempToken", "multipleChoice"], // Use Required
},
{
type: "object",
properties: {
token: { type: "string" },
user: {
type: "object",
properties: {
id: { type: "number" },
username: { type: "string" },
},
required: ["id", "username"], // Use Required
},
},
required: ["token", "user"], // Use Required
},
],
},