405 Cloudinary ONLY on production using Vercel - Nextjs

joao
joao Member Posts: 2
edited February 15 in Developer APIs

Please help, 2 weeks stuck. So far no answers works, no AI, nobody, no workaround, I have no idea where to look anymore.

Uploading images on development works perfect, on production 405. Add headers, take headers, change CORS, use Axios, change to fetch, clear cache, downgrade Nextjs, try different npm's etc... No idea on what to do next.


ON FRONT

  const createImagesSrc = async (

    imageFileToAdd: File[]

  ): Promise<AxiosResponse | null> => {

    try {

      const formData = new FormData();

      imageFileToAdd.forEach((image) => {

        formData.append("images", image);

      });

      const uploadResponse = await axios.post("/api/upload", formData, {

      })

      if (uploadResponse.status === 200) {

        uploadResponse.data.uploadResponses.forEach(

          (response: any, index: number) => {

            imageSrc.push(response.secure_url);

          }

        );

        return uploadResponse;

      } else {

        console.error("Failed to upload images");

        return null;

      }

    } catch (error) {

      console.error(error);

      return null;

    }

  };


ON BACK

cloudinary.config({

  cloud_name: process.env.CLOUD_NAME,

  api_key: process.env.API_KEY,

  api_secret: process.env.API_SECRET,

});


export async function POST(request: Request) {

  const data = await request.formData();

  const files = data.getAll("images");


  if (files.length === 0) {

    return NextResponse.json({

      success: false,

      message: "No images to upload.",

    });

  }


  const uploadPreset = "yfhyp9my";

  const uploadResponses: any[] = [];


  for (const file of files) {

    // @ts-ignore

    const bytes = await file.arrayBuffer();

    const buffer = Buffer.from(bytes);


    const response = await new Promise((resolve, reject) => {

      cloudinary.uploader

        .upload_stream({ upload_preset: uploadPreset }, (error, result) => {

          if (error) {

            reject(error);

          }

          resolve(result);

        })

        .end(buffer);

    });

    uploadResponses.push(response);

  }

  return NextResponse.json({ success: true, uploadResponses });

}


NEXT.CONFIG.JS

const nextConfig = {

  async headers() {

    return [

      {

        // matching all API routes

        source: "/api/:path*",

        headers: [

          { key: "Access-Control-Allow-Credentials", value: "true" },

          { key: "Access-Control-Allow-Origin", value: "coolbananas.org, localhost:3000" },

          {

            key: "Access-Control-Allow-Methods",

            value: "GET,OPTIONS,PATCH,DELETE,POST,PUT",

          },

          {

            key: "Access-Control-Allow-Headers",

            value:

              "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version",

          },

        ],

      },

    ];

  },

  images: {

    remotePatterns: [

      {

        protocol: "https",

        hostname: "res.cloudinary.com",

        port: "",

        pathname: "/jpsm83/image/upload/**",

      },

      {

        protocol: "https",

        hostname: "lh3.googleusercontent.com",

      },

      {

        protocol: "https",

        hostname: "restcountries.com",

      },

    ],

  },

};


ERROR

POST https://www.coolbananas.org/api/upload 405 (Method Not Allowed)

  1. AxiosError {message: 'Request failed with status code 405', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
  2. code
  3. :
  4. "ERR_BAD_REQUEST"
  5. config
  6. :
  7. {transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}
  8. message
  9. :
  10. "Request failed with status code 405"
  11. name
  12. :
  13. "AxiosError"
  14. request
  15. :
  16. XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
  17. response
  18. :
  19. config
  20. :
  21. adapter
  22. :
  23. (2) ['xhr', 'http']
  24. data
  25. :
  26. FormData {}
  27. env
  28. :
  29. {FormData: ƒ, Blob: ƒ}
  30. headers
  31. :
  32. AxiosHeaders {Accept: 'application/json, text/plain, */*', Content-Type: false}
  33. maxBodyLength
  34. :
  35. -1
  36. maxContentLength
  37. :
  38. -1
  39. method
  40. :
  41. "post"
  42. timeout
  43. :
  44. 0
  45. transformRequest
  46. :
  47. [ƒ]
  48. transformResponse
  49. :
  50. [ƒ]
  51. transitional
  52. :
  53. {silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false}
  54. url
  55. :
  56. "/api/upload"
  57. validateStatus
  58. :
  59. ƒ (e)
  60. xsrfCookieName
  61. :
  62. "XSRF-TOKEN"
  63. xsrfHeaderName
  64. :
  65. "X-XSRF-TOKEN"
  66. [[Prototype]]
  67. :
  68. Object
  69. data
  70. :
  71. ""
  72. headers
  73. :
  74. AxiosHeaders {access-control-allow-credentials: 'true', access-control-allow-headers: 'X-CSRF-Token, X-Requested-With, Accept, Accept-Ver…h, Content-MD5, Content-Type, Date, X-Api-Version', access-control-allow-methods: 'GET,OPTIONS,PATCH,DELETE,POST,PUT', access-control-allow-origin: 'coolbananas.org, localhost:3000', cache-control: 'public, max-age=0, must-revalidate', …}
  75. request
  76. :
  77. XMLHttpRequest
  78. onabort
  79. :
  80. ƒ ()
  81. onerror
  82. :
  83. ƒ ()
  84. onload
  85. :
  86. null
  87. onloadend
  88. :
  89. ƒ onloadend()
  90. onloadstart
  91. :
  92. null
  93. onprogress
  94. :
  95. null
  96. onreadystatechange
  97. :
  98. null
  99. ontimeout
  100. :
  101. ƒ ()
  102. readyState
  103. :
  104. 4
  105. response
  106. :
  107. ""
  108. responseText
  109. :
  110. ""
  111. responseType
  112. :
  113. ""
  114. responseURL
  115. :
  116. "https://www.coolbananas.org/api/upload"
  117. responseXML
  118. :
  119. null
  120. status
  121. :
  122. 405
  123. statusText
  124. :
  125. ""
  126. timeout
  127. :
  128. 0
  129. upload
  130. :
  131. XMLHttpRequestUpload
  132. onabort
  133. :
  134. null
  135. onerror
  136. :
  137. null
  138. onload
  139. :
  140. null
  141. onloadend
  142. :
  143. null
  144. onloadstart
  145. :
  146. null
  147. onprogress
  148. :
  149. null
  150. ontimeout
  151. :
  152. null
  153. [[Prototype]]
  154. :
  155. XMLHttpRequestUpload
  156. withCredentials
  157. :
  158. false
  159. [[Prototype]]
  160. :
  161. XMLHttpRequest
  162. status
  163. :
  164. 405
  165. statusText
  166. :
  167. ""
  168. [[Prototype]]
  169. :
  170. Object
  171. stack
  172. :
  173. "AxiosError: Request failed with status code 405\n at settle (https://www.coolbananas.org/_next/static/chunks/118-3737272d4a0dcc07.js:37:18624)\n at XMLHttpRequest.onloadend (https://www.coolbananas.org/_next/static/chunks/118-3737272d4a0dcc07.js:37:21902)"
  174. [[Prototype]]
  175. :
  176. Error


This is an issue only in PRODUCTION mode, development is fine. All "keys" been add to Vercel and Vercel is working properly once I can save all data to MongoDB but the images doesnt get create at Cloudinary. I tryed to update CORS at Cloudinary but seen to have no such option on their website. Very frustate, I got pretty bad stuck ;(

Tagged:

Best Answer

  • Zachary
    Zachary Member, Cloudinary Staff Posts: 46
    Answer ✓

    Using the SDK method upload_stream, there is an unhandled error when the method is invoked in a build NextJS environment. An error HTML page is being received as a response instead of a JSON data format.

    As an alternative to the use of upload_stream, the `upload` method of the Node SDK together with a Data URI can be used, for example:

    import { v2 as cloudinary } from "cloudinary";
    import { NextResponse } from "next/server";
    
    // Cloudinary config
    cloudinary.config({
      cloud_name: "your_cloud_name",
      api_key: "your_api_key",
      api_secret: "your_api_secret",
      secure: true,
    });
    
    export const POST = async (req) => {
      
      const data = await req.formData();
      const image = await data.get("image");
      const fileBuffer = await image.arrayBuffer();
    
      var mime = image.type; 
      var encoding = 'base64'; 
      var base64Data = Buffer.from(fileBuffer).toString('base64');
      var fileUri = 'data:' + mime + ';' + encoding + ',' + base64Data;
    
      try {
        
        const uploadToCloudinary = () => {
          return new Promise((resolve, reject) => {
    
              var result = cloudinary.uploader.upload(fileUri, {
                invalidate: true
              })
                .then((result) => {
                  console.log(result);
                  resolve(result);
                })
                .catch((error) => {
                  console.log(error);
                  reject(error);
                });
          });
        };
    
        const result = await uploadToCloudinary();
        
        let imageUrl = result.secure_url;
    
        return NextResponse.json(
          { success: true, imageUrl: imageUrl },
          { status: 200 }
        );
      } catch (error) {
        console.log("server err", error);
        return NextResponse.json({ err: "Internal Server Error" }, { status: 500 });
      }
    };
    

    As for the uploading of files in their byte array buffer format, we have raised an internal ticket for review and to provide the needed fix.


    Can you try this different upload method?

Answers

  • joao
    joao Member Posts: 2

    AMAZING is working..... :D Thanks very much, you made my day

  • lolikana
    lolikana Member Posts: 1

    Saved my life.

    Spend two day to handle this error with nextjs 14 and server action.

    The nextjs-server-actions-upload example was working perfectly but when tried to implement in my project which was a little bit different it did not work.

    And because I had an http-error 500 I did not find this post... I was desperate I start to try anything and found yours.

    Thank you

  • Cristguevara
    Cristguevara Member Posts: 1

    Thank you. I just found this solutions and works well. I didn't know what was happening, you save my life.