feat: turbostarter boilerplate
Production-ready Next.js boilerplate with: - Runtime env validation (fail-fast on missing vars) - Feature-gated config (S3, Stripe, email, OAuth) - Docker + Coolify deployment pipeline - PostgreSQL + pgvector, MinIO S3, Better Auth - TypeScript strict mode (no ignoreBuildErrors) - i18n (en/es), AI modules, billing, monitoring Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
76
packages/api/src/utils/on-error.ts
Normal file
76
packages/api/src/utils/on-error.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import * as z from "zod";
|
||||
|
||||
import { isKey } from "@turbostarter/i18n";
|
||||
import { getTranslation } from "@turbostarter/i18n/server";
|
||||
import { captureException } from "@turbostarter/monitoring-web/server";
|
||||
import { HttpStatusCode } from "@turbostarter/shared/constants";
|
||||
import { logger } from "@turbostarter/shared/logger";
|
||||
import { getStatusCode } from "@turbostarter/shared/utils";
|
||||
|
||||
import type { Context } from "hono";
|
||||
|
||||
const errorSchema = z.object({
|
||||
code: z.string().optional(),
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
const isError = (e: unknown): e is z.infer<typeof errorSchema> => {
|
||||
return errorSchema.safeParse(e).success;
|
||||
};
|
||||
|
||||
export const onError = async (
|
||||
e: unknown,
|
||||
c?: Context<{
|
||||
Bindings: { NODE_ENV: string };
|
||||
Variables: { locale: string };
|
||||
}>,
|
||||
) => {
|
||||
const { t, i18n } = await getTranslation({
|
||||
locale: c?.var.locale,
|
||||
request: c?.req.raw,
|
||||
});
|
||||
|
||||
const status = getStatusCode(e);
|
||||
const details = {
|
||||
status,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
};
|
||||
|
||||
const timestamp = new Date().toISOString();
|
||||
const path = c?.req.raw.url ? new URL(c.req.raw.url).pathname : "/api";
|
||||
|
||||
if (status >= HttpStatusCode.INTERNAL_SERVER_ERROR) {
|
||||
captureException(e, { path, status, timestamp });
|
||||
}
|
||||
|
||||
if (isError(e)) {
|
||||
logger.error(e.code, e.message);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
code: e.code,
|
||||
message: e.message
|
||||
? e.message
|
||||
: e.code && isKey(e.code, i18n)
|
||||
? t(e.code)
|
||||
: ((e.message || e.code) ?? t("common:error.general")),
|
||||
status,
|
||||
timestamp,
|
||||
path,
|
||||
}),
|
||||
details,
|
||||
);
|
||||
}
|
||||
|
||||
logger.error(e);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
code: "common:error.general",
|
||||
message: t("common:error.general"),
|
||||
status,
|
||||
path,
|
||||
}),
|
||||
details,
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user