# Create Reusable Tasks

import { Aside, Steps, CardGrid, LinkCard } from "@astrojs/starlight/components";

To create task functions that can be used across multiple flows without tight coupling to specific flow structures.


## Steps

<Steps>

1. **Define task parameters**

   Identify exactly what data the task needs to function. Accept specific parameters, not entire input objects:

   ```typescript add="userId: string" del="input: { user: { id: string }"
   // ✅ Accept specific parameters
   async function fetchUserProfile(userId: string) {
     const response = await fetch(`https://api.example.com/users/${userId}`);
     return response.json();
   }

   // ❌ Don't accept entire input object
   async function fetchUserProfile(input: { user: { id: string }) {
     const response = await fetch(`https://api.example.com/users/${input.user.id}`);
     return response.json();
   }
   ```

2. **Return JSON-serializable data**

   Return primitives, plain objects, or arrays. Convert dates to ISO strings:

   ```typescript ".toISOString()"
   async function fetchUserProfile(userId: string) {
     const response = await fetch(`https://api.example.com/users/${userId}`);
     const user = await response.json();
     return {
       id: user.id,
       name: user.name,
       email: user.email,
       createdAt: new Date(user.created_at).toISOString() // Convert to ISO string
     };
   }
   ```

3. **Use step handlers as adapters**

   Let the flow's step handler extract needed data from the flow context:

   ```typescript "input.run.userId"
   // Your task function
   async function fetchUserProfile(userId: string) {
     const response = await fetch(`https://api.example.com/users/${userId}`);
     return response.json();
   }

   // Flow uses handler to adapt context to task parameters
   new Flow<{ userId: string }>({ slug: 'user_flow' })
     .step({ slug: 'profile' }, async (input) =>
       await fetchUserProfile(input.run.userId)
     )
   ```

4. **Test in isolation**

   Verify the task works independently of any flow:

   ```typescript
   // tasks/fetchUserProfile.test.ts
   import { assertEquals } from "jsr:@std/assert";
   import { fetchUserProfile } from "./fetchUserProfile.ts";

   Deno.test("fetches user profile", async () => {
     const result = await fetchUserProfile("user_123", "test-key");

     assertEquals(result.id, "user_123");
     assertEquals(result.name, "Alice Johnson");
     assertEquals(result.email, "alice@example.com");
     assertEquals(result.createdAt, "2024-01-15T10:30:00.000Z");
   });
   ```

</Steps>

## Learn More

<CardGrid>
  <LinkCard
    title="Organize Flow Code"
    href="/build/organize-flow-code/"
    description="Learn where to put your task files and structure your codebase"
  />
  <LinkCard
    title="Context Object"
    href="/concepts/context-object/"
    description="Understanding the context parameter available to handlers"
  />
</CardGrid>

## See it in Action

<CardGrid>
  <LinkCard
    title="AI Web Scraper Tutorial"
    href="/tutorials/ai-web-scraper/"
    description="Complete example showing reusable tasks in a real-world flow"
  />
</CardGrid>