feat: add onError and throw error options for APIError (#162)

This commit is contained in:
Bereket Engida
2024-10-13 21:34:09 +03:00
committed by GitHub
parent 8255a6ed5d
commit b145d78d6f
11 changed files with 103 additions and 46 deletions

View File

@@ -1,21 +0,0 @@
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { APIError } from "better-auth/api";
import { twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
database: prismaAdapter(
{},
{
provider: "mysql",
},
),
plugins: [twoFactor()],
});
try {
await auth.api.signOut();
} catch (e) {
if (e instanceof APIError) {
}
}

View File

@@ -0,0 +1,17 @@
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
baseURL: "http://localhost:4000",
database: prismaAdapter(
{},
{
provider: "sqlite",
},
),
emailAndPassword: {
enabled: true,
},
plugins: [twoFactor()],
});

15
dev/bun/client.ts Normal file
View File

@@ -0,0 +1,15 @@
import { createAuthClient } from "better-auth/client";
await fetch("http://localhost:4000/api/auth/sign-up/email", {
method: "POST",
body: JSON.stringify({
email: "test-2@test.com",
password: "password",
name: "test-2",
}),
headers: {
"content-type": "application/json",
},
})
.then((res) => res.json())
.then((data) => console.log(data));

View File

@@ -1,8 +1,7 @@
import { auth } from "./_auth";
import { auth } from "./auth";
Bun.serve({
fetch(request, server) {
auth;
return new Response("Hello, World!");
},
fetch: auth.handler,
port: 4000,
});
console.log("Server running on port 4000");

View File

@@ -644,8 +644,8 @@ export const contents: Content[] = [
icon: () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<g

View File

@@ -3,11 +3,11 @@ title: API
description: Better Auth API
---
When you create a new Better Auth instance, it gives you an `api` object. This object provides functions to interact with the server while your code is running server-side.
When you create a new Better Auth instance, it gives you an `api` object. This object provides functions to interact with the server while your code is running server-side. You can use these functions to call any API endpoint on the server.
Any endpoint added to Better Auth, whether from plugins or the core, will be accessible through the `api` object.
## Calling API on the Server
## Calling API Endpoints on the Server
To call an API endpoint on the server, import your `auth` instance and call the endpoint using the `api` object.
@@ -16,9 +16,7 @@ import { betterAuth } from "better-auth";
import { headers } from "next/server";
export const auth = betterAuth({
plugins: [
// add your plugins here
]
//...
})
// calling get session on the server
@@ -29,8 +27,18 @@ await auth.api.getSession({
Unlike the client, the server needs the values to be passed as an object with the key `body` for the body, `headers` for the headers, and `query` for the query.
```ts title="server.ts"
await auth.api.signInEmail({
body: {
email: "",
password: ""
}
})
```
<Callout>
Better Auth uses a library called [better-call](https://github.com/bekacru/better-call) to create API endpoints. Developed by the same team, it's built to integrate seamlessly with Better Auth. With Better Call, you can invoke `rest` API handlers as if they were regular functions.
Better auth API endpoints are built on top of [better-call](https://github.com/bekacru/better-call), a tiny web framework that lets you call REST API endpoints as if they were regular functions and allows us to easily infer client types from the server.
</Callout>
### Getting the `Response` Object
@@ -47,7 +55,7 @@ const response = await auth.api.signInEmail({
})
```
## Error Handling
### Error Handling
When you call an API endpoint in the server, it will throw an error if the request fails. You can catch the error and handle it as you see fit. The error instance is an instance of `APIError`.

View File

@@ -1,5 +0,0 @@
---
title: Secondary Storage
description: Using secondary storage with BetterAuth
---

View File

@@ -250,6 +250,23 @@ list of trusted origins. This will disable CSRF token check for the provided ori
/>
### `onAPIError`
`OnAPIErrorOptions` - Options for handling API errors.
<TypeTable
type={{
throw: {
description: "Throw the error.",
type: 'boolean',
default: false
},
onError: {
description: "Custom error handler.",
type: '(error: Error, ctx: APIContext) => void'
}
}}
/>
### `logger`

View File

@@ -7,6 +7,7 @@
"scripts": {
"build": "turbo --filter \"./packages/*\" build",
"dev": "turbo --filter \"./packages/*\" dev",
"dev:dts": "turbo --filter \"./packages/*\" dev:dts",
"clean": "turbo --filter \"./packages/*\" clean && rm -rf node_modules",
"format": "biome format . --write",
"lint": "biome check .",

View File

@@ -219,6 +219,14 @@ export const router = <C extends AuthContext, Option extends BetterAuthOptions>(
return res;
},
onError(e) {
if (options.onAPIError?.throw) {
throw e;
}
if (options.onAPIError?.onError) {
options.onAPIError.onError(e, ctx);
return;
}
const log = options.logger?.verboseLogging ? logger : undefined;
if (options.logger?.disabled !== true) {
if (e instanceof APIError) {
@@ -230,7 +238,7 @@ export const router = <C extends AuthContext, Option extends BetterAuthOptions>(
if (typeof e === "object" && e !== null && "message" in e) {
const errorMessage = e.message as string;
if (!errorMessage || typeof errorMessage !== "string") {
log?.error(e);
logger?.error(e);
return;
}
if (errorMessage.includes("no such table")) {
@@ -258,10 +266,10 @@ export const router = <C extends AuthContext, Option extends BetterAuthOptions>(
)} to create the tables. There are missing tables in your MySQL database.`,
);
} else {
log?.error(e);
logger?.error(e);
}
} else {
log?.error(e);
logger?.error(e);
}
}
}

View File

@@ -8,7 +8,7 @@ import type { BetterSqlite3Database, MysqlPool } from "./database";
import type { KyselyDatabaseType } from "../adapters/kysely-adapter/types";
import type { FieldAttribute } from "../db";
import type { RateLimit } from "./models";
import type { EligibleCookies } from "../cookies";
import type { AuthContext } from ".";
export interface BetterAuthOptions {
/**
@@ -436,4 +436,22 @@ export interface BetterAuthOptions {
};
};
};
/**
* API error handling
*/
onAPIError?: {
/**
* Throw an error on API error
*
* @default false
*/
throw?: boolean;
/**
* Custom error handler
*
* @param error
* @param ctx - Auth context
*/
onError?: (error: unknown, ctx: AuthContext) => void | Promise<void>;
};
}