Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for alternative runtimes like Egde (Axios not functioning in Vercel Edge runtime) #130

Open
sundaycrafts opened this issue Jun 17, 2023 · 10 comments

Comments

@sundaycrafts
Copy link

sundaycrafts commented Jun 17, 2023

Hi maintainers,

I've encountered an issue where Axios is not functioning within the Vercel Edge runtime.
My understanding is that Postmark.js depends on Axios for some kind of compatibility, but it is intended to run solely on the server side, so concerns such as browser backward compatibility are not relevant.

As a potential solution, replacing Axios with the Fetch Web standard API might be best. I would appreciate your thoughts on this.

Thanks in advance for your attention to this matter.

@pepyta
Copy link

pepyta commented Jun 19, 2023

I've also encountered this issue today. Using axios in edge runtime is in fact not supported and causes runtime exception.

The best approach would be to use the @edge-runtime/ponyfill to use the web API when possible and to provide polyfill when needed.

Until, the library supports Edge runtime, you can use one of these workarounds:

  1. Opt out of edge runtime for those routes that uses the Postmark client. I know, that this can be problematic as some of the APIs might change when switching back to NodeJS, that can cause unexpected side effects.

  2. You can write a custom API wrapper that communicates with the Postmark API. Here is an example that lets you send emails through:

import { type Message } from "postmark";

if (!process.env.POSTMARK_TOKEN) {
    throw new Error(`POSTMARK_TOKEN is not set!`);
}

// We are using this workaround because the internal postmark client uses axios and that prevents us from using the edge runtime.
export const postmark = {
    /**
     * Sends an email through the Postmark API.
     * @param message The content of the email.
     * @returns The pure response object from the server.
     */
    sendEmail: async (message: Message) => {
        const response = await fetch('https://api.postmarkapp.com/email', {
            method: 'POST',
            headers: {
                "Accept": 'application/json',
                "Content-Type": 'application/json',
                "X-Postmark-Server-Token": process.env.POSTMARK_TOKEN,
            } as any,
            body: JSON.stringify(message),
        });

        if(!response.ok) {
            throw new Error(await response.json());
        }

        return await response.json();
    },
};

@ibalosh
Copy link
Contributor

ibalosh commented Jun 27, 2023

Hi

Postmark.js is mainly a Node JS library and at it does not have support for now for alternative runtimes like Edge.
We will add Edge to our request list for now.

The Postmark library is flexible though and http client used can be replaced, in case you would like to use something else.
Since the version of the library 3.0.1, this is possible.

For node-fetch http client for example, implemented http client contract could look like this:

const fetch = require("node-fetch");
import * as postmark from "postmark";
import {ErrorHandler} from "postmark/dist/client/errors";

const client: postmark.ServerClient = new postmark.ServerClient("token");

class HttpClientNew extends postmark.Models.HttpClient {
  public client:any;
  private errorHandler: ErrorHandler;

  public constructor(configOptions?: postmark.Models.ClientOptions.Configuration) {
    super(configOptions);
    this.errorHandler = new ErrorHandler();
  }

  public initHttpClient(configOptions?: postmark.Models.ClientOptions.Configuration): any {
    this.client = fetch.default;
  }

  public httpRequest<T>(httpMethod: postmark.Models.ClientOptions.HttpMethod, path: string, queryParameters: ({} | object),
                        body: (null | object), requestHeaders: any): Promise<any> {

    let that = this;
    return this.client(`${this.getBaseHttpRequestURL()}/${path}`, {
      method: httpMethod,
      headers: requestHeaders,
      body: JSON.stringify(body)}).then(async function (response:any) {
        if (!response.ok) {
          let dataJson = await response.json();
          await Promise.reject(that.errorHandler.buildError(dataJson.Message,dataJson.ErrorCode, response.status));
        } else {
          return response.json();
        }
    });
  }
}

client.httpClient = new HttpClientNew();

In order to use your custom http client you will need to handle requests and errors to provide library compatible errors and response in json format. One thing to keep in mind is that although this is possible now, we do not provide support for it, since it can completely alter the behaviour of the library and is out of the scope of our verifications.

@ibalosh ibalosh changed the title Axios not functioning in Vercel Edge runtime Add support for alternative runtimes like Egde (Axios not functioning in Vercel Edge runtime) Jun 27, 2023
@sundaycrafts
Copy link
Author

Hi @ibalosh

Thank you for response.

The Postmark library is flexible though and http client used can be replaced, in case you would like to use something else.
Since the version of the library 3.0.1, this is possible.

That's really good to know! My problem has been solved, but I'm leaving this issue open because of the added feature-request. What I really appreciate is that this library works with edge runtime straight out of the box.

@sundaycrafts
Copy link
Author

sundaycrafts commented Jun 27, 2023

Just so you know, I have replaced Axios for myself. I didn't run unit/int. tests, but it seems to work fine. I hope it helps your work!

https://github.com/sundaycrafts/postmark.js/commits/main (npm)

@kelbyfaessler
Copy link

+1 for edge runtime support please

@jackhuynh95
Copy link

jackhuynh95 commented Nov 29, 2023

@kelbyfaessler

try using https://www.npmjs.com/package/@sundaycrafts/postmark, it works well

thank fork repo @sundaycrafts

@MaikuMori
Copy link

Similar issues with Bun. I think moving to fetch based implementation is the way to go. At least supporting both.

@houmark
Copy link

houmark commented Feb 22, 2024

+1 for adding edge runtime support.

@EvanBoyle
Copy link

Why is postmark listed as the preferred Vercel email provider if the nodejs client doesn't even work on the edge?
https://vercel.com/guides/sending-emails-from-an-application-on-vercel

Sharing my workaround for templated emails:

import { TemplatedMessage  } from "postmark";

// TODO: replace postmark https://github.com/ActiveCampaign/postmark.js/issues/130
export const sendInviteEmail = async (fromEmail: string, toEmail: string, org: string) => {
    const template: TemplatedMessage = {
        "From": fromEmail,
        "To": toEmail,
        "TemplateAlias": "user-invitation",
        "TemplateModel": {
          "to_email": toEmail,
          "from_email": fromEmail,
          "org": org,
        }
      };
    await fetch("https://api.postmarkapp.com/email/withTemplate", {
        method: "POST",
        headers: {
            "Accept": 'application/json',
            "Content-Type": 'application/json',
            "X-Postmark-Server-Token": process.env.POSTMARK_SERVER_API_TOKEN,
        } as any,
        body: JSON.stringify(template),
    })
}

@ibalosh
Copy link
Contributor

ibalosh commented Apr 15, 2024

Hi @EvanBoyle , we did not list on Vercel postmark, but since the library allows you to implement any http client you want, its pretty straightforward to make the adjustment to any http client you want including fetch which I described at the top. Or you can use @sundaycrafts solution which does just that, implements alternative http client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants