# Fetching Composers

A composer is a document template that combines static HTML segments with prompt references. `getComposer()` fetches a composer, interpolates your variables, and resolves AI models. Then `compose()` runs every prompt in parallel and assembles the final output — all in one line.

## Quick example

```typescript
import { createPromptlyClient } from '@promptlycms/prompts';
import { generateText } from 'ai';

const promptly = createPromptlyClient();

const { compose } = await promptly.getComposer('article-composer', {
  input: { topic: 'TypeScript', audience: 'developers' },
});

// One line — runs all prompts in parallel and assembles the output
const output = await compose(generateText);
```

## How it works

1. **Fetch** — `getComposer()` calls the Promptly API and gets the composer's segment array
2. **Interpolate** — Template variables (`${var}` in prompts, `{{var}}` in static HTML) are replaced with your `input` values
3. **Resolve models** — Each prompt segment's model is resolved to an AI SDK `LanguageModel`
4. **Compose** — `compose()` passes each prompt to your generate function, runs them in parallel via `Promise.all`, and assembles the static HTML + AI text into a single string

Composer output preserves authoring line breaks by default. Empty rich-text paragraphs from static segments stay visible, text prompt output placed in its own rich-text paragraph keeps a visible email-safe paragraph gap, and newlines in generated text are emitted as `<br>` tags. Raw `html_block` segments remain opaque except for variable interpolation.

## Per-prompt overrides

Pass a wrapper function to customise parameters for every prompt:

```typescript
const output = await compose((prompt) =>
  generateText({ ...prompt, maxTokens: 500 })
);
```

The `prompt` argument has the shape `{ model, system, prompt, temperature, promptId, promptName }`, so you can spread it and override individual fields.

## Accessing metadata

```typescript
const composer = await promptly.getComposer('my-composer', {
  input: { text: 'Hello world', targetLang: 'French' },
});

composer.composerId;   // 'my-composer'
composer.composerName; // 'My Composer'
composer.version;      // '1.0.0'
composer.config;       // { schema, inputData, inputDataRootName }
composer.segments;     // raw segment array from the API
```

## Version pinning

By default, `getComposer()` fetches the latest published version. Pin to a specific version:

```typescript
const { compose } = await promptly.getComposer('my-composer', {
  version: '2.0.0',
  input: { text: 'Hello world' },
});
```

**Tip:** Version pinning is useful for production deployments where you want to lock to a known-good composer version while iterating on a new version in the CMS.

## Batch fetch

Use `getComposers()` to fetch multiple composers in parallel:

```typescript
const [emailComposer, reportComposer] = await promptly.getComposers([
  { composerId: 'email-composer', input: { name: 'Alice' } },
  { composerId: 'report-composer', version: '2.0.0', input: { quarter: 'Q1' } },
]);

const emailOutput = await emailComposer.compose(generateText);
const reportOutput = await reportComposer.compose(generateText);
```

## Real-world example

A Next.js API route that fetches a composer and returns the assembled document:

```typescript
// app/api/generate-report/route.ts
import { createPromptlyClient } from '@promptlycms/prompts';
import { generateText } from 'ai';

const promptly = createPromptlyClient();

export const POST = async (req: Request) => {
  const { companyName, quarter } = await req.json();

  const { compose } = await promptly.getComposer('quarterly-report', {
    input: { companyName, quarter },
  });

  const report = await compose(generateText);

  return new Response(report, {
    headers: { 'Content-Type': 'text/html' },
  });
};
```

## Type generation

If you have run [codegen](https://docs.promptlycms.com/getting-started/type-generation/), the composer ID narrows to generated IDs and the `input` object is fully typed to the exact variables defined in the CMS. Without codegen, composer IDs accept any string and inputs fall back to `Record<string, unknown>`.

## Next steps

- Need per-prompt control, streaming, or structured output? See [Advanced Composer Patterns](https://docs.promptlycms.com/guides/advanced-composer-patterns/)
- Explore [AI SDK integration](https://docs.promptlycms.com/guides/ai-sdk-integration/) patterns for prompts
- Learn about [model resolution](https://docs.promptlycms.com/guides/model-resolution/) and custom resolvers
- Handle [errors](https://docs.promptlycms.com/guides/error-handling/) from the API
- Create and version your composers in the [Promptly CMS](https://promptlycms.com)