Skip to content

Commit 835b0dc

Browse files
committed
feat(middleware): add light mode
1 parent 26f9629 commit 835b0dc

File tree

7 files changed

+91
-24
lines changed

7 files changed

+91
-24
lines changed

README.md

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ const middleware = createRedirectionIoMiddleware({
5858
// Optional: matcher to specify which routes should be ignored by redirection.io middleware
5959
// Default: "^/((?!api/|_next/|_static/|_vercel|[\\w-]+\\.\\w+).*)$"
6060
matcherRegex: "^/((?!api/|_next/|_static/|_vercel|[\\w-]+\\.\\w+).*)$",
61+
// Optional: If light, redirection.io middleware will only redirect and not override the response
62+
// Default: "full"
63+
mode: "light",
64+
// Optional: If true, redirection.io middleware will log information in Redirection.io
65+
// Default: true
66+
logged: true,
6167
});
6268

6369
export default middleware;
@@ -78,13 +84,30 @@ createRedirectionIoMiddleware({ matcherRegex: null });
7884

7985
Here's a summary of the middleware options:
8086

81-
| Option | Type | Description |
82-
| -------------------- | -------------- | ---------------------------------------------------------------------------- |
83-
| `previousMiddleware` | Function | Middleware to be executed before redirection.io middleware |
84-
| `nextMiddleware` | Function | Middleware to be executed after redirection.io middleware |
85-
| `matcherRegex` | String or null | Regex to specify which routes should be handled by redirection.io middleware |
87+
| Option | Type | Description |
88+
| -------------------- | ----------------- | -------------------------------------------------------------------------------------------------------- |
89+
| `previousMiddleware` | Function | Middleware to be executed before redirection.io middleware |
90+
| `nextMiddleware` | Function | Middleware to be executed after redirection.io middleware |
91+
| `matcherRegex` | String or null | Regex to specify which routes should be handled by redirection.io middleware |
92+
| `mode` | `full` or `light` | If `light`, redirection.io middleware will only redirect and not override the response (default: `full`) |
93+
| `logged` | Boolean | If true, redirection.io middleware will log information in Redirection.io (default: `true`) |
8694

87-
### Next.js
95+
## Light mode
96+
97+
The response rewriting features (e.g., SEO overrides, custom body, etc.) of redirection.io are currently not compatible with React Server Components (RSC). This is due to the fact that Vercel’s middleware implementation does not follow standard middleware protocols, requiring us to fetch requests, which is incompatible with both RSC and Vercel’s implementation.
98+
99+
However, we provide a light mode that supports RSC by offering only the redirection functionality. To enable this mode, simply set the `mode` option to `light`.
100+
101+
This allows you to implement redirection behavior without modifying response content, ensuring smooth operation with RSC.
102+
103+
```typescript
104+
const middleware = createRedirectionIoMiddleware({
105+
//
106+
mode: "light",
107+
});
108+
```
109+
110+
## Next.js
88111

89112
If you are using next.js middlewares, you can use the `createRedirectionIoMiddleware` method
90113
from `@redirection.io/vercel-middleware/next` which is compatible with `NextRequest` type.

middleware.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ type CreateMiddlewareConfig = {
55
previousMiddleware?: Middleware;
66
nextMiddleware?: Middleware;
77
matcherRegex?: string | null;
8+
mode?: "full" | "light";
9+
logged?: boolean;
810
};
911
export declare const createRedirectionIoMiddleware: (config: CreateMiddlewareConfig) => Middleware;
1012
declare const defaultMiddleware: Middleware;

