Skip to content

Commit

Permalink
perf: improve fetch response types (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
keyding authored Apr 19, 2024
1 parent 218d2ef commit 13c5ff0
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/rotten-lions-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lemonsqueezy/lemonsqueezy.js": patch
---

Improve API response types.
8 changes: 7 additions & 1 deletion examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ lemonSqueezySetup({

// Get authenticated user
const { data, error, statusCode } = await getAuthenticatedUser();
console.log({ data, error, statusCode });

if (error) {
console.log(error.cause);
} else {
console.log({ data, error, statusCode });
console.log(data.data.attributes.email);
}
59 changes: 41 additions & 18 deletions src/internal/fetch/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { Config } from "../setup/types";
import { API_BASE_URL, CONFIG_KEY, getKV } from "../utils";
import type { FetchOptions, FetchResponse } from "./types";
import type {
FetchOptions,
FetchResponse,
FetchDataWithError,
JSONAPIError,
} from "./types";

/**
* Internal customization of fetch.
Expand All @@ -11,21 +16,21 @@ import type { FetchOptions, FetchResponse } from "./types";
* @returns Fetch response. Includes `statusCode`, `error` and `data`.
*/
export async function $fetch<T>(options: FetchOptions, needApiKey = true) {
const response: FetchResponse<T> = {
const response = {
statusCode: null,
data: null,
error: null,
error: Error(),
};
const { apiKey, onError } = getKV<Config>(CONFIG_KEY) || {};

try {
if (needApiKey && !apiKey) {
response.error = Error(
response.error = createLemonError(
"Please provide your Lemon Squeezy API key. Create a new API key: https://app.lemonsqueezy.com/settings/api",
{ cause: "Missing API key" }
"Missing API key"
);
onError?.(response.error);
return response;
return response as FetchResponse<T>;
}

const { path, method = "GET", query, body } = options;
Expand Down Expand Up @@ -55,22 +60,40 @@ export async function $fetch<T>(options: FetchOptions, needApiKey = true) {
}

const fetchResponse = await fetch(url.href, _options);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data = (await fetchResponse.json()) as { error?: any; errors?: any };
const data = await fetchResponse.json();
const fetchOk = fetchResponse.ok;
const fetchStatus = fetchResponse.status;

Object.assign(response, {
statusCode: fetchResponse.status,
// The license api returns data in the event of an error
data: fetchOk ? data : data.error ? data : null,
error: fetchOk
? null
: Error(fetchResponse.statusText, { cause: data.errors || data.error }),
});
if (fetchOk) {
Object.assign(response, {
statusCode: fetchStatus,
data: data as T,
error: null,
});
} else {
const { errors, error, message } = data as FetchDataWithError;
const _error = errors || error || message || "unknown cause";

Object.assign(response, {
statusCode: fetchStatus,
data: null,
error: createLemonError(fetchResponse.statusText, _error),
});
}
} catch (error) {
response.error = error as Error;
Object.assign(response, { error: error as Error });
}

response.error && onError?.(response.error);
return response;
return response as FetchResponse<T>;
}

function createLemonError(
message: string,
cause: string | JSONAPIError[] = "unknown"
) {
const error = new Error(message);
error.name = "Lemon Squeezy Error";
error.cause = cause;
return error;
}
37 changes: 33 additions & 4 deletions src/internal/fetch/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
type ApiVersion = "v1";

export type FetchResponse<T> = {
statusCode: number | null;
data: T | null;
error: Error | null;
export type JSONAPIError = {
id?: string;
links?: {
about?: string;
type?: string;
};
status?: string;
code?: string;
title: string;
detail?: string;
source?: {
pointer?: string;
parameter?: string;
};
meta?: Record<string, any>;
};

export type FetchResponse<T> =
| {
statusCode: number;
data: T;
error: null;
}
| {
statusCode: number | null;
data: null;
error: Error;
};

export type FetchDataWithError = {
errors?: JSONAPIError[];
error?: string;
message?: string;
};

export type FetchOptions = {
Expand Down
5 changes: 3 additions & 2 deletions test/checkouts/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ beforeAll(async () => {
lemonSqueezySetup({
apiKey: import.meta.env.LEMON_SQUEEZY_API_KEY,
});
const { data } = await listVariants();
variantId = data!.data[0].id;
const { error, data } = await listVariants();
if (error) return;
variantId = data.data[0].id;
});

describe("Create a checkout", () => {
Expand Down

0 comments on commit 13c5ff0

Please sign in to comment.