Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

The property 'relationTo' doesn't accept String | PayloadCMS v3

default discord avatar
ashketshuplast year
12

Hi, I'm migrating a few Collections from PayloadCMS v2 to v3 and it's giving me an error with the following:


Type '"translations"' is not assignable to type 'CollectionSlug | CollectionSlug[]'.ts(2322)
(property) relationTo: CollectionSlug[]


Prior to version 3, the property 'relationTo' could be assigned to a String corresponding to the Slug of the collection. Not sure what changed.



Here's the code snippet:


const personFields: Field[] = [
    { name: 'matchName' , type: 'relationship', relationTo: 'translations', hasMany: true },
    { name: 'shortName' , type: 'relationship', relationTo: 'translations', hasMany: true },
    { name: 'firstName' , type: 'text' },
    { name: 'lastName'  , type: 'text' },
    { name: 'image'     , type: 'text' },
    { name: 'video'     , type: 'text' },
    { name: 'vbn_folder', type: 'text' },
    { name: 'team'      , type: 'relationship', relationTo: 'teams', admin: { readOnly: true } },
]
  • default discord avatar
    rilromlast year

    Could you share the import for the Field type?

  • default discord avatar
    ashketshuplast year

    This is the whole file.



    import { CollectionConfig, CollectionAfterChangeHook, Field, CollectionAfterLoginHook } from 'payload'
    import { cache } from '../middlewares';
    
    // Languages Collection
    const languageFields: Field[] = [
        { name: 'name', type: 'text' },
        { name: 'isDefault', type: 'checkbox' }
    ]
    
    const Languages: CollectionConfig = {
        slug: 'languages',
        admin: { useAsTitle: 'name' },
        fields: languageFields,
        access: {
            read  : () => true, // allows public GET
            update: () => true, // allows public PATCH
            create: () => true, // allows public POST
            delete: () => true, // allows public DELETE
        },
    }
    
    const translationFields: Field[] = [
        { name: 'key'     , type: 'text' },
        { name: 'language', type: 'relationship', relationTo: 'languages' },
        { name: 'value'   , type: 'text' },
    ]
    
    
    const afterChangeHook: CollectionAfterChangeHook = async ({ doc }) => { cache.deletekeys(['&target=']); };
    
    const Translations: CollectionConfig = {
        slug: 'translations',
        admin: { useAsTitle: 'name' },
        fields: translationFields,
        access: {
            read  : () => true, // allows public GET
            update: () => true, // allows public PATCH
            create: () => true, // allows public POST
            delete: () => true, // allows public DELETE
        }
    };
    
    
    export { Languages, Translations };


    I think i've tracked down the problem to the generated types



    But made me go against another problem



    Zod is screaming out of its lungs that the validation is not working



    For context, Zod is being called only here:


    import { z } from 'zod';
    import dotenv from "dotenv";
    dotenv.config();
    
    const isBoolean = (bool: string) => bool == String(true) || bool == String(false);
    const isNumber = (num: string) => !Number.isNaN(Number(num));
    
    const envSchema = z.object({
        PAYLOAD_PUBLIC_DATABASE_URI: z.string()
            .refine((URI: string) => URI.startsWith("mongodb://"), "Invalid Database URI")
        ,
        PAYLOAD_PUBLIC_PAYLOAD_SECRET: z.string(),
        PAYLOAD_PUBLIC_OPTA_KEY: z.string(),
        PAYLOAD_PUBLIC_CACHE_ONEDAY_MS: z.string()
            .refine(isNumber, "Cache time is not a Number")
            .refine((num: string) => Number(num) >= 0, "Cache time cannot be negative.")
        ,
        PAYLOAD_PUBLIC_CACHE_FIVEMINUTES_MS: z.string()
            .refine(isNumber, "Cache time is not a Number")
            .refine((num: string) => Number(num) >= 0, "Cache time cannot be negative.")
        ,
        PAYLOAD_PUBLIC_TZ: z.string().refine(
            (tz: string) => Intl.supportedValuesOf('timeZone').includes(tz),
            "Timezone can't be recognized."
        ),
        PAYLOAD_PUBLIC_DISABLE_LOGGING: z.string().refine(isBoolean, "Not a boolean."),
        PAYLOAD_PUBLIC_SHEETS_TRANSLATE_ENABLE: z.string().refine(isBoolean, "Not a boolean."),
        PAYLOAD_PUBLIC_FLAGS_PATH: z.string(),
        PAYLOAD_PUBLIC_TEAM_LOGO_PATH: z.string(),
        PAYLOAD_PUBLIC_TEAM_BIG_LOGO_PATH: z.string(),
        PAYLOAD_PUBLIC_TEAM_SMALL_LOGO_PATH: z.string(),
        PAYLOAD_PUBLIC_COMPETITIONS_LOGO: z.string(),
        PAYLOAD_PUBLIC_COMPETITIONS_SMALL_LOGO: z.string(),
        PAYLOAD_PUBLIC_PLAYER_PHOTO_PATH: z.string(),
        PAYLOAD_PUBLIC_PLAYER_BIG_PHOTO_PATH: z.string(),
        PAYLOAD_PUBLIC_PLAYER_SMALL_PHOTO_PATH: z.string(),
        PAYLOAD_PUBLIC_SHEET_ID: z.string(),
        PAYLOAD_PUBLIC_SHEET_CREDENTIALS_FILE: z.string(),
        PAYLOAD_PUBLIC_SEASONS_ID: z.string(),
        PAYLOAD_PUBLIC_LANGUAGES: z.string(),
        NEXT_PUBLIC_SERVER_URL: z.string(),
    });
    
    type Env = z.infer<typeof envSchema>;
    
    declare global {
        namespace NodeJS {
            interface ProcessEnv extends Env { }
        }
    }
    
    console.log(`Process Env:
        ${process.env}`)
    
    export const ENV: Env = envSchema.parse(process.env);
  • default discord avatar
    rilromlast year

    What errors is zod showing

  • default discord avatar
    ashketshuplast year

    That every single one of the properties are getting undefined (Invalid Types):


    ZodError: [
      {
        "code": "invalid_type",
        "expected": "string",
        "received": "undefined",
        "path": [
          "PAYLOAD_PUBLIC_DATABASE_URI"
        ],
        "message": "Required"
      },
      {
        "code": "invalid_type",
        "expected": "string",
        "received": "undefined",
        "path": [
          "PAYLOAD_PUBLIC_PAYLOAD_SECRET"
        ],
        "message": "Required"
      },
      {
        "code": "invalid_type",
        "expected": "string",
        "received": "undefined",
        "path": [
          "PAYLOAD_PUBLIC_OPTA_KEY"
        ],
        "message": "Required"
      }
    ]
        at get error [as error] (file:///D:/backoffice-keystone-opta/backoffice-opta/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/index.mjs?tsx-namespace=1732276123176:587:31)
        at ZodObject.parse (file:///D:/backoffice-keystone-opta/backoffice-opta/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/index.mjs?tsx-namespace=1732276123176:692:22)
        at <anonymous> (D:\backoffice-keystone-opta\backoffice-opta\src\env.ts:57:35)
        at ModuleJob.run (node:internal/modules/esm/module_job:234:25)
        at async ModuleLoader.import (node:internal/modules/esm/loader:473:24)
        at async bin (D:\backoffice-keystone-opta\backoffice-opta\node_modules\.pnpm\payload@3.0.2_graphql@16.9.0_monaco-editor@0.52.0_react-dom@19.0.0-rc-65a56d0e-20241020_react_fp54bueddpkts7o7cdap7tptuu\node_modules\payload\dist\bin\index.js:47:27)
        at async start (file:///D:/backoffice-keystone-opta/backoffice-opta/node_modules/.pnpm/payload@3.0.2_graphql@16.9.0_monaco-editor@0.52.0_react-dom@19.0.0-rc-65a56d0e-20241020_react_fp54bueddpkts7o7cdap7tptuu/node_modules/payload/bin.js:30:7) {
      issues: [
        {
          code: 'invalid_type',
          expected: 'string',
          received: 'undefined',
          path: [ 'PAYLOAD_PUBLIC_DATABASE_URI' ],
          message: 'Required'
        },
        {
          code: 'invalid_type',
          expected: 'string',
          received: 'undefined',
          path: [ 'PAYLOAD_PUBLIC_PAYLOAD_SECRET' ],
          message: 'Required'
        },
        {
          code: 'invalid_type',
          expected: 'string',
          received: 'undefined',
          path: [ 'PAYLOAD_PUBLIC_OPTA_KEY' ],
          message: 'Required'
        }
      ],
      addIssue: [Function (anonymous)],
      addIssues: [Function (anonymous)],
      errors: [
        {
          code: 'invalid_type',
          expected: 'string',
          received: 'undefined',
          path: [ 'PAYLOAD_PUBLIC_DATABASE_URI' ],
          message: 'Required'
        },
        {
          code: 'invalid_type',
          expected: 'string',
          received: 'undefined',
          path: [ 'PAYLOAD_PUBLIC_PAYLOAD_SECRET' ],
          message: 'Required'
        },
        {
          code: 'invalid_type',
          expected: 'string',
          received: 'undefined',
          path: [ 'PAYLOAD_PUBLIC_OPTA_KEY' ],
          message: 'Required'
        }
      ]
    }


    Oh wait a min



    Gimme a sec

  • default discord avatar
    rilromlast year

    I think you need to be using NEXT_PUBLIC

  • default discord avatar
    ashketshuplast year

    Wait. its no longer PAYLOAD_PUBLIC_?

  • default discord avatar
    rilromlast year

    I dont believe so

  • default discord avatar
    ashketshuplast year

    Ok, so zod having a tantrum had its reasoning



    I was missing those in the .env file

  • default discord avatar
    rilromlast year

    Here's a snippet of the migration docs



    https://github.com/payloadcms/payload/blob/main/docs/migration-guide/overview.mdx

    Keep in mind this is only for client values, some of those you shared shouldnt be client values.

  • default discord avatar
    ashketshuplast year

    F, so the v3 docs need to be updated. Cuz it still says that PAYLOAD_PUBLIC_ is the way to go



    I'm aware, its just for development purposes only.

  • default discord avatar
    rilromlast year

    Would you mind sharing where in the docs it still says PAYLOAD_PUBLIC? I can look to resolve it.

  • default discord avatar
    ashketshuplast year

    Oh wait a min



    Since 2 days ago it changed



    Ok, I found what the problem was. I had to declare the Collections in the PayloadConfig and generate the Types. I ended up solving the conflicts and now it does recognize 'translations' as a CollectionSlug

Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team.