middleware.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { next } from "@vercel/edge";
22
import { ipAddress } from "@vercel/functions";
33
import * as redirectionio from "@redirection.io/redirectionio";
4+
import { NextResponse } from "next/server";
45
const REDIRECTIONIO_TOKEN = process.env.REDIRECTIONIO_TOKEN || "";
56
const REDIRECTIONIO_INSTANCE_NAME = process.env.REDIRECTIONIO_INSTANCE_NAME || "redirection-io-vercel-middleware";
67
const REDIRECTIONIO_VERSION = "redirection-io-vercel-middleware/0.3.12";
@@ -10,6 +11,8 @@ const REDIRECTIONIO_ADD_HEADER_RULE_IDS = process.env.REDIRECTIONIO_ADD_HEADER_R
1011
const REDIRECTIONIO_TIMEOUT = process.env.REDIRECTIONIO_TIMEOUT ? parseInt(process.env.REDIRECTIONIO_TIMEOUT, 10) : 500;
1112
const DEFAULT_CONFIG = {
1213
matcherRegex: "^/((?!api/|_next/|_static/|_vercel|[\\w-]+\\.\\w+).*)$",
14+
mode: "full",
15+
logged: true,
1316
};
1417
export const createRedirectionIoMiddleware = (config) => {
1518
return async (request, context) => {
@@ -41,13 +44,17 @@ export const createRedirectionIoMiddleware = (config) => {
4144
}
4245
middlewareRequest = middlewareResponseToRequest(middlewareRequest, response, body);
4346
}
44-
return handler(middlewareRequest, context, async (request, useFetch) => {
47+
return handler(middlewareRequest, context, config, async (request, useFetch) => {
4548
let response = null;
4649
if (config.nextMiddleware) {
4750
response = await config.nextMiddleware(request, context);
4851
if (response.status !== 200) {
4952
return response;
5053
}
54+
// If light mode, only return the response
55+
if (config.mode === "light") {
56+
return response;
57+
}
5158
request = middlewareResponseToRequest(request, response, body);
5259
}
5360
if (!useFetch) {
@@ -71,7 +78,7 @@ export const createRedirectionIoMiddleware = (config) => {
7178
};
7279
const defaultMiddleware = createRedirectionIoMiddleware({});
7380
export default defaultMiddleware;
74-
async function handler(request, context, fetchResponse) {
81+
async function handler(request, context, config, fetchResponse) {
7582
if (!REDIRECTIONIO_TOKEN) {
7683
console.warn("No REDIRECTIONIO_TOKEN environment variable found. Skipping redirection.io middleware.");
7784
return fetchResponse(request, false);
@@ -87,14 +94,20 @@ async function handler(request, context, fetchResponse) {
8794
});
8895
const url = new URL(request.url);
8996
const location = response.headers.get("Location");
90-
if (location && location.startsWith("/")) {
97+
const hasLocation = location && location.startsWith("/");
98+
if (hasLocation) {
9199
response.headers.set("Location", url.origin + location);
92100
}
93-
context.waitUntil(
94-
(async function () {
95-
await log(response, backendStatusCode, redirectionIORequest, startTimestamp, action, ip);
96-
})(),
97-
);
101+
if (config.logged) {
102+
context.waitUntil(
103+
(async function () {
104+
await log(response, backendStatusCode, redirectionIORequest, startTimestamp, action, ip);
105+
})(),
106+
);
107+
}
108+
if (config.mode === "light" && hasLocation) {
109+
return NextResponse.redirect(url.origin + location, response.status);
110+
}
98111
return response;
99112
}
100113
function splitSetCookies(cookiesString) {

middleware.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { next, RequestContext } from "@vercel/edge";
22
import { ipAddress } from "@vercel/functions";
33
import * as redirectionio from "@redirection.io/redirectionio";
4-
import type { NextRequest } from "next/server";
4+
import { NextResponse, type NextRequest } from "next/server";
55

66
const REDIRECTIONIO_TOKEN = process.env.REDIRECTIONIO_TOKEN || "";
77
const REDIRECTIONIO_INSTANCE_NAME = process.env.REDIRECTIONIO_INSTANCE_NAME || "redirection-io-vercel-middleware";
@@ -13,7 +13,9 @@ const REDIRECTIONIO_TIMEOUT = process.env.REDIRECTIONIO_TIMEOUT ? parseInt(proce
1313

1414
const DEFAULT_CONFIG = {
1515
matcherRegex: "^/((?!api/|_next/|_static/|_vercel|[\\w-]+\\.\\w+).*)$",
16-
};
16+
mode: "full",
17+
logged: true,
18+
} as const;
1719

1820
type Middleware = (request: Request | NextRequest, context: RequestContext) => Response | Promise<Response>;
1921

@@ -23,6 +25,8 @@ type CreateMiddlewareConfig = {
2325
previousMiddleware?: Middleware;
2426
nextMiddleware?: Middleware;
2527
matcherRegex?: string | null;
28+
mode?: "full" | "light";
29+
logged?: boolean;
2630
};
2731

2832
export const createRedirectionIoMiddleware = (config: CreateMiddlewareConfig): Middleware => {
@@ -64,7 +68,7 @@ export const createRedirectionIoMiddleware = (config: CreateMiddlewareConfig): M
6468
middlewareRequest = middlewareResponseToRequest(middlewareRequest, response, body);
6569
}
6670

67-
return handler(middlewareRequest, context, async (request, useFetch): Promise<Response> => {
71+
return handler(middlewareRequest, context, config, async (request, useFetch): Promise<Response> => {
6872
let response: Response | null = null;
6973

7074
if (config.nextMiddleware) {
@@ -74,6 +78,11 @@ export const createRedirectionIoMiddleware = (config: CreateMiddlewareConfig): M
7478
return response;
7579
}
7680

81+
// If light mode, only return the response
82+
if (config.mode === "light") {
83+
return response;
84+
}
85+
7786
request = middlewareResponseToRequest(request, response, body);
7887
}
7988

@@ -105,7 +114,12 @@ const defaultMiddleware = createRedirectionIoMiddleware({});
105114

106115
export default defaultMiddleware;
107116

108-
async function handler(request: Request, context: RequestContext, fetchResponse: FetchResponse): Promise<Response> {
117+
async function handler(
118+
request: Request,
119+
context: RequestContext,
120+
config: CreateMiddlewareConfig,
121+
fetchResponse: FetchResponse,
122+
): Promise<Response> {
109123
if (!REDIRECTIONIO_TOKEN) {
110124
console.warn("No REDIRECTIONIO_TOKEN environment variable found. Skipping redirection.io middleware.");
111125

@@ -127,16 +141,23 @@ async function handler(request: Request, context: RequestContext, fetchResponse:
127141

128142
const url = new URL(request.url);
129143
const location = response.headers.get("Location");
144+
const hasLocation = location && location.startsWith("/");
130145

131-
if (location && location.startsWith("/")) {
146+
if (hasLocation) {
132147
response.headers.set("Location", url.origin + location);
133148
}
134149

135-
context.waitUntil(
136-
(async function () {
137-
await log(response, backendStatusCode, redirectionIORequest, startTimestamp, action, ip);
138-
})(),
139-
);
150+
if (config.logged) {
151+
context.waitUntil(
152+
(async function () {
153+
await log(response, backendStatusCode, redirectionIORequest, startTimestamp, action, ip);
154+
})(),
155+
);
156+
}
157+
158+
if (config.mode === "light" && hasLocation) {
159+
return NextResponse.redirect(url.origin + location, response.status);
160+
}
140161

141162
return response;
142163
}

next.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ type CreateMiddlewareConfig = {
44
previousMiddleware?: Middleware;
55
nextMiddleware?: Middleware;
66
matcherRegex?: string | null;
7+
mode?: "full" | "light";
8+
logged?: boolean;
79
};
810
export declare const createRedirectionIoMiddleware: (config: CreateMiddlewareConfig) => Middleware;
911
export {};

next.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export const createRedirectionIoMiddleware = (config) => {
2020
previousMiddleware,
2121
nextMiddleware,
2222
...(configMatcherRegex ? { matcherRegex: configMatcherRegex } : {}),
23+
mode: config.mode ?? "full",
24+
logged: config.logged ?? true,
2325
});
2426
return async (req, context) => {
2527
const response = await edgeMiddleware(req, context);

next.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ type CreateMiddlewareConfig = {
88
previousMiddleware?: Middleware;
99
nextMiddleware?: Middleware;
1010
matcherRegex?: string | null;
11+
mode?: "full" | "light";
12+
logged?: boolean;
1113
};
1214

1315
export const createRedirectionIoMiddleware = (config: CreateMiddlewareConfig): Middleware => {
@@ -34,6 +36,8 @@ export const createRedirectionIoMiddleware = (config: CreateMiddlewareConfig): M
3436
previousMiddleware,
3537
nextMiddleware,
3638
...(configMatcherRegex ? { matcherRegex: configMatcherRegex } : {}),
39+
mode: config.mode ?? "full",
40+
logged: config.logged ?? true,
3741
});
3842

3943
return async (req: NextRequest, context: NextFetchEvent) => {

0 commit comments

Comments
 (0)