Drupal and ChatGPT plugins

After waiting for a month, I was finally granted developer access to ChatGPT plugins by OpenAI. This was an exciting moment, not just because of the potential of AI technology, but also because it presented a unique opportunity for me to experiment with my Drupal blog site.

I decided to create a simple plugin that would connect my blog site with ChatGPT. The idea was straightforward: allow ChatGPT users to pull information from my articles directly within their ChatGPT interface.

This wasn't about creating a revolutionary user experience or redefining how readers interact with my content. Instead, it was a practical project aimed at proving the concept - demonstrating that it's possible to integrate ChatGPT with a Drupal site in a meaningful way.

With this goal in mind, I set out to develop the plugin, eager to see the results of this experiment. The steps for creating a plugin are:

  1. Create an API in Drupal that includes the necessary endpoints
  2. Write a detailed OpenAPI specification that provides documentation for your API

Moving forward, I'll be diving deep into each of these steps, offering a detailed explanation to guide you.

1. Create an API in Drupal that includes the necessary endpoints

In the process of constructing an API, I focused on creating a simple endpoint that would serve the necessary data about my articles. This endpoint was designed to return key details such as the article's ID, title, summary, and the dates of creation and last update. This straightforward approach ensured that the ChatGPT plugin would have access to the most relevant information about my blog posts, providing a solid foundation for the integration.

MY_MODULE.routing.yml

MY_MODULE.get_articles:
  path: '/getArticles'
  methods: ['GET']
  defaults:
    _controller: '\Drupal\MY_MODULE\Controller\PageController::getArticles'
    _title: 'Get articles'
  requirements:
    _access: 'TRUE'

src/Controller/PageController.php

/**
 * Callback for the getArticles route.
 *
 * @return \Symfony\Component\HttpFoundation\JsonResponse
 *   The response.
 */
public function getArticles() {
  $query_to_execute = "SELECT nid, title, field_summary_value, created, changed 
                       FROM node_field_data 
                       LEFT JOIN node__field_summary ON node_field_data.nid = node__field_summary.entity_id 
                       WHERE status='1'";
  $query = $this->database->query($query_to_execute);
  $articles = array_map(function ($article) {
    return [
      'nid' => intval($article->nid),
      'title' => $article->title,
      'summary' => strip_tags($article->field_summary_value),
      'created' => intval($article->created),
      'changed' => intval($article->changed),
    ];
  }, $query->fetchAll());
  return new JsonResponse(['articles' => $articles]);
}

To ensure that the API was functioning as expected, I used Postman, a popular API client, for testing. Postman allowed me to send requests to the endpoint and view the responses in real time. The returned data included the article's ID, title, summary, and the dates of creation and last update, just as I had designed it.

Image

This successful test confirmed that the API was ready for the next step: integration with the ChatGPT plugin.

2. Write a detailed OpenAPI specification that provides documentation for your API

For the second step in the process, two crucial files need to be created: the Plugin Manifest and the OpenAPI Definition files. These files are essential for the successful integration of the API with the ChatGPT plugin, as they provide the necessary documentation and metadata.

Plugin manifest

Every plugin needs an ai-plugin.json file hosted on the API's domain, accessible via a specific path (/.well-known/ai-plugin.json), which is crucial for ChatGPT to connect with your plugin:

/.well-known/ai-plugin.json

{
  "schema_version": "v1",
  "name_for_human": "Goran Nikolovski Blog",
  "name_for_model": "Goran Nikolovski Blog",
  "description_for_human": "Explore the wealth of dev articles about Drupal and React from gorannikolovski.com effortlessly through ChatGPT, thanks to my newly developed plugin that bridges the gap between AI and content.",
  "description_for_model": "A plugin that allows the user to access the list of dev articles about Drupal and React from gorannikolovski.com blog site using ChatGPT.",
  "auth": {
    "type": "none"
  },
  "api": {
    "type": "openapi",
    "url": "https://gorannikolovski.com/openapi.yaml",
    "is_user_authenticated": false
  },
  "logo_url": "https://gorannikolovski.com/themes/custom/gn2021/images/goran-nikolovski-signature.png",
  "contact_email": "goran@gorannikolovski.com",
  "legal_info_url": "https://gorannikolovski.com/legal"
}

OpenAPI definition

Building the OpenAPI specification is crucial to document your API, as ChatGPT only knows about your API based on this specification and the manifest file; you can selectively expose specific endpoints, ensuring control over the model's capabilities, and this specification essentially acts as a wrapper for your API.

openapi.yaml

openapi: 3.0.1
info:
  title: Goran Nikolovski Blog
  description: Explore the wealth of dev articles about Drupal and React from gorannikolovski.com effortlessly through ChatGPT, thanks to my newly developed plugin that bridges the gap between AI and content.
  version: 'v1'
servers:
  - url: https://gorannikolovski.com
paths:
  /getArticles:
    get:
      operationId: getArticles
      summary: Get articles
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    nid:
                      type: integer
                      description: The ID of the article
                    title:
                      type: string
                      description: The title of the article
                    summary:
                      type: string
                      description: The summary of the article
                    created:
                      type: integer
                      description: The timestamp when the article was created
                    changed:
                      type: integer
                      description: The timestamp when the article was last changed
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: A descriptive error message indicating the reason for the internal server error

