Search code examples
reactjstypescriptintegrationzod

How to transform object to array before parsing in Zod


I do have an external URL endpoint that returns an array of field object when it is more than 2 and an object when there is only one, see the snippet below:

Return when the field count is one:

{
  "fields": { "fullName": "fieldFullname", "type": "fieldType" }
}

Return when the field is more than one:

{
  "fields": [
      { "fullName": "fieldFullname", "type": "fieldType" },
      { "fullName": "fieldFullname", "type": "fieldType" }
   ]
}

Currently, this is my schema using zod:

export const sObjectMetadataSchema = z.object({
  fields: z.array(metadataFieldSchema).optional()
});

export const metadataFieldSchema = z.object({
  fullName: z.string().optional(),
  type: z.string().optional(),
});

It is configured that it will only accept an array of objects. When it returns only one field it throws an error:

{
  "code": "invalid_type",
  "expected": "array",
  "received": "object",
  "path": [],
  "message": "Expected array, received object"
}

My goal is if it returns a single object it will convert it to an array of objects during runtime. Currently trying to implement using transform but still not working:

An initial implementation using transform:

export const sObjectMetadataSchema = z.object({
fields: z.unknown().transform((rel) => {
    return Array.isArray(rel)
        ? z.array(metadataFieldSchema).optional()
        : 'Convert the rel to Array?';
    }),
});

Solution

  • I didn't test that, but seems like should work:

    const FieldsSchema = z.object({
      fullName: z.string(),
      type: z.string()
    });
    
    export const sObjectMetadataSchema = z.object({
    fields: z.union([FieldsSchema, FieldsSchema.array()]).transform((rel) => {
        return Array.isArray(rel)
            ? rel
            : [rel];
        }),
    });