Skip to content

Commit

Permalink
feat: add method to execute code after request cycle
Browse files Browse the repository at this point in the history
  • Loading branch information
Ernxst committed Jul 3, 2024
1 parent 86eb952 commit 1ca2619
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/proud-peaches-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@galaxiajs/cloudflare-kit": patch
---

Add method to execute code after request handling
2 changes: 1 addition & 1 deletion packages/cloudflare/src/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export {
type $Locals as Locals,
type $Secrets as Secrets,
} from "./modules/env";
export { executionContext, waitUntil } from "./modules/execution-context";
export { executionContext, waitUntil, after } from "./modules/execution-context";
export {
headers,
ip,
Expand Down
20 changes: 15 additions & 5 deletions packages/cloudflare/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "./modules/context/context";
import { createContext, createLocals } from "./modules/context/utils";
import type { Env } from "./modules/env";
import { afters } from "./modules/execution-context";
import type { ExportedWorker, Handler, MakeAsync, Promisable } from "./types";

/**
Expand Down Expand Up @@ -77,12 +78,14 @@ export function handleRequest(ctx: IHandlerContext, handle: () => Promisable<Res
const injected = await createLocals();
return await LocalsContext.run(injected, async () => {
const result = await handle();
let response: Response;

if (result instanceof Response) {
for (const [key, value] of result.headers.entries()) {
ctx.response.headers.append(key, value);
}

return new Response(result.body, {
response = new Response(result.body, {
statusText: result.statusText,
status: result.status,
headers: ctx.response.headers as unknown as Headers,
Expand All @@ -93,12 +96,19 @@ export function handleRequest(ctx: IHandlerContext, handle: () => Promisable<Res
// @ts-expect-error is a CF only type
encodeBody: result.encodeBody,
});
} else {
response = createResponse(result, {
status: ctx.response.status,
headers: ctx.response.headers as unknown as Headers,
});
}

return createResponse(result, {
status: ctx.response.status,
headers: ctx.response.headers as unknown as Headers,
});
ctx.executionContext.waitUntil(handleAfter());
return response;
});
});
}

async function handleAfter() {
await Promise.all(afters.map((r) => r()));
}
29 changes: 29 additions & 0 deletions packages/cloudflare/src/modules/execution-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,32 @@ export function waitUntil(promise: Promise<any>): void {
const ctx = executionContext();
ctx.waitUntil(promise);
}

export const afters: Array<() => any> = [];

/**
* Allows you to schedule work to be executed after a response is finished.
* This is useful for tasks and other side effects that should not block
* the response, such as logging and analytics.
*
* Unlike {@linkcode waitUntil}, this will run _after_ the response has turned,
* rather than being kicked off concurrently during the request cycle.
*
* `after()` will be executed even if the response didn't complete successfully,
* including when an error is thrown or when `redirect()` is called.
*
* Note that you cannot:
*
* - Set cookies
* - Modify response headers
* - Modify response status
*
* Inside `after()` as the response will have already been sent. Doing any of these
* will throw a Cloudflare error.
*
* @param {() => any} callback A function that will be executed after the response is finished.
* @returns {void}
*/
export function after(callback: () => any): void {
afters.push(callback);
}

0 comments on commit 1ca2619

Please sign in to comment.