For more detailed information and guidance, visit the OpenAI documentation at https://platform.openai.com/docs/plugins/getting-started.

ChatGPT plugin installation

Now that all the necessary files are in place, we can move forward with the plugin installation process. Visit the ChatGPT plugin store and click on the Develop your own plugin link.

Image

Then enter https://gorannikolovski.com in the Domain input field.

Image

Next, click on the "Find Manifest File" button and carefully follow the provided instructions. Once completed, you should successfully have the plugin installed, as shown below:

Image

This is just a sample plugin and it's not officially approved yet. So, you'll need to go through a few extra steps to get it installed correctly.

Testing the ChatGPT plugin

Now comes the exciting part! Let's put the plugin to the test and utilize it to locate something on my website.

Image

Interestingly, ChatGPT has effectively utilized my API to locate all articles containing the word "recap". Even more intriguing is the fact that it managed to provide links to these articles, despite the absence of any such links in the API I developed.

The next step in my journey is to see if the ChatGPT plugin can identify my oldest article and provide the exact date it was published.

Image

Not only can it do that, but it also has the impressive ability to convert the original timestamp into a more understandable human-readable date format.

This is merely a small glimpse into the vast capabilities of ChatGPT plugins. The potential is immense - you could even write plugins that create or modify content directly on your website. So let's do it.

Create content in Drupal from the ChatGPT interface

We require a new endpoint that will enable us to generate content, so let's add a new route in Drupal.

MY_MODULE.routing.yml

MY_MODULE.api.create_promo:
  path: '/createPromo'
  methods: ['POST']
  defaults:
    _controller: '\Drupal\MY_MODULE\Controller\PageController::createPromo'
    _title: 'Create promo'
  requirements:
    _access: 'TRUE'

src/Controller/PageController.php

public function createPromo(Request $request) {
  $data = json_decode($request->getContent(), TRUE);
  $title = $data['title'] ?? NULL;
  $body = $data['body'] ?? NULL;

  // Validate that params are not empty.
  if (empty($title) || empty($body)) {
    return new JsonResponse(['message' => 'Please provide title and body params.'], 400);
  }

  $promo = $this->entityTypeManager()
    ->getStorage('node')
    ->create([
      'type' => 'promo',
      'title' => $title,
      'body' => [
        'value' => $body,
      ],
    ]);
  $promo->save();

  return new JsonResponse(['Promo created.']);
}

and then update the openapi.yaml file to include details about the new endpoint for creating nodes in Drupal.

openapi: 3.0.1
info:
  title: Goran Nikolovski Blog
  description: Explore the wealth of dev articles about Drupal and React from gorannikolovski.com effortlessly through ChatGPT, thanks to my newly developed plugin that bridges the gap between AI and content.
  version: 'v1'
servers:
  - url: https://gorannikolovski.com
paths:
  /getArticles:
    get:
      operationId: getArticles
      summary: Get articles
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    nid:
                      type: integer
                      description: The ID of the article
                    title:
                      type: string
                      description: The title of the article
                    summary:
                      type: string
                      description: The summary of the article
                    created:
                      type: integer
                      description: The timestamp when the article was created
                    changed:
                      type: integer
                      description: The timestamp when the article was last changed
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: A descriptive error message indicating the reason for the internal server error

  /createPromo:
    post:
      operationId: createPromo
      summary: Create a promotional content
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  description: The title of the promotional content
                body:
                  type: string
                  description: The body or description of the promotional content
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    description: A success message confirming the creation of the promotional content
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: A descriptive error message indicating the reason for the bad request
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: A descriptive error message indicating the reason for the internal server error

With that, we have all we need. The remaining steps involve testing this integration thoroughly and attempting to generate some content on my Drupal site directly from the ChatGPT interface.

Image

Indeed, the results are as anticipated; it works like a charm. A fresh new node springs to life on my site, demonstrating the successful integration of ChatGPT with Drupal. This opens up a realm of possibilities for enriched user interactions and dynamic content creation on the platform.

Image

In conclusion, the merging of artificial intelligence, specifically ChatGPT, with a versatile content management system like Drupal 9 and 10 is not only possible, but it also holds enormous potential for innovating how we interact with our websites. As we've seen, a new node creation is just the tip of the iceberg; the possibilities are virtually limitless. With AI integration, our websites can become more dynamic, interactive, and responsive.

I hope this blog post has offered an insightful peek into this exciting frontier of web development. Remember, this is just one application of AI in web development, and the future undoubtedly holds many more such advancements. Stay tuned to this space as I continue to explore and share more about the fascinating interplay of AI and web development.

Until next time, keep exploring, keep innovating, and most importantly, keep learning!

About the Author

Goran Nikolovski is a web and AI developer with over 10 years of expertise in PHP, Drupal, Python, JavaScript, React, and React Native. He founded this website and enjoys sharing his knowledge.