Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ Wrapping your Route Handlers in `withAxiom` will add a logger to your
request and automatically log exceptions:

```typescript
import { withAxiom, AxiomRequest } from 'next-axiom';
import { withAxiom } from 'next-axiom';

export const GET = withAxiom((req: AxiomRequest) => {
export const GET = withAxiom((req) => {
req.log.info('Login function called');

// You can create intermediate loggers
Expand Down
4 changes: 2 additions & 2 deletions examples/logger/app/api/dynamic/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { AxiomRequest, withAxiom } from 'next-axiom';
import { withAxiom } from 'next-axiom';

export const runtime = 'edge';

export const POST = withAxiom((req: AxiomRequest, { params }: { params: { id: string}}) => {
export const POST = withAxiom((req, { params }: { params: { id: string}}) => {
req.log.info('axiom dynamic route');
return new Response(`Hello, Next.js! ${params.id}`);
})
4 changes: 2 additions & 2 deletions examples/logger/app/api/edge/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { AxiomRequest, withAxiom } from 'next-axiom';
import { withAxiom } from 'next-axiom';

export const runtime = 'edge';

export const GET = withAxiom(async (req: AxiomRequest) => {
export const GET = withAxiom(async (req) => {
req.log.info('fired from edge route');
return new Response('Hello, Next.js!');
});
4 changes: 2 additions & 2 deletions examples/logger/app/api/lambda/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { AxiomRequest, withAxiom } from 'next-axiom';
import { withAxiom } from 'next-axiom';

export const runtime = 'edge';

export const GET = withAxiom(async (req: AxiomRequest) => {
export const GET = withAxiom(async (req) => {
req.log.info('axiom lambda route');
return new Response('Hello, Next.js!');
});
43 changes: 24 additions & 19 deletions src/withAxiom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { Logger, RequestReport } from './logger';
import { type NextRequest, type NextResponse } from 'next/server';
import { EndpointType } from './shared';

export function withAxiomNextConfig(nextConfig: NextConfig): NextConfig {
export function withAxiomNextConfig<T extends NextConfig>(
nextConfig: T
): Omit<T, 'rewrites'> & Pick<NextConfig, 'rewrites'> {
return {
...nextConfig,
rewrites: async () => {
Expand Down Expand Up @@ -49,13 +51,13 @@ export function withAxiomNextConfig(nextConfig: NextConfig): NextConfig {
}

export type AxiomRequest = NextRequest & { log: Logger };
type NextHandler<T = any> = (
req: AxiomRequest,
arg?: T
) => Promise<Response> | Promise<NextResponse> | NextResponse | Response;
type AxiomHandler<
Args = any,
Res extends Response | NextResponse = Response | NextResponse
> = (req: AxiomRequest, arg?: Args) => Promise<Res> | Res;

export function withAxiomRouteHandler(handler: NextHandler): NextHandler {
return async (req: Request | NextRequest, arg: any) => {
export function withAxiomRouteHandler<T extends AxiomHandler>(handler: T): T {
return (async (req, arg) => {
let region = '';
if ('geo' in req) {
region = req.geo?.region ?? '';
Expand All @@ -72,13 +74,14 @@ export function withAxiomRouteHandler(handler: NextHandler): NextHandler {
region,
};

const logger = new Logger({ req: report, source: isEdgeRuntime ? 'edge' : 'lambda' });
const axiomContext = req as AxiomRequest;
const args = arg;
axiomContext.log = logger;
const logger = new Logger({
req: report,
source: isEdgeRuntime ? 'edge' : 'lambda',
});
req.log = logger;

try {
const result = await handler(axiomContext, args);
const result = await handler(req, arg);
await logger.flush();
if (isEdgeRuntime) {
logEdgeReport(report);
Expand All @@ -93,24 +96,26 @@ export function withAxiomRouteHandler(handler: NextHandler): NextHandler {
}
throw error;
}
};
}) as T;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the unsafe cast I mean.

If you do this, which would be the proper way, there is a type error I can't fix:

export function withAxiomRouteHandler<T extends AxiomHandler>(handler: T): T {
  const result: T = async (req, arg) => { ... }
  return result
}
Type '(req: AxiomRequest, arg: any) => Promise<Response | NextResponse<unknown>>' is not assignable to type 'T'.
  '(req: AxiomRequest, arg: any) => Promise<Response | NextResponse<unknown>>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'AxiomHandler<any, Response | NextResponse<unknown>>'.ts(2322)

}

function logEdgeReport(report: RequestReport) {
console.log(`AXIOM_EDGE_REPORT::${JSON.stringify(report)}`);
}

type WithAxiomParam = NextConfig | NextHandler;

function isNextConfig(param: WithAxiomParam): param is NextConfig {
function isNextConfig(param: NextConfig | AxiomHandler): param is NextConfig {
return typeof param == 'object';
}

// withAxiom can be called either with NextConfig, which will add proxy rewrites
// to improve deliverability of Web-Vitals and logs.
export function withAxiom(param: NextHandler): NextHandler;
export function withAxiom(param: NextConfig): NextConfig;
export function withAxiom(param: WithAxiomParam) {
export function withAxiom<T extends AxiomHandler>(
param: T
): ReturnType<typeof withAxiomRouteHandler<T>>;
export function withAxiom<T extends NextConfig>(
param: T
): ReturnType<typeof withAxiomNextConfig<T>>;
export function withAxiom<T extends NextConfig | AxiomHandler>(param: T) {
if (typeof param == 'function') {
return withAxiomRouteHandler(param);
} else if (isNextConfig(param)) {
Expand Down