Skip to content

Commit 4005dc8

Browse files
feat: make middleware an extensible function and change name to axiomMiddleware
1 parent 6b6ef2f commit 4005dc8

File tree

3 files changed

+53
-50
lines changed

3 files changed

+53
-50
lines changed

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { log, Logger, LogLevel, type LoggerConfig, type RequestReport } from './logger';
22
export { EndpointType, throttle } from './shared';
3-
export { middleware, config } from './middleware';
3+
export { axiomMiddleware } from './middleware';
44
export * from './platform/base';
55
export * from './config';
66
export { withAxiom, type AxiomRequest, withAxiomNextConfig, withAxiomRouteHandler } from './withAxiom';

src/middleware.ts

+47-44
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,55 @@
1-
import { NextResponse } from 'next/server';
2-
import type { NextFetchEvent, NextRequest } from 'next/server';
1+
import { NextMiddleware, NextResponse } from 'next/server';
32
import { config as axiomConfig } from './config';
43
import { EndpointType } from './shared';
54

65
const webVitalsEndpoint = axiomConfig.getIngestURL(EndpointType.webVitals);
76
const logsEndpoint = axiomConfig.getIngestURL(EndpointType.logs);
87

9-
const headers = {
10-
authorization: 'Bearer ' + axiomConfig.token,
11-
'Content-Type': 'application/json',
12-
};
13-
14-
export async function middleware(request: NextRequest, event: NextFetchEvent): Promise<NextResponse<unknown> | void> {
15-
// If the request is not for axiom, do nothing
16-
// This is a safety check, as users may add a custom matcher
17-
if (!request.nextUrl.pathname.startsWith('/_axiom')) return;
18-
19-
// Web vitals
20-
if (request.nextUrl.pathname.startsWith('/_axiom/web-vitals')) {
21-
// Forward the request to the axiom ingest endpoint
22-
event.waitUntil(
23-
fetch(webVitalsEndpoint, {
24-
body: request.body,
25-
method: 'POST',
26-
headers,
27-
}).catch(console.error)
28-
);
29-
30-
// Return a 204 to the client
31-
return new NextResponse(null, { status: 204 });
32-
}
33-
34-
// Logs
35-
if (request.nextUrl.pathname.startsWith('/_axiom/logs')) {
36-
// Forward the request to the axiom ingest endpoint
37-
event.waitUntil(
38-
fetch(logsEndpoint, {
8+
export const axiomMiddleware =
9+
(middleware?: NextMiddleware): NextMiddleware =>
10+
async (request, event) => {
11+
// If the request is not for axiom, do nothing
12+
// This is a safety check, as users may add a custom matcher
13+
if (!request.nextUrl.pathname.startsWith('/_axiom') || typeof axiomConfig.customEndpoint !== 'undefined') {
14+
return middleware ? middleware(request, event) : undefined;
15+
}
16+
17+
const headers = new Headers(request.headers);
18+
19+
// If we send the host and referrer headers, the request will fail
20+
headers.delete('host');
21+
headers.delete('referrer');
22+
23+
// Add the authorization header
24+
headers.set('Authorization', 'Bearer ' + axiomConfig.token);
25+
headers.set('Content-Type', 'application/json');
26+
27+
let endpoint: string | null = null;
28+
29+
if (request.nextUrl.pathname.startsWith('/_axiom/web-vitals')) {
30+
endpoint = webVitalsEndpoint;
31+
} else if (request.nextUrl.pathname.startsWith('/_axiom/logs')) {
32+
endpoint = logsEndpoint;
33+
}
34+
35+
// Web vitals
36+
if (endpoint) {
37+
const response = fetch(endpoint, {
3938
body: request.body,
4039
method: 'POST',
41-
headers,
42-
}).catch(console.error)
43-
);
44-
45-
// Return a 204 to the client
46-
return new NextResponse(null, { status: 204 });
47-
}
48-
}
49-
50-
export const config = {
51-
matcher: '/_axiom/:path*',
52-
};
40+
headers: headers,
41+
}).catch(console.error);
42+
if (typeof event.waitUntil !== 'undefined') {
43+
// Forward the request to the axiom ingest endpoint
44+
event.waitUntil(response);
45+
// Return a 204 to the client because we are not waiting for the response
46+
return new NextResponse(null, { status: 204 });
47+
} else {
48+
const res = await response;
49+
if (res) {
50+
return new NextResponse(res.body, { status: res.status });
51+
}
52+
return new NextResponse(null, { status: 500 });
53+
}
54+
}
55+
};

tests/middleware.test.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { NextFetchEvent, NextRequest, NextResponse } from 'next/server';
22
import { expect, test, vi, vitest } from 'vitest';
3-
import { middleware } from '../src/middleware';
3+
import { axiomMiddleware } from '../src/middleware';
44

55
vi.hoisted(() => {
66
// set axiom env vars before importing logger
@@ -12,7 +12,7 @@ test('middleware processes web vital requests', async () => {
1212
const context = {
1313
waitUntil: vitest.fn(),
1414
} as unknown as NextFetchEvent;
15-
const response = (await middleware(
15+
const response = (await axiomMiddleware()(
1616
new NextRequest('http://localhost/_axiom/web-vitals', {
1717
method: 'POST',
1818
}),
@@ -27,7 +27,7 @@ test('middleware processes logs requests', async () => {
2727
const context = {
2828
waitUntil: vitest.fn(),
2929
} as unknown as NextFetchEvent;
30-
const response = (await middleware(
30+
const response = (await axiomMiddleware()(
3131
new NextRequest('http://localhost/_axiom/logs', {
3232
method: 'POST',
3333
}),
@@ -42,7 +42,7 @@ test('middleware ignores non-axiom requests', async () => {
4242
const context = {
4343
waitUntil: vitest.fn(),
4444
} as unknown as NextFetchEvent;
45-
const response = await middleware(
45+
const response = await axiomMiddleware()(
4646
new NextRequest('http://localhost/', {
4747
method: 'GET',
4848
}),
@@ -56,7 +56,7 @@ test('all logs from the browser are sent to the server', async () => {
5656
const context = {
5757
waitUntil: vitest.fn(),
5858
} as unknown as NextFetchEvent;
59-
const response = (await middleware(
59+
const response = (await axiomMiddleware()(
6060
new NextRequest('http://localhost/_axiom/logs', {
6161
method: 'POST',
6262
body: JSON.stringify({ logs: ['hello, world!'] }),

0 commit comments

Comments
 (0)