GQLoom

Executor

Sometimes we want to invoke resolver methods directly instead of initiating a full GraphQL query. In such cases, we can use the resolver().toExecutor() method to create an executor.

Basic Example

import { , , ,  } from "@gqloom/core"
import * as  from "valibot"

export const  = .({
  : .(.("Giraffe")),
  : .(),
  : .(),
  : .(),
})

export interface IGiraffe extends .<typeof > {}

const  = new <number, IGiraffe>([
  [1, { : 1, : "Spotty", : new ("2020-01-01") }],
  [2, { : 2, : "Longneck", : new ("2020-01-02") }],
])

export const  = .(, {
  : (.())
    .({ : .() })
    .(({  }) => .()),

  : (.()).(() =>
    .(.())
  ),

  : ()
    .(
      .({
        : .(),
        : .(.(), () => new ()),
      })
    )
    .(({ ,  }) => {
      const  = . + 1
      const  = { , ,  }
      .(, )
      return 
    }),
})

const  = .()
const  = .({ : "Aurora" })

.
  • createGiraffe
  • giraffe
  • giraffes

Context Injection

Both contexts created via the createContext method and memoized contexts created via the createMemoization method can have different values injected when creating an executor.

const giraffeExecutor = giraffeResolver.toExecutor(
  asyncContextProvider.with(useCurrentUser.provide({ id: 9, roles: ["admin"] }))
)

Unit Testing

Executors are well-suited for unit testing. Here's a simple unit test example:

import { giraffeResolver } from "./giraffe"
import { describe, it, expect } from "vitest"

describe("giraffeResolver", () => {
  const giraffeExecutor = giraffeResolver.toExecutor()
  it("should create a giraffe", async () => {
    const giraffe = await giraffeExecutor.createGiraffe({ name: "Aurora" })
    expect(giraffe).toBeDefined()
    expect(giraffe.name).toBe("Aurora")
  })

  it("should find giraffes", async () => {
    const giraffes = await giraffeExecutor.giraffes()
    expect(giraffes).toBeDefined()
    expect(giraffes.map((g) => g.name)).toContain("Aurora")
  })
})

Non-GraphQL Entry Points

In large-scale backend applications, there are often multiple entry points to invoke application logic. Besides GraphQL, common ones include message queues, gRPC, and scheduled tasks.

We can use executors at these other entry points to invoke application logic.
Here's an example of using an executor in a scheduled task:

import { giraffeResolver } from "../resolvers/giraffe"
import { schedule } from "node-cron"

// Create an executor instance
const giraffeExecutor = giraffeResolver.toExecutor()

// Schedule a task to run daily at 2 AM
schedule("0 2 * * *", async () => {
  try {
    // Fetch all giraffes
    const giraffes = await giraffeExecutor.giraffes()
    
    // Create a new entry for each giraffe
    for (const giraffe of giraffes) {
      await giraffeExecutor.createGiraffe({
        name: `${giraffe.name} Jr.`,
        birthDate: new Date()
      })
    }
    
    console.log("Scheduled task completed successfully")
  } catch (error) {
    console.error("Scheduled task failed:", error)
  }
})

On this page