Closed pharindoko closed 2 years ago
@pharindoko Hi, just to confirm, are you adding the Type.Box()
to fastify's AJV implementation (usually via .addSchema()
) ? The error seems to suggest that AJV can't resolve the referenced schema.
Failed building the validation schema ... can't resolve reference User#/definitions/UserObject from id
You could try give the Type.Box()
an $id
. This can help AJV locate the correct schema.
export const User = Type.Box({
UserObject: Type.Object({
name: Type.String(),
mail: Type.Optional(Type.String({ format: "email" })),
foo: Type.Optional(Type.String(T)),
}),
}, { $id: 'Entities' }) // Required
...
fastify.addSchema(Box) // Required for target to be referenced.
const UserRef = Type.Ref(User, "UserObject")
export type UserType = Static<typeof UserRef>
As for adding a mechanism to override the default /definitions
placement for Type.Box(...)
. I'd need to get more insight into that. Generally TypeBox tries to align with JSON schema conventions over Open API (in fact /definitions
is planned to be renamed to /$defs
in future releases), so providing mechanisms to work outside standard conventions is something I'm a little bit reluctant to do at this stage.
Would you be able to provide the expected Type.Box({...})
schema required Open API 3.0? I'm happy to take a look, noting that both Type.Box()
and Type.Ref()
are flagged as experimental features of TypeBox, so this may provide insights into how they can be improved.
S
Thanks for your reply @sinclairzx81
still not working - to give you an update ....
this fails at fastify.addSchema() call in index.ts
I get following error:
user: {"definitions":{"UserObject":{"$id":"UserObject","type":"object","properties":{"name":{"type":"string"},"mail":{"format":"email","type":"string"},"foo":{"enum":["A","B","C"],"type":"string"}},"required":["name"]}}}
...
FastifyError [Error]: Failed building the validation schema for POST: /, due to error schema is invalid: data.properties['kind'] should be object,boolean
index.ts
import fastify from "fastify";
import * as user from "./modules/user/user.controller";
import fastifySwagger from "fastify-swagger";
import helmet from "fastify-helmet";
import fastifySensible from "fastify-sensible";
import { User } from "./modules/user/user.schema";
const server = fastify({
logger: true,
});
console.log("user: " + JSON.stringify(User));
User.$id = "UserObject";
server.addSchema(User.definitions.UserObject);
user.schema.ts
import { Static, TLiteral, TUnion, Type } from "@sinclair/typebox";
type IntoStringUnion<T> = {
[K in keyof T]: T[K] extends string ? TLiteral<T[K]> : never;
};
function StringUnion<T extends string[]>(
values: [...T]
): TUnion<IntoStringUnion<T>> {
return { enum: values } as any;
}
const T = StringUnion(["A", "B", "C"]);
type T = Static<typeof T>;
export const User = Type.Box({
UserObject: Type.Object(
{
name: Type.String(),
mail: Type.Optional(Type.String({ format: "email" })),
foo: Type.Optional(Type.String(T)),
},
{ $id: "UserObject" }
),
});
const UserRef = Type.Ref(User, "UserObject");
export type UserType = Static<typeof UserRef>;
hmm this is what happens in ajv in background ...
schema is invalid: data.properties['kind'] should be object,boolean
@pharindoko Hiya. Can you replace
server.addSchema(User.definitions.UserObject) // This isn't correct
// with
server.addSchema(User) // You should pass the Box directly to AJV
Additionally, the box should take an $id
also which is used to namespace
the schemas within.
export const User = Type.Box({
UserObject: Type.Object(
{
name: Type.String(),
mail: Type.Optional(Type.String({ format: "email" })),
foo: Type.Optional(Type.String(T)),
},
{ $id: "UserObject" }
),
}, { $id: 'User' }); // add here
This is required for Type.Ref(User, "UserObject")
to target the correct schema when referencing into a Box.
Also, is there a need for the UserObject
to exist with a Box? Box's are really intended to namespace a set of domain related schemas (similar to xmlns
namespacing). If you only need a single type, you can use.
const UserObject = Type.Object({
name: Type.String(),
mail: Type.Optional(Type.String({ format: "email" })),
foo: Type.Optional(Type.String(T)),
}, { $id: "UserObject" })
server.addSchema(UserObject)
const R = Type.Ref(UserObject) // const R = { $ref: 'UserObject' }
As for the kind
errors, be mindful that AJV 7+ runs in strict mode which will disallow the TypeBox kind
and modifier
keywords. There is documentation on configuring AJV here to enable these, alternatively, you can use Type.Strict(T)
to cull out these keywords if you need to test things, refer to readme for more info.
Hey @sinclairzx81,
thanks for your amazing support! Yes I don`t need the Type.Box type. It is as you described it ... there was no need for me to change anything related to kind or modifier for ajv.
This is what I achieved so far.
Now the issue I have to solve is why I get def-0 as schema name.. This seems to be a fastify-swagger issue when fastify.addSchema is used... But it`s annoying cause I wanted to create my typescript interfaces for the frontend out of it.
Thanks a lot again for this amazing lib.
br,
pharindoko
I`ve added a repo with the solution https://github.com/pharindoko/fastify-prisma-ts
Would be great if type.ref would be a stable feature (not experimental)
@pharindoko Hey thanks :)
Just on Type.Ref()
and Type.Box()
as experimental. The only external changes I expect there is potentially renaming Type.Box()
to Type.Namespace()
(this to align JSON Schema closer to XML's xmlns
for a reference model on how boxed types should work, as well as to align with TypeScript's namespace
keyword)
All other changes are anticipated to be internal to TypeBox (for example renaming definitions
to $def
). I am also currently waiting on some external functionality in AJV related to Type.Rec()
recursive types, which has some flow of implications to Type.Ref()
.
Thanks for the reference project, very helpful.
@pharindoko Hey thanks :)
Just on
Type.Ref()
andType.Box()
as experimental. The only external changes I expect there is potentially renamingType.Box()
toType.Namespace()
(this to align JSON Schema closer to XML'sxmlns
for a reference model on how boxed types should work, as well as to align with TypeScript'snamespace
keyword)All other changes are anticipated to be internal to TypeBox (for example renaming
definitions
to$def
). I am also currently waiting on some external functionality in AJV related toType.Rec()
recursive types, which has some flow of implications toType.Ref()
.Thanks for the reference project, very helpful.
What did Type.Box
end up getting renamed to? I don't see anything like it on 0.32.14
Hey @sinclairzx81
getting following error trying to use fastify-swagger with openapi 3.0 and schema/refs
I do have following code in place:
for openapi 3.0 spec the object schema has to be under --> components --> schemas
currently 'definitions' will be set (swagger 2.0) statically. https://github.com/sinclairzx81/typebox/blob/4a57e05116c765317f31bb13d767365ad4af7da6/src/typebox.ts#L499
My Request:
Can I have an enum or flag to use (swagger /'definitions' or openapi behaviour ('components/schemas') ? e.g.
or maybe with a string