Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix wrong inference of return value of withAxiomRouteHandler #162

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion examples/logger/app/api/dynamic/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AxiomRequest, withAxiom } from 'next-axiom';
import { withAxiom } from 'next-axiom';

export const runtime = 'edge';

Expand Down
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 = 'nodejs';

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