Skip to content

Commit

Permalink
fix: throw proper error if import handler paths are not able to be im…
Browse files Browse the repository at this point in the history
…ported, improve import handler path docs (#9679)

Fixes #9453
  • Loading branch information
AlessioGr authored Dec 2, 2024
1 parent a89d544 commit 0dbfc23
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 10 deletions.
4 changes: 3 additions & 1 deletion docs/jobs-queue/tasks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Simply add a task to the `jobs.tasks` array in your Payload config. A task consi
| Option | Description |
| --------------------------- | -------------------------------------------------------------------------------- |
| `slug` | Define a slug-based name for this job. This slug needs to be unique among both tasks and workflows.|
| `handler` | The function that should be responsible for running the job. You can either pass a string-based path to the job function file, or the job function itself. If you are using large dependencies within your job, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. |
| `handler` | The function that should be responsible for running the job. You can either pass a string-based path to the job function file, or the job function itself. If you are using large dependencies within your job, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. Passing a string path is an advanced feature that may require a sophisticated build pipeline in order to work. |
| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |
| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this task. By default, this is "Task" + the capitalized task slug. |
| `outputSchema` | Define the output field schema - payload will generate a type for this schema. |
Expand Down Expand Up @@ -93,6 +93,8 @@ export default buildConfig({

In addition to defining handlers as functions directly provided to your Payload config, you can also pass an _absolute path_ to where the handler is defined. If your task has large dependencies, and you are planning on executing your jobs in a separate process that has access to the filesystem, this could be a handy way to make sure that your Payload + Next.js app remains quick to compile and has minimal dependencies.

Keep in mind that this is an advanced feature that may require a sophisticated build pipeline, especially when using it in production or within Next.js, e.g. by calling opening the `/api/payload-jobs/run` endpoint. You will have to transpile the handler files separately and ensure they are available in the same location when the job is run. If you're using an endpoint to execute your jobs, it's recommended to define your handlers as functions directly in your Payload Config, or use import paths handlers outside of Next.js.

In general, this is an advanced use case. Here's how this would look:

`payload.config.ts:`
Expand Down
2 changes: 1 addition & 1 deletion docs/jobs-queue/workflows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ To define a JS-based workflow, simply add a workflow to the `jobs.wokflows` arra
| Option | Description |
| --------------------------- | -------------------------------------------------------------------------------- |
| `slug` | Define a slug-based name for this workflow. This slug needs to be unique among both tasks and workflows.|
| `handler` | The function that should be responsible for running the workflow. You can either pass a string-based path to the workflow function file, or workflow job function itself. If you are using large dependencies within your workflow, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. |
| `handler` | The function that should be responsible for running the workflow. You can either pass a string-based path to the workflow function file, or workflow job function itself. If you are using large dependencies within your workflow, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. Passing a string path is an advanced feature that may require a sophisticated build pipeline in order to work. |
| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |
| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this workflow. By default, this is "Workflow" + the capitalized workflow slug. |
| `label` | Define a human-friendly label for this workflow. |
Expand Down
3 changes: 2 additions & 1 deletion packages/payload/src/queues/config/types/taskTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ export type TaskConfig<
* You can either pass a string-based path to the job function file, or the job function itself.
*
* If you are using large dependencies within your job, you might prefer to pass the string path
* because that will avoid bundling large dependencies in your Next.js app.
* because that will avoid bundling large dependencies in your Next.js app. Passing a string path is an advanced feature
* that may require a sophisticated build pipeline in order to work.
*/
handler: string | TaskHandler<TTaskSlugOrInputOutput>
/**
Expand Down
5 changes: 2 additions & 3 deletions packages/payload/src/queues/config/types/workflowTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,8 @@ export type WorkflowConfig<TWorkflowSlugOrInput extends keyof TypedJobs['workflo
* You can either pass a string-based path to the workflow function file, or the workflow function itself.
*
* If you are using large dependencies within your workflow control flow, you might prefer to pass the string path
* because that will avoid bundling large dependencies in your Next.js app.
*
*
* because that will avoid bundling large dependencies in your Next.js app. Passing a string path is an advanced feature
* that may require a sophisticated build pipeline in order to work.
*/
handler:
| string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@ export async function importHandlerPath<T>(path: string): Promise<T> {
let runner: T
const [runnerPath, runnerImportName] = path.split('#')

const runnerModule =
typeof require === 'function'
? await eval(`require('${runnerPath.replaceAll('\\', '/')}')`)
: await eval(`import('${pathToFileURL(runnerPath).href}')`)
let runnerModule
try {
// We need to check for `require` for compatibility with outdated frameworks that do not
// properly support ESM, like Jest. This is not done to support projects without "type": "module" set
runnerModule =
typeof require === 'function'
? await eval(`require('${runnerPath.replaceAll('\\', '/')}')`)
: await eval(`import('${pathToFileURL(runnerPath).href}')`)
} catch (e) {
throw new Error(
`Error importing job queue handler module for path ${path}. This is an advanced feature that may require a sophisticated build pipeline, especially when using it in production or within Next.js, e.g. by calling opening the /api/payload-jobs/run endpoint. You will have to transpile the handler files separately and ensure they are available in the same location when the job is run. If you're using an endpoint to execute your jobs, it's recommended to define your handlers as functions directly in your Payload Config, or use import paths handlers outside of Next.js. Import Error: \n${e.message}`,
)
}

// If the path has indicated an #exportName, try to get it
if (runnerImportName && runnerModule[runnerImportName]) {
Expand Down

0 comments on commit 0dbfc23

Please sign in to comment.