Search code examples
javascriptstrapi

are there any examples of writing simple Strapi plugin that can CRUD to the database?


Background

I have decided to deploy Strapi as a headless CMS/backend for my project and have been quite happy with the ease of setup out of the box. However, a part of my project requires capturing user input through a simple form and I thought extending Strapi's REST API through writing custom plugin and utilizing the same backend is the way to go instead of spinning up another express server with its own DB. I was able to find documentation on generating a plugin and I have a good understanding of how the file structure and logic of the different plugin files work (model, controllers, policies, config, routes etc...). However, for the life of me, I have not been able to find the most basic resources or any adequate documentation on how to write logic allowing my controllers to hook into the basic CRUD methods that Strapi has created for my custom collection ('messages' in my case).

What I have done so far

  • Generated a plugin

    Strapi generate:plugin contact-book

  • In plugins\contact-form\config\routes.json. Created a /postMessage route which I tested successfully after allowing public access to it from the Admin panel

     {   "routes": 
       [
        {
         "method": "POST",
         "path": "/postMessage",
         "handler": "contact-form.postMessage"
        }
       ]
     }
  • In plugins\contact-form\models. I created an empty model file Message.js & Message.settings.json containing the definition of my model 'message' which defines a collectionType collection names 'message' with its fields as follows:
     {
     "kind": "collectionType",
     "connection": "default",
     "info": {
       "name": "message",
       "description": "This represents the Message Model"
      },
     "attributes": {
       "name": {
         "default": "",
         "type": "string",
         "required": true
        },
       "email": {
         "default": "",
         "type": "email",
         "required": true
        },
       "message": {
         "default": "",
         "type": "text",
         "required": true
        }
     }
    }

Upon dev server restart, Strapi has already recognized my plugin and has reflected the 'message' model in its DB and I can see the collection from the admin panel correctly.

What I need help with

What I would like to do, is to extend the plugin's REST API endpoints to provide the same functionality as the out of the box end points that Strapi builds whenever a new collection/entity is created, while adding a custom layer of business logic to it.

I would appreciate any one pointing me towards an example or a resource that shows the methods or functions that Strapi exposes to plugins which can be hooked into or invoked to achieve this.


[Update] 14-June

So after hours of logging and inspecting 10s of objects that Strapi exposes to the controller, I was able to find that the strapi object exposes a query method which accepts 2 parameters, the first being the model and second is the plugin name. So simply speaking, the following enabled me to perform a write to my collection:

postMessage: async (ctx) => {
  const testData = {
    name: 'John Doe',
    email: 'john@doe.com',
    message: 'Hello World!'
  }

  result = await strapi.query("message","contact-form").create(data)
}

However, I still do not consider this an answer since I would like to find a more comprehensive approach through which built in policies and services can be used. Furthermore, I am still not sure if invoking this method bypasses any layers of middleware that Strapi sets up for default controllers and therefore exposes the app to security or stability risks.


Solution

  • The above can be achieved by utilizing the methods exposed by the built-in Strapi entityService. By examining how the stack handles create operations via the automatically build API endpoints, I was able to identify the entityService as the appropriate module for this functionality. However, when passing the plugin's model name to strapi.entityService.create({data} , {model: 'modelName') , the service was not able to locate the modelName. So I examined the service's source code and found out that it accepts the model UID instead. So in the case of a custom plugin, instead of just passing the modelName we need to pass the modelUID which is formatted as such:

    plugins::plugin-name.modelName
    

    In summary, for a create operation against the example in my question, this is how it would be:

    const result = await strapi.entityService.create(
      { data: ctx.request.body },
      { model: "plugins::contact-form.message" }
    )
    

    all other CRUD operations that Strapi supports are also exposed by the entityService and can be accessed in a similar fashion (Create, Update, Find, FindOne, Etc...). You can find all these methods under the Strapi documentation > Concepts > Controllers > Core Controllers.

    I have also created a YouTube video covering how this works: https://www.youtube.com/watch?v=kIZHzbmnhnU