> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grunt.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Storage pipeline

> Implement a custom Grunt object storage pipeline backed by your API

<Warning>
  Do not use `LocalObjectStoragePipeline` in production.
</Warning>

Use a custom API-backed pipeline and register it through a CKEditor plugin.

## Step 1: create a custom storage pipeline

```ts theme={null}
import type { GruntObjectStoragePipeline } from "@grunt/ckeditor5-grunt/grunt";

export class CustomStoragePipeline implements GruntObjectStoragePipeline {
  async saveCommit(key: string, commit: string): Promise<void> {
    await fetch(`/api/grunt/${key}/commit`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ commit })
    });
  }

  async saveSvg(key: string, svg: string): Promise<void> {
    await fetch(`/api/grunt/${key}/svg`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ svg })
    });
  }

  async getCommit(key: string): Promise<string> {
    const response = await fetch(`/api/grunt/${key}/commit`);
    if (!response.ok) throw new Error("Failed to load commit");
    const data = await response.json();
    return data.commit;
  }

  async getSvg(key: string): Promise<string | null> {
    const response = await fetch(`/api/grunt/${key}/svg`);
    if (response.status === 404) return null;
    if (!response.ok) throw new Error("Failed to load svg");
    const data = await response.json();
    return data.svg ?? null;
  }
}
```

You can add:

* Authentication headers
* JWT handling
* Versioning
* Multi-tenant routing
* Signed URLs
* Retry logic

## Step 2: register the pipeline in a plugin

```ts theme={null}
import { Plugin } from "ckeditor5";
import Grunt from "@grunt/ckeditor5-grunt/grunt";
import { CustomStoragePipeline } from "./CustomStoragePipeline";

class CustomGruntPipelinePlugin extends Plugin {
  static get pluginName() {
    return "CustomGruntPipelinePlugin";
  }

  static get requires() {
    return [Grunt];
  }

  init() {
    const editor = this.editor;

    editor.plugins.get("Grunt").setPipeline(new CustomStoragePipeline(editor));
  }
}
```

## Step 3: include the plugin in your editor config

```ts theme={null}
ClassicEditor.create(document.querySelector("#editor"), {
  plugins: [
    Essentials,
    Paragraph,
    Bold,
    Italic,
    Grunt,
    CustomGruntPipelinePlugin
  ]
});
```

Grunt now uses your API for object persistence.

<Card title="Next: runtime assets" icon="arrow-right" href="/ckeditor-integration/runtime-assets">
  Serve `grunt.js` and `_framework/*` correctly.
</Card>
