Search code examples
reactjs.nettypescriptnext.jscors

ASP.NET/React.JS - API/CORS NOT fetching when using react-email


I have a React App running at port 44411 and react-email at port 3000.

I have watched a youtube vid on how to set up react-email and resend but it inmediately didn't work. So after fiddling with it alot I got it to take my API request through postman so the next step was to make it work through the website. I enabled CORS (Which also caused postman requests to not work anymore), and tried sending it through website (API request sent from 44411 tries fetching at 3000) which triggers this error:

Access to fetch at 'http://localhost:3000/api/email' from origin 'https://localhost:44411' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
ContactForm.js:30 
        
        
POST http://localhost:3000/api/email net::ERR_FAILED
handleSubmit @ ContactForm.js:30
callCallback @ react-dom.development.js:3706
invokeGuardedCallbackDev @ react-dom.development.js:3750
invokeGuardedCallback @ react-dom.development.js:3807
invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:3821
executeDispatch @ react-dom.development.js:7964
processDispatchQueueItemsInOrder @ react-dom.development.js:7990
processDispatchQueue @ react-dom.development.js:8001
dispatchEventsForPlugins @ react-dom.development.js:8010
(anonymous) @ react-dom.development.js:8170
batchedUpdates$1 @ react-dom.development.js:22559
batchedUpdates @ react-dom.development.js:3554
dispatchEventForPluginEventSystem @ react-dom.development.js:8169
dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ react-dom.development.js:5676
dispatchEvent @ react-dom.development.js:5670
dispatchDiscreteEvent @ react-dom.development.js:5647
Show 15 more frames
Show less
ContactForm.js:55 Error during fetch: TypeError: Failed to fetch
    at handleSubmit (ContactForm.js:30:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3706:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3750:1)
    at invokeGuardedCallback (react-dom.development.js:3807:1)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:3821:1)
    at executeDispatch (react-dom.development.js:7964:1)
    at processDispatchQueueItemsInOrder (react-dom.development.js:7990:1)
    at processDispatchQueue (react-dom.development.js:8001:1)
    at dispatchEventsForPlugins (react-dom.development.js:8010:1)
    at react-dom.development.js:8170:1

