Add node SDK (#19)
This commit is contained in:
57
packages/sdk/src/email.ts
Normal file
57
packages/sdk/src/email.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { renderAsync } from "@react-email/render";
|
||||
import * as React from "react";
|
||||
import { Unsend } from "./unsend";
|
||||
import { paths } from "../types/schema";
|
||||
import { ErrorResponse } from "../types";
|
||||
|
||||
type SendEmailPayload =
|
||||
paths["/v1/emails"]["post"]["requestBody"]["content"]["application/json"] & {
|
||||
react?: React.ReactElement;
|
||||
};
|
||||
|
||||
type CreateEmailResponse = {
|
||||
data: CreateEmailResponseSuccess | null;
|
||||
error: ErrorResponse | null;
|
||||
};
|
||||
|
||||
type CreateEmailResponseSuccess =
|
||||
paths["/v1/emails"]["post"]["responses"]["200"]["content"]["application/json"];
|
||||
|
||||
type GetEmailResponseSuccess =
|
||||
paths["/v1/emails/{emailId}"]["get"]["responses"]["200"]["content"]["application/json"];
|
||||
|
||||
type GetEmailResponse = {
|
||||
data: GetEmailResponseSuccess | null;
|
||||
error: ErrorResponse | null;
|
||||
};
|
||||
|
||||
export class Emails {
|
||||
constructor(private readonly unsend: Unsend) {
|
||||
this.unsend = unsend;
|
||||
}
|
||||
|
||||
async send(payload: SendEmailPayload) {
|
||||
return this.create(payload);
|
||||
}
|
||||
|
||||
async create(payload: SendEmailPayload): Promise<CreateEmailResponse> {
|
||||
if (payload.react) {
|
||||
payload.html = await renderAsync(payload.react as React.ReactElement);
|
||||
delete payload.react;
|
||||
}
|
||||
|
||||
const data = await this.unsend.post<CreateEmailResponseSuccess>(
|
||||
"/emails",
|
||||
payload
|
||||
);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async get(id: string): Promise<GetEmailResponse> {
|
||||
const data = await this.unsend.get<GetEmailResponseSuccess>(
|
||||
`/emails/${id}`
|
||||
);
|
||||
return data;
|
||||
}
|
||||
}
|
119
packages/sdk/src/unsend.ts
Normal file
119
packages/sdk/src/unsend.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { ErrorResponse } from "../types";
|
||||
import { Emails } from "./email";
|
||||
|
||||
const defaultBaseUrl = "https://app.unsend.dev";
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
const baseUrl = `${process?.env?.UNSEND_BASE_URL ?? defaultBaseUrl}/api/v1`;
|
||||
|
||||
function isUNSENDErrorResponse(error: { error: ErrorResponse }) {
|
||||
return error.error.code !== undefined;
|
||||
}
|
||||
|
||||
export class Unsend {
|
||||
private readonly headers: Headers;
|
||||
|
||||
// readonly domains = new Domains(this);
|
||||
readonly emails = new Emails(this);
|
||||
|
||||
constructor(readonly key?: string) {
|
||||
if (!key) {
|
||||
if (typeof process !== "undefined" && process.env) {
|
||||
this.key = process.env.UNSEND_API_KEY;
|
||||
}
|
||||
|
||||
if (!this.key) {
|
||||
throw new Error(
|
||||
'Missing API key. Pass it to the constructor `new Unsend("re_123")`'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.headers = new Headers({
|
||||
Authorization: `Bearer ${this.key}`,
|
||||
"Content-Type": "application/json",
|
||||
});
|
||||
}
|
||||
|
||||
async fetchRequest<T>(
|
||||
path: string,
|
||||
options = {}
|
||||
): Promise<{ data: T | null; error: ErrorResponse | null }> {
|
||||
const response = await fetch(`${baseUrl}${path}`, options);
|
||||
const defaultError = {
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: response.statusText,
|
||||
};
|
||||
|
||||
if (!response.ok) {
|
||||
try {
|
||||
const resp = await response.json();
|
||||
if (isUNSENDErrorResponse(resp)) {
|
||||
return { data: null, error: resp };
|
||||
}
|
||||
|
||||
return { data: null, error: resp.error };
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
return {
|
||||
data: null,
|
||||
error: defaultError,
|
||||
};
|
||||
}
|
||||
|
||||
return { data: null, error: defaultError };
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return { data, error: null };
|
||||
}
|
||||
|
||||
async post<T>(path: string, body: unknown) {
|
||||
const requestOptions = {
|
||||
method: "POST",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return this.fetchRequest<T>(path, requestOptions);
|
||||
}
|
||||
|
||||
async get<T>(path: string) {
|
||||
const requestOptions = {
|
||||
method: "GET",
|
||||
headers: this.headers,
|
||||
};
|
||||
|
||||
return this.fetchRequest<T>(path, requestOptions);
|
||||
}
|
||||
|
||||
async put<T>(path: string, body: any) {
|
||||
const requestOptions = {
|
||||
method: "PUT",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return this.fetchRequest<T>(path, requestOptions);
|
||||
}
|
||||
|
||||
async patch<T>(path: string, body: any) {
|
||||
const requestOptions = {
|
||||
method: "PATCH",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return this.fetchRequest<T>(path, requestOptions);
|
||||
}
|
||||
|
||||
async delete<T>(path: string, body?: unknown) {
|
||||
const requestOptions = {
|
||||
method: "DELETE",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return this.fetchRequest<T>(path, requestOptions);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user