Custom validation and 500 error in docker?

System Information
  • Strapi Version: 4.14.5
  • Operating System: macos (local environment), debian (docker image based on node:18-slim)
  • Database: PostgreSQL
  • Node Version: 18.18.2

Hey,

I hope someone can help me with this as I’m scratching my head for few days already. Ive tried looking into documentation, this forum, google search, but I found only scraps regarding custom fields validation.

Basically, in a component called sponsored I have two date fields (start date and end date). I need to validate that if both dates are filled, the start date must be before end date.

I tried to apply the solution from @sofian-io that he posted in this forum post.

So my article lifecycle file looked like this:

import {errors} from "@strapi/utils";
import yup from "yup"

export default {
  beforeCreate(event) {
    const ctx = strapi.requestContext.get();
    const sponsored = ctx.request.body.sponsored;
    sponsoredValidation(sponsored);
  }
}
function sponsoredValidation(sponsored) {
  if (sponsored?.isSponsored) {
    if (sponsored.sponsoredEndDate < sponsored.sponsoredStartDate) {
      sponsoredErrorMessage("SponsoredStartDate should be before SponsoredEndDate.", sponsored.sponsoredEndDate);
    }
  }
}

function sponsoredErrorMessage(message, endDate) {
  const errorMessage: yup.ValidationError = {
      path: 'sponsored.sponsoredEndDate',
      message: message,
      value: endDate,
      name: "Sponsored  Validation",
      errors: null,
      inner: null,
  };
  throw new errors.YupValidationError(errorMessage)
}

It was running fine when executed from my local environment using npm run develop
The field validation run fine and I got red border on the date field with a Validation message underneath saying “SponsoredStartDate should be before SponsoredEndDate.”

I don’ t get anything logged in console,

and in chrome dev tools I can see request to /content-manager/collection-types/api::article.article/128 with a 400 response and response body

{
    "data": null,
    "error": {
        "status": 400,
        "name": "ValidationError",
        "message": "SponsoredStartDate should be before SponsoredEndDate.",
        "details": {
            "errors": [
                {
                    "path": [
                        "sponsored",
                        "sponsoredEndDate"
                    ],
                    "message": "SponsoredStartDate should be before SponsoredEndDate.",
                    "name": "Sponsored Start Date"
                }
            ]
        }
    }
}

The same code, deployed to a docker image, in the UI I briefly see an error popup saying “Warning: Internal Server Error” and logged in the console

[2023-11-06 15:31:19.429] error: SponsoredStartDate should be before SponsoredEndDate.
ValidationError: SponsoredStartDate should be before SponsoredEndDate.
    at sponsoredErrorMessage (/opt/app/dist/src/api/article/content-types/article/lifecycles.js:44:11)
    at sponsoredValidation (/opt/app/dist/src/api/article/content-types/article/lifecycles.js:31:13)
    at Object.beforeUpdate (/opt/app/dist/src/api/article/content-types/article/lifecycles.js:22:9)
    at modelsLifecyclesSubscriber (/opt/node_modules/@strapi/database/dist/index.js:5877:79)
    at Object.run (/opt/node_modules/@strapi/database/dist/index.js:5963:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Object.update (/opt/node_modules/@strapi/database/dist/index.js:5078:22)
    at async Object.update (/opt/node_modules/@strapi/strapi/dist/services/entity-service/index.js:161:22)
    at async Object.<anonymous> (/opt/node_modules/@strapi/strapi/dist/services/entity-service/index.js:295:24)
    at async Object.update (/opt/node_modules/@strapi/plugin-i18n/server/services/entity-service-decorator.js:155:19)

in chrome dev tools I can see a see request to /content-manager/collection-types/api::article.article/128 with a 500 response and response body

{
    "data": null,
    "error": {
        "status": 500,
        "name": "InternalServerError",
        "message": "Internal Server Error"
    }
}

Then I thought that maybe lifecycle hook is not the best place to do this. So after some digging I found this blog post and decided to move the validation into a global middleware.

Again everything works fine in local dev environment, but when I execute in docker, I have the same behaviour - 500 response and error logged.

and this is my Dockerfile

# Creating multi-stage build for production
FROM public.ecr.aws/docker/library/node:18-slim as build
RUN apt-get update
RUN apt-get install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev -y
RUN apt-get install python3 -y
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*

ARG NODE_ENV=prod
ENV NODE_ENV=${NODE_ENV}

WORKDIR /opt/
COPY code/package.json ./
COPY code/patches/ ./patches/
RUN npm install --production
ENV PATH /opt/node_modules/.bin:$PATH
WORKDIR /opt/app
COPY code/ .
RUN npm run build

# Creating final production image
FROM public.ecr.aws/docker/library/node:18-slim

WORKDIR /opt/
COPY --from=build /opt/node_modules ./node_modules
WORKDIR /opt/app
COPY --from=build /opt/app ./
ENV PATH /opt/node_modules/.bin:$PATH

RUN chown -R node:node /opt/app
USER node
EXPOSE 1337
CMD ["npm", "run", "start"]

I also noticed same with invalid credentials - when I provide invalid credentials on my local environment, the UI validation message will say Invalid credentials. But run from inside docker, I see Internal Server Error and error log in the console saying:

[2023-11-06 15:59:05.419] error: Invalid credentials
ApplicationError: Invalid credentials
    at /opt/node_modules/@strapi/admin/server/controllers/authentication.js:36:17
    at callback (/opt/node_modules/koa-passport/lib/framework/koa.js:93:25)
    at allFailed (/opt/node_modules/passport/lib/middleware/authenticate.js:110:18)
    at attempt (/opt/node_modules/passport/lib/middleware/authenticate.js:183:28)
    at strategy.fail (/opt/node_modules/passport/lib/middleware/authenticate.js:305:9)
    at verified (/opt/node_modules/passport-local/lib/strategy.js:82:30)
    at /opt/node_modules/@strapi/admin/server/services/passport/local-strategy.js:21:18

Why would validation error handling behave differently depending on environment? Is there any specific configuration that I need to add/update to make sure it runs correctly on any environment?

In Discord, user Daedalus suggested I don’t copy the package-lock.json file to docker, so it may be that different dependencies versions are being used locally and in docker images. I was able to replicate this after clearing the project (deleted .cache, node_modules, dist folders package-lock.json fille) and after building locally was behaving this way too. So maybe some newer dependency could have caused it.

I added package-lock.json to Dockerfile and it worked well.