And when I enabled CORS and sent a request through postman, these errors started coming up in my email console

 ⨯ TypeError: res.setHeader is not a function
    at applyHeaders (webpack-internal:///(rsc)/./node_modules/cors/lib/index.js:149:25)
    at applyHeaders (webpack-internal:///(rsc)/./node_modules/cors/lib/index.js:145:21)
    at applyHeaders (webpack-internal:///(rsc)/./node_modules/cors/lib/index.js:145:21)
    at cors (webpack-internal:///(rsc)/./node_modules/cors/lib/index.js:179:13)
    at eval (webpack-internal:///(rsc)/./node_modules/cors/lib/index.js:213:33)
    at originCallback (webpack-internal:///(rsc)/./node_modules/cors/lib/index.js:204:29)
    at eval (webpack-internal:///(rsc)/./node_modules/cors/lib/index.js:208:25)
    at optionsCallback (webpack-internal:///(rsc)/./node_modules/cors/lib/index.js:190:17)
    at corsMiddleware (webpack-internal:///(rsc)/./node_modules/cors/lib/index.js:194:13)
    at POST (webpack-internal:///(rsc)/./src/app/api/email/route.ts:25:5)
    at C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\next-server\app-route.runtime.dev.js:6:63257
    at C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\lib\trace\tracer.js:133:36
    at NoopContextManager.with (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\@opentelemetry\api\index.js:1:7062)
    at ContextAPI.with (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\@opentelemetry\api\index.js:1:518)
    at NoopTracer.startActiveSpan (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\@opentelemetry\api\index.js:1:18093)
    at ProxyTracer.startActiveSpan (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\@opentelemetry\api\index.js:1:18854)
    at C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\lib\trace\tracer.js:122:103
    at NoopContextManager.with (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\@opentelemetry\api\index.js:1:7062)
    at ContextAPI.with (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\@opentelemetry\api\index.js:1:518)
    at NextTracerImpl.trace (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\lib\trace\tracer.js:122:28)
    at C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\next-server\app-route.runtime.dev.js:6:56774
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at Object.wrap (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\next-server\app-route.runtime.dev.js:6:37057)
    at C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\next-server\app-route.runtime.dev.js:6:54547
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at Object.wrap (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\next-server\app-route.runtime.dev.js:6:36306)
    at C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\next-server\app-route.runtime.dev.js:6:54509
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at eH.execute (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\next-server\app-route.runtime.dev.js:6:53902)
    at eH.handle (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\compiled\next-server\app-route.runtime.dev.js:6:64515)
    at doRender (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\base-server.js:1330:60)
    at cacheEntry.responseCache.get.routeKind (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\base-server.js:1552:34)
    at ResponseCache.get (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\response-cache\index.js:49:26)
    at DevServer.renderToResponseWithComponentsImpl (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\base-server.js:1460:53)
    at C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\base-server.js:990:121
    at NextTracerImpl.trace (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\lib\trace\tracer.js:104:20)
    at DevServer.renderToResponseWithComponents (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\base-server.js:990:41)
    at DevServer.renderPageComponent (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\base-server.js:1843:35)
    at async DevServer.renderToResponseImpl (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\base-server.js:1881:32)
    at async DevServer.pipeImpl (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\base-server.js:909:25)
    at async NextNodeServer.handleCatchallRenderRequest (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\next-server.js:266:17)
    at async DevServer.handleRequestImpl (C:\Users\renek\source\ReactNet\ReactNet\ClientApp\.react-email\node_modules\next\dist\server\base-server.js:805:17)

This is my route.ts located in ReactNet\ReactNet\ClientApp.react-email\src\app\api\email

import { NextApiRequest, NextApiResponse } from 'next';
import Cors from 'cors';
import { Resend } from 'resend';
import Email from '../../../../emails/ContactEmail';
import AdminEmail from '../../../../emails/Email';

// Create CORS middleware
const corsMiddleware = Cors({
    origin: 'http://localhost:44411', // Update this with your React app's URL
    methods: ['POST'],
});

const resend = new Resend(process.env.RESEND_API_KEY || "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");

// Export a function for the POST method
export async function POST(req: NextApiRequest, res: NextApiResponse) {
    // Apply the CORS middleware
    corsMiddleware(req, res, async () => {
        try {
            const { name, message, email, phone, workType } = req.body;

            await resend.emails.send({
                from: 'onboarding@resend.dev',
                to: email,
                subject: 'TedSite.nl - Bevestiging Contact Aanvraag',
                react: Email({
                    name,
                }),
            });

            await resend.emails.send({
                from: 'onboarding@resend.dev',
                to: "renekukske@gmail.com",
                subject: 'TedSite.nl - Contact Aanvraag',
                react: AdminEmail({
                    name,
                    message,
                    email,
                    phone,
                    workType,
                }),
            });

            res.status(200).json({
                status: 'Ok',
            });
        } catch (e: unknown) {
            if (e instanceof Error) {
                console.log(`Failed to send email: ${e.message}`);
            }

            res.status(500).json({
                error: 'Internal server error.',
            });
        }
    });
}

And this is my handleSubmit function

    // Event handler for form submission
    const handleSubmit = async (e) => {
        e.preventDefault();

        const { name, email, phone, workType, message } = formData;
        console.log('Submitting data:', { name, email, phone, workType, message });

        try {
            const response = await fetch('http://localhost:3000/api/email', {
                method: 'POST',
                mode: "cors",  // Change the mode to CORS
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    name,
                    email,
                    phone,
                    workType,
                    message,
                }),
            });

            if (!response.ok) {
                // Handle non-successful response status
                console.error(`HTTP error! Status: ${response.status}`);
                // You can throw an error or handle it in another way
            } else {
                // Success
                const data = await response.json();
                console.log('Server response:', data);
                // Handle success if needed
            }
        } catch (error) {
            console.error('Error during fetch:', error);
            // Handle errors that occur during the fetch itself
        }
    };

I hope someone knows how to help me.

Thanks in advance!

Bobbie


Solution

  • After fiddling with it some more for 3 days I have learned that:

    • You have to set the headers in next.config.js
      async headers() {
        return [
            {
                // matching all API routes
                source: "/api/:path*",
                headers: [
                    { key: "Access-Control-Allow-Credentials", value: "true" },
                    { key: "Access-Control-Allow-Origin", value: "https://localhost:44411" }, // replace this your actual origin
                    { key: "Access-Control-Allow-Methods", value: "GET,DELETE,PATCH,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" },
                ]
            }
        ]
      },
    
    • For some unknown reason you should set Content-Type to 'application/x-www-form-urlencoded' even though we're clearly creating a JSon body.
                const response = await fetch('http://localhost:3000/api/email', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: JSON.stringify(formData),
                    credentials: 'include',
                    mode: 'cors',
                });
    
    • You have to set Status 200 for an Options request & You need to set the headers for client & Server
        if (request.method === 'OPTIONS') {
            // Handling preflight request
            return NextResponse.json({ status: 200 });
        }
    
        const res = NextResponse.next()
    
        // add the CORS headers to the response
        res.headers.append('Access-Control-Allow-Credentials', "true")
        res.headers.append('Access-Control-Allow-Origin', 'https://localhost:44411') // replace this your actual origin
        res.headers.append('Access-Control-Allow-Methods', 'GET,PATCH,POST')
        res.headers.append(
            'Access-Control-Allow-Headers',
            'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
        )
    
    

    I hope with these answers I can help someone in the future. Setting the Content-Type is what did the trick for me in the end.