# How to create a plugin with backend API calls to populate composer while drafting?

**URL:** https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970
**Category:** Development
**Created:** [March 12, 2025, 2:38pm UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970 "2025-03-12T14:38:02Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![fabkosta](https://avatars.discourse-cdn.com/v4/letter/f/b3f665/32.png) [@fabkosta](https://meta.discourse.org/u/fabkosta)
#### Post date: [March 12, 2025, 2:38pm UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/1 "2025-03-12T14:38:02Z")

</div>

I am trying to create a plugin that achieves the following:

1. Next to composer-fields there is a new button “search”.
2. When clicking the button, then an overlay is popping up with a search form.
3. The search form allows the user to enter some search terms (let’s say, a city name), and a search with a proprietary image search engine (running under my own URL that requires an API key) is being triggered to search for images of that city.
4. Top 5 images are shown to the user. The user selects one image, the overlay disappears, the image plus search string is then added inside the composer as a draft. The use can now continue writing whatever their post was.

My actual use case is a bit more complicated, but that’s roughly what I want to achieve. The goal is simply to help the user to create post drafts with images much more conveniently than to go to the search engine and manually copy an image into the composer to create a draft.

I have already managed to create a plugin with a new button using composer-field as a connector. The button can be clicked and it triggers an action. But now I’m stuck.

Some questions:

1. Are there any plugins that would make my life easier? I was thinking maybe about Pavilion wizard plugin, but I’ve seen that making calls to an API only comes with the paid version, and right now it’s too early for me to decide if it’s worth spending money on this.
2. What is the right CSS class / div ID that I could target for activating an overlay?
3. What would be a good example to figure out how to make an API call via the backend? I don’t want to give the API key of the search engine to the client, so it must go via backend.
4. I’m not sure what the plugin API actually is and whether I’d need that. (I’m struggling a little bit with docs being all over the place.)

All other tips and hints are welcome.

---

<div class="post-metadata">

### Author: ![fabkosta](https://avatars.discourse-cdn.com/v4/letter/f/b3f665/32.png) [@fabkosta](https://meta.discourse.org/u/fabkosta)
#### Post date: [March 12, 2025, 5:21pm UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/2 "2025-03-12T17:21:18Z")

</div>

In the meanwhile I found a few resources that I found informative. I really wish these could be put more prominently in the “How to write a plugin” tutorial. For beginners it is not obvious they even exist.

1. Discourse’s own API (not helpful for writing plugins, but still relevant for beginners): [https://docs.discourse.org/](https://docs.discourse.org/)
2. Client Plugin API (not helpful for writing a plugin’s backend, but makes things definitely easier for frontend):  
[A versioned API for client side plugins](https://meta.discourse.org/t/a-versioned-api-for-client-side-plugins/40051)  
[https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.gjs](https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.gjs)

I also understood that Backend Plugin API is essentially Rails or Ruby - but it’s not clear how exactly frontend and backend interact with each other, or where the initial “hook” for the backend is. Sure, there’s a plugin.rb to be placed, but I’m left wondering whether there’s any docs on what’s the entry point into this file. Maybe that’s totally obvious for Ruby on Rails programmers, but I’m not one of them, so it’s a steep entry here.

---

<div class="post-metadata">

### Author: ![renato](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/renato/32/383632_2.png) [@renato](https://meta.discourse.org/u/renato)
#### Post date: [March 12, 2025, 6:24pm UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/3 "2025-03-12T18:24:59Z")

</div>

> [@fabkosta](#):
>
> (running under my own URL that requires an API key)

It doesn’t seem like you need any backend changes. The behavior you describe is very similar to how our [“Insert Hyperlink” modal](https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/components/modal/insert-hyperlink.gjs) works – you can search for topics, select one of them to add as a link, and a link will be added to the composer when you confirm.

You can add the button to a [plugin outlet](https://meta.discourse.org/t/using-plugin-outlet-connectors-from-a-theme-or-plugin/32727), or to the toolbar via a [`api.onToolbarCreate`](https://github.com/discourse/discourse/blob/897b043f01e9e06c14239c233efd3dab5be85aae/app/assets/javascripts/discourse/app/lib/plugin-api.gjs#L1008-L1027) added to an initializer, and open a modal similarly to how we’re doing it for the [“Insert Hyperlink” modal](https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/components/modal/insert-hyperlink.gjs). It’s able to [`addText`](https://github.com/discourse/discourse/blob/897b043f01e9e06c14239c233efd3dab5be85aae/app/assets/javascripts/discourse/app/components/modal/insert-hyperlink.gjs#L142-L149) to the composer by levering the [`toolbarEvent` object](https://github.com/discourse/discourse/blob/897b043f01e9e06c14239c233efd3dab5be85aae/app/assets/javascripts/discourse/app/components/d-editor.js#L565-L570) from the toolbar action (if you need an outlet, you can also interact with the composer via [`appEvents` triggers](https://meta.discourse.org/t/appevents-triggers-reference/338465#p-1654554-composerinsert-text-linkhttpsgithubcomdiscoursediscourseblobmainappassetsjavascriptsdiscourseappservicessearchjsl42-46)).

---

<div class="post-metadata">

### Author: ![fabkosta](https://avatars.discourse-cdn.com/v4/letter/f/b3f665/32.png) [@fabkosta](https://meta.discourse.org/u/fabkosta)
#### Post date: [March 12, 2025, 7:41pm UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/4 "2025-03-12T19:41:10Z")

</div>

Thanks, that’s really helpful Yes, a modal actually seems the thing I’m looking for: it interrupts the user’s flow, and only when the user has completed the modal, things go back to the state before. I’ll have a look how the Insert Hyperlink Modal works.

Question: Where can I find docs for DModal, DButton, and other such Discourse-specific objects?

---

<div class="post-metadata">

### Author: ![Arkshine](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/arkshine/32/298682_2.png) [@Arkshine](https://meta.discourse.org/u/Arkshine)
#### Post date: [March 12, 2025, 8:03pm UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/5 "2025-03-12T20:03:13Z")

</div>

> [@fabkosta](#):
>
> Question: Where can I find docs for DModal, DButton, and other such Discourse-specific objects?

Here:

> [@Using the DModal API to render Modal windows (aka popups/dialogs) in Discourse](https://meta.discourse.org/t/using-the-dmodal-api-to-render-modal-windows-aka-popups-dialogs-in-discourse/268304):
>
> Discourse 3.1.0.beta6 ships with a brand new \<DModal\> component-based API. information_source This supersedes the old controller-based API, which is now deprecated. If you have existing modals using the old APIs, check out the migration guide [here](https://meta.discourse.org/t/converting-modals-from-legacy-controllers-to-new-dmodal-component-api/268057). Rendering a Modal Modals are rendered by including the \<DModal\> component in a handlebars template. If you don’t already have a suitable template, check out [Using Plugin Outlet Connectors from a Theme or Plugin](https://meta.discourse.org/t/using-plugin-outlet-connectors-from-a-theme-or-plugin/32727). A simple modal would look someth…

Also worth noting:

> [@Discourse toolkit to render forms](https://meta.discourse.org/t/discourse-toolkit-to-render-forms/326439?silent=true):
>
> Basic Usage FormKit exposes a single component as its public API: \<Form /\>. All other elements are yielded as contextual components, modifiers, or plain data. Every form is composed of one or multiple fields, representing the value, validation, and metadata of a control. Each field encapsulates a control, which is the form element the user interacts with to enter data, such as an input or select. The control type is specified via @type on the field. Other utilities, like submit or alert, are …

You can look at Discourse/plugins/theme components for examples.

---

<div class="post-metadata">

### Author: ![fabkosta](https://avatars.discourse-cdn.com/v4/letter/f/b3f665/32.png) [@fabkosta](https://meta.discourse.org/u/fabkosta)
#### Post date: [March 13, 2025, 7:51am UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/6 "2025-03-13T07:51:54Z")

</div>

Thank you, senior!

---

<div class="post-metadata">

### Author: ![fabkosta](https://avatars.discourse-cdn.com/v4/letter/f/b3f665/32.png) [@fabkosta](https://meta.discourse.org/u/fabkosta)
#### Post date: [March 14, 2025, 10:24am UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/7 "2025-03-14T10:24:43Z")

</div>

Here’s another piece that seems to be pretty important, as it describes how frontend and backend are glued together via routes:

> [@Creating Routes in Discourse and Showing Data](https://meta.discourse.org/t/creating-routes-in-discourse-and-showing-data/48827):
>
> Over time Discourse has grown in complexity and it can be daunting for beginners to understand how data gets all the way from the back end Ruby on Rails application to the Ember.js application in front. This tutorial is meant to show the full lifecycle of a request in Discourse and explain the steps necessary if you want to build a new page with its own URL in our application. URLs First I always prefer to start thinking of features in terms of the URLs to access them. For example let’s say we…

---

<div class="post-metadata">

### Author: ![fabkosta](https://avatars.discourse-cdn.com/v4/letter/f/b3f665/32.png) [@fabkosta](https://meta.discourse.org/u/fabkosta)
#### Post date: [March 14, 2025, 10:47am UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/8 "2025-03-14T10:47:31Z")

</div>

Also important for my use case is the FormKit: [Discourse toolkit to render forms](https://meta.discourse.org/t/discourse-toolkit-to-render-forms/326439?silent=true)

---

<div class="post-metadata">

### Author: ![fabkosta](https://avatars.discourse-cdn.com/v4/letter/f/b3f665/32.png) [@fabkosta](https://meta.discourse.org/u/fabkosta)
#### Post date: [March 14, 2025, 11:04am UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/9 "2025-03-14T11:04:36Z")

</div>

Some more puzzle pieces:

API keys can be securely stored under /admin/api/keys.

---

<div class="post-metadata">

### Author: ![fabkosta](https://avatars.discourse-cdn.com/v4/letter/f/b3f665/32.png) [@fabkosta](https://meta.discourse.org/u/fabkosta)
#### Post date: [March 14, 2025, 12:15pm UTC](https://meta.discourse.org/t/how-to-create-a-plugin-with-backend-api-calls-to-populate-composer-while-drafting/356970/10 "2025-03-14T12:15:09Z")

</div>

Finally I found a great tutorial on how to write a plugin.

- [How to create a Discourse plugin – kleinfreund.de](https://kleinfreund.de/how-to-create-a-discourse-plugin/)
- [GitHub - kleinfreund/notebook: Discourse example plugin · GitHub](https://github.com/kleinfreund/notebook)
