Last active
June 10, 2025 15:08
-
-
Save staciax/f764730328cc1105d2a388243dc5995c to your computer and use it in GitHub Desktop.
ElysiaJS with background tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Background Tasks Implementation | |
* Inspired by Starlette's background task processing | |
* @see https://github.com/encode/starlette/blob/master/starlette/background.py | |
*/ | |
import { Elysia } from 'elysia'; | |
export interface IBackgroundTask { | |
run(): Promise<void>; | |
} | |
const isAsyncFunction = <P extends any[]>( | |
func: (...args: P[]) => any, | |
): boolean => func.constructor.name === 'AsyncFunction'; | |
type TaskFunction<P extends any[]> = (...args: P) => void | Promise<void>; | |
export class BackgroundTask<P extends any[]> implements IBackgroundTask { | |
public readonly func: TaskFunction<P>; | |
public readonly args: P; | |
public readonly isAsync: boolean; | |
constructor(func: TaskFunction<P>, ...args: P) { | |
this.func = func; | |
this.args = args; | |
this.isAsync = isAsyncFunction(func); | |
} | |
async run(): Promise<void> { | |
if (this.isAsync) { | |
await this.func(...this.args); | |
} else { | |
throw new Error( | |
'Background task does not support synchronous functions. Please use async functions.', | |
); | |
// NOTE: I'm not sure how to run a synchronous function in the background | |
// without blocking the main thread. I tried using The Worker API but it | |
// doesn't seem to work with synchronous functions. | |
// Reference: https://bun.sh/docs/api/workers | |
} | |
} | |
} | |
export class BackgroundTasks implements IBackgroundTask { | |
public readonly tasks: BackgroundTask<any[]>[]; | |
constructor(tasks: BackgroundTask<any[]>[] = []) { | |
this.tasks = tasks; | |
// this.tasks = [...tasks]; // clone the array to avoid mutation | |
} | |
addTask<P extends any[]>(func: TaskFunction<P>, ...args: P): void { | |
const task = new BackgroundTask(func, ...args); | |
this.tasks.push(task); | |
} | |
async run(): Promise<void> { | |
// If you want to run all tasks concurrently, you can use Promise.all | |
// await Promise.all(this.tasks.map((task) => task.run())); | |
// Otherwise, run them sequentially | |
for (const task of this.tasks) { | |
await task.run(); | |
} | |
} | |
} | |
export const backgroundTasksPlugin = new Elysia({ name: 'background-tasks' }) | |
.derive(() => ({ | |
backgroundTasks: new BackgroundTasks(), | |
})) | |
.onAfterResponse(({ backgroundTasks }) => { | |
backgroundTasks.run().catch((e) => { | |
const error = e instanceof Error ? e.stack : e; | |
const now = new Date().toISOString(); | |
console.error(`background-tasks: ${now} - ${error}`); | |
}); | |
}) | |
.as('scoped'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Elysia } from 'elysia'; | |
import { backgroundTasksPlugin } from './background'; | |
async function test_async_task() { | |
await new Promise((resolve) => setTimeout(resolve, 3000)); // Simulate a 3-second async task | |
console.log('Async task completed'); | |
} | |
const app = new Elysia() | |
.use(backgroundTasksPlugin) | |
.get('/', ({ backgroundTasks }) => { | |
backgroundTasks.addTask(test_async_task); | |
backgroundTasks.addTask(test_async_task); | |
return 'task initiated'; | |
}); | |
app.listen(3000, () => { | |
console.log('Server is running on http://localhost:3000'); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I make library now: https://github.com/staciax/elysia-background