{"id":13560208,"url":"https://github.com/bcms/cms","last_synced_at":"2025-09-25T15:11:16.092Z","repository":{"id":36946003,"uuid":"232842535","full_name":"bcms/cms","owner":"bcms","description":"⭐️ BCMS is a Headless CMS for developers \u0026 their clients. Works nicely with Gatsby, Next.js \u0026 Nuxt. This is an open-source version for self-hosting.","archived":false,"fork":false,"pushed_at":"2024-10-28T09:55:47.000Z","size":44079,"stargazers_count":354,"open_issues_count":17,"forks_count":22,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-30T00:35:53.667Z","etag":null,"topics":["api-gateway","gatsby","gatsbyjs","headless","headless-cms","jamstack","marketing","nextjs","nodejs","nuxt","nuxtjs","paas","serverless","static","static-website"],"latest_commit_sha":null,"homepage":"https://thebcms.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bcms.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["bcms"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2020-01-09T15:39:21.000Z","updated_at":"2024-10-29T02:40:47.000Z","dependencies_parsed_at":"2023-07-16T17:23:59.603Z","dependency_job_id":"6d0fd757-b254-4c31-a59d-9627f023e168","html_url":"https://github.com/bcms/cms","commit_stats":{"total_commits":486,"total_committers":6,"mean_commits":81.0,"dds":0.1049382716049383,"last_synced_commit":"c2147940a3ef2af04a66ba07a6a12f5362b3b58d"},"previous_names":["becomesco/cms"],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcms%2Fcms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcms%2Fcms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcms%2Fcms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcms%2Fcms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bcms","download_url":"https://codeload.github.com/bcms/cms/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247305932,"owners_count":20917208,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-gateway","gatsby","gatsbyjs","headless","headless-cms","jamstack","marketing","nextjs","nodejs","nuxt","nuxtjs","paas","serverless","static","static-website"],"created_at":"2024-08-01T13:00:39.607Z","updated_at":"2025-09-25T15:11:11.071Z","avatar_url":"https://github.com/bcms.png","language":"TypeScript","readme":"\u003cimg src=\"https://raw.githubusercontent.com/bcms/cms/v3/assets/readme/bcms-preview.webp\" alt=\"Interface Animation\"  width=\"800px\" /\u003e\n\n# BCMS - Open-source Headless CMS\n\nThis repository holds open-source version of BCMS, a modern headless CMS that allows you to easily manage content with a flexible structure. User-friendly interface, fast deployment options - makes it perfect for developers and teams looking for a customizable CMS solution.\n\n## Table of Contents\n\n-   [Run Locally](#run-locally)\n-   [Deploy on Debian-based Server with CLI](#deploy-on-debian-based-server-with-cli)\n-   [Deploy on Debian-based Server Manually](#deploy-on-debian-based-server-manually)\n-   [Contributing](#contributing)\n-   [Cloud vs Open-source](#bcms-cloud-vs-open-source-bcms)\n-   [Support](#support)\n-   [Extending BCMS](#extending-bcms)\n    -   [BCMS Events](#bcms-events)\n    -   [BCMS Functions](#bcms-functions)\n    -   [BCMS Jobs](#bcms-jobs)\n    -   [BCMS Plugins](#bcms-plugins)\n\n## Prerequisites\n\n-   Node.js 20\n-   Docker and Docker Compose\n-   Git\n\n## Run Locally\n\n1. Make sure that you have Node.js 20, Docker, and Docker Compose installed on your system.\n2. Clone the repository: `git clone https://github.com/bcms/cms`\n3. Open the repository in your favorite code editor and install dependencies: `npm i`\n4. Start the local development server: `docker compose up`\n5. When everything is ready, open a browser and navigate to `http://localhost:8080`\n\n## Deploy on Debian-based Server with CLI\n\nAfter you have a Debian-based server, you can SSH into it and follow the steps below.\n\n### Install Dependencies\n\nInstall dependencies if you do not already have them on the server:\n\n```bash\nsudo apt update \u0026\u0026 sudo apt install docker.io git nodejs npm\n```\n\n### Update Node.js to version 20:\n\n```bash\nnpm i -g n \u0026\u0026 n 20\n```\n\n### Set Up GitHub Packages\n\nSince we are using GitHub Packages, you will need to add configuration to the `~/.npmrc` to pull packages. Add the following two lines:\n\n```npm\n//npm.pkg.github.com/:_authToken=\u003cGITHUB_TOKEN\u003e\n@bcms:registry=https://npm.pkg.github.com\n```\n\nTo generate a `GITHUB_TOKEN`, follow [this tutorial](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic). The only permission needed for this token is `read:packages`.\n\n### Install BCMS CLI\n\n```bash\nnpm i -g @bcms/selfhosted-cli\n```\n\n### Deploy BCMS\n\n```bash\nselfbcms --deploy debian\n```\n\n## Deploy on Debian-based Server Manually\n\nAfter you have a Debian-based server, you can SSH into it and follow the steps below.\n\n### Install Dependencies\n\n```bash\nsudo apt update \u0026\u0026 sudo apt install docker.io git\n```\n\n### Create a Directory for BCMS Data\n\n```bash\nmkdir ~/bcms\n```\n\n### Create Directories for BCMS Container Volumes\n\n```bash\nmkdir ~/bcms/db ~/bcms/uploads ~/bcms/backups\n```\n\n### Clone the Repository\n\n```bash\ngit clone https://github.com/bcms/cms\n```\n\n### Build Docker Image\n\n```bash\ndocker build . -t my-bcms\n```\n\n### Create Docker Network\n\n```bash\ndocker network create -d bridge --subnet 10.20.0.0/16 --ip-range 10.20.30.0/24 --gateway 10.20.30.1 bcms-net\n```\n\n### Optional: Set Up MongoDB Database in Docker\n\nIf you don't have MongoDB, you can run it inside a Docker container on the same server:\n\n```bash\ndocker run -d --name my-bcms-db -v ~/bcms/db:/data/db -e MONGO_INITDB_ROOT_USERNAME=\u003cDB_ADMIN_USERNAME\u003e -e MONGO_INITDB_ROOT_PASSWORD=\u003cDB_ADMIN_PASSWORD\u003e --network bcms-net mongo:7\n```\n\nWith this setup, the MongoDB database will be stored in `~/bcms/db` and accessible from `bcms-net` on port 27017.\n\n### Create BCMS Container\n\n```bash\ndocker run -d --name my-bcms -v ~/bcms/uploads:/app/backend/uploads -v ~/bcms/backups:/app/backend/backups -e \"DB_URL=\u003cMONGODB_CONNECTION_URL\u003e\" --network bcms-net my-bcms\n```\n\nIf MongoDB is set up on the same server, the `DB_URL` will be `mongodb://\u003cDB_ADMIN_USERNAME\u003e:\u003cDB_ADMIN_PASSWORD\u003e@my-bcms-db:27017/admin`.\n\n### Set Up Nginx Reverse Proxy\n\nTo handle incoming requests, you need to set up an Nginx reverse proxy.\n\n#### Nginx Configuration:\n\n```nginx\n# File location: ~/bcms/nginx.conf\nuser www-data;\nworker_processes auto;\npid /run/nginx.pid;\ninclude /etc/nginx/modules-enabled/*.conf;\n\nevents {\n  worker_connections 768;\n}\n\nhttp {\n  sendfile on;\n  tcp_nopush on;\n  tcp_nodelay on;\n  keepalive_timeout 65;\n  types_hash_max_size 2048;\n  server_tokens off;\n\n  include /etc/nginx/mime.types;\n  default_type application/octet-stream;\n\n  ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;\n  ssl_prefer_server_ciphers on;\n\n  access_log /var/log/nginx/access.log;\n  error_log /var/log/nginx/error.log;\n\n  include /etc/nginx/conf.d/*.conf;\n  include /etc/nginx/sites-enabled/*;\n\n  add_header Content-Security-Policy \"default-src 'self' 'unsafe-inline' blob: data:\";\n  add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains\" always;\n  add_header Referrer-Policy \"no-referrer\";\n\n  server {\n    listen 80 default_server;\n    listen [::]:80 default_server;\n    server_name _;\n\n    client_max_body_size 105G;\n\n    location /api/v4/socket {\n      proxy_http_version 1.1;\n      proxy_set_header Upgrade $http_upgrade;\n      proxy_set_header Connection \"upgrade\";\n\n      proxy_pass http://my-bcms:8080/api/v4/socket;\n    }\n    location /__plugin {\n      proxy_read_timeout 60;\n      proxy_connect_timeout 60;\n      proxy_send_timeout 60;\n      proxy_pass http://my-bcms:8080/__plugin;\n    }\n    location / {\n      proxy_read_timeout 60;\n      proxy_connect_timeout 60;\n      proxy_send_timeout 60;\n      proxy_pass http://my-bcms:8080;\n    }\n  }\n}\n```\n\nThis configuration uses the default Nginx virtual host. To use a custom domain, adjust the configuration as needed.\n\n### Create Dockerfile for Nginx\n\n```Dockerfile\n# File location: ~/bcms/proxy.Dockerfile\nFROM nginx\n\nCOPY nginx.conf /etc/nginx/nginx.conf\n```\n\n### Build Nginx Docker Image\n\n```bash\ndocker build . -f proxy.Dockerfile -t my-bcms-proxy\n```\n\n### Run Nginx Container\n\n```bash\ndocker run -d -p 80:80 --name my-bcms-proxy --network bcms-net my-bcms-proxy\n```\n\n## Contributing\n\nWe welcome contributions!\n\n## BCMS Cloud vs. Open-source BCMS\n\nIn September 2024, we made a big shift: BCMS Cloud went closed-source (as [BCMS Pro](https://thebcms.com)), and the open-source version became completely standalone.\n\nWhy did we do this?\n\nWhen we first built BCMS, we centralized the authentication system in BCMS Cloud. Even if you were self-hosting, you still had to log in through our system. We thought this would simplify things like inviting users, sending emails, and managing onboarding. But then people on Reddit tore us apart for it. And they were right. So, we listened.\n\nAnother issue was keeping BCMS up to date. We’re constantly improving it, but making sure your self-hosted version could update easily was always a technical headache.\n\nThe way we originally set up BCMS Cloud also created problems. Each instance had to run on its own isolated VPS, which slowed things down and made infrastructure costs shoot through the roof.\n\nOur goal has always been to create a fast, opinionated content editing experience. But trying to keep both the open-source and Cloud versions moving forward started to feel unsustainable. So, we made the call to split them. The open-source community now has exactly what they asked for: a fully self-contained, self-hostable BCMS, completely free.\n\nMeanwhile, we’ll keep pushing forward on all new - BCMS Pro, now closed-source, redeveloped from ground-up, and optimized for those who need the premium, managed experience.\n\nThe core BCMS team is super small - just three engineers, one designer, a project manager, and a content writer. So, we’re focusing most of our energy on [BCMS Pro](https://thebcms.com), but we’re excited to see where the community takes the open-source version.\n\nHappy coding!\n\n## Support\n\nIf you have any questions or need help, feel free to open an issue or reach out to us @ [Discord](https://discord.gg/Rr4kTKpU).\n\n## Stay in touch 🌐\n\n\u003ca href=\"https://twitter.com/thebcms\"\u003eFollow on X (Twitter)\u003c/a\u003e\u003cbr\u003e\n\u003ca href=\"https://www.linkedin.com/company/thebcms/\"\u003eFollow on LinkedIn\u003c/a\u003e\u003cbr\u003e\n\u003ca href=\"https://discord.gg/Rr4kTKpU\"\u003eJoin us on Discord\u003c/a\u003e\u003cbr\u003e\n\n## Extending BCMS\n\nThere are 4 main ways in which you can extend your BCMS and those are Events, Functions, Jobs and Plugins. First 3 are only for extending backend functionality and performing custom backend tasks while Plugins are apps which have their own backend and UI on top of BCMS core.\n\n### BCMS Events\n\nBCMS Events are custom JavaScript files (JS function) which are executed when internal BCMS event is triggered, for example when Entry is created, updated or deleted or when Widget is created, update or deleted. [List of all Event types](https://github.com/bcms/cms/blob/master/backend/src/event/models/main.ts)\n\nCreating an Event handler is as simple adding a file root `/backend/events` directory. This directory is located inside the BCMS container. You can mount it as a volume when running the container and this would look something like this: `-v \u003cpath_to_my_events_dir\u003e:/app/backend/events`.\n\nIf you are running BCMS locally from this repository you can add an Event file to `/backend/events/\u003cmy_event\u003e.{js|ts}`.\n\nHere is a simple example Event:\n\n```js\n// Is used locally in /backend/events/test.js\n\nconst { createEvent } = require('@bcms/selfhosted-backend/event');\n\nmodule.exports = createEvent(async () =\u003e {\n    return {\n        config: {\n            // Trigger Event handler only for\n            // Entry changes\n            scope: 'entry',\n            // Listen for all Event types:\n            // (create, update, delete)\n            type: 'all',\n        },\n        // This method will be executed when\n        // Event is triggered\n        async handler(type, scope, data) {\n            // Do something with the event\n            console.log({ type, scope, data });\n        },\n    };\n});\n```\n\nThis file will be loaded by the BCMS backend and executed which means that it runs in the same scope and you can use internal utilities like `Repo.*` to access database. Execution of this file is unrestricted, all permissions that BCMS backend have, your Event will also have. Isn't this dangerous? Yes and no, with great power comes great responsibility. This means that you should never run untrusted code inside the Event.\n\nNow, what if you want to import some custom module, for example [@paralleldrive/cuid2](https://www.npmjs.com/package/@paralleldrive/cuid2)? You can do this by adding it to `/backend/custom-package.json` which should have structure like this:\n\n```json\n{\n    \"dependencies\": {\n        // Your custom packages\n        \"@paralleldrive/cuid2\": \"^2.2.2\"\n    }\n}\n```\n\nCustom packages will be initialized when BCMS backend starts.\n\nThere is 1 more thing, what if you have some shared logic between your Events, Jobs and Functions? We are calling those additional files, but you can look at them as your custom utilities. Inside the `/backend/additional` you can add any JS file and import it in the Event.\n\n### BCMS Functions\n\nSimilar to BCMS Events, BCMS Functions are JavaScript files (JS function) which are executed when function endpoint is called: `POST: /api/v4/function/\u003cmy_function_name\u003e`. You can look at them like your custom code which will run on the BCMS backend when HTTP request is made to function execution endpoint.\n\nCreating a Function is as simple as adding a file to the `/backend/functions`. This directory is located inside the BCMS container. You can mount it as a volume when running the container and this would look something like this: `-v \u003cpath_to_my_functions_dir\u003e:/app/backend/functions`.\n\nIf you are running BCMS locally from this repository you can add an Function file to `/backend/functions/\u003cmy_function\u003e.{js|ts}`.\n\nHere is a simple example Function which will echo a request:\n\n```js\nconst { createFunction } = require('@bcms/selfhosted-backend/function');\n\nmodule.exports = createFunction(async () =\u003e {\n    return {\n        config: {\n            name: 'echo',\n        },\n        async handler({ request }) {\n            return {\n                message: 'Function echo',\n                data: request.body,\n            };\n        },\n    };\n});\n```\n\nTo successfully call this Function you will need to create an Api Key (_Administration/Key manager_) and allow it to call the Function. After this you can create a HTTP request to it:\n\n```http request\nPOST http://localhost:8080/api/v4/function/echo\nContent-Type: application/json\nAuthorization: ApiKey \u003ckey_id\u003e.\u003ckey_secret\u003e\n\n{\n  \"fromClient\": \"Hey from client\"\n}\n\n// Response\n// {\n//   \"success\": true,\n//   \"result\": {\n//     \"message\": \"Function echo\",\n//     \"data\": {\n//       \"fromClient\": \"Hey from client\"\n//     }\n//   }\n// }\n```\n\nThis file will be loaded by the BCMS backend and executed which means that it runs in the same scope and you can use internal utilities like `Repo.*` to access database. Execution of this file is unrestricted, all permissions that BCMS backend have, your Function will also have. Isn't this dangerous? Yes and no, with great power comes great responsibility. This means that you should never run untrusted code inside the Function.\n\nNow, what if you want to import some custom module, for example [@paralleldrive/cuid2](https://www.npmjs.com/package/@paralleldrive/cuid2)? You can do this by adding it to `/backend/custom-package.json` which should have structure like this:\n\n```json\n{\n    \"dependencies\": {\n        // Your custom packages\n        \"@paralleldrive/cuid2\": \"^2.2.2\"\n    }\n}\n```\n\nCustom packages will be initialized when BCMS backend starts.\n\nThere is 1 more thing, what if you have some shared logic between your Events, Jobs and Functions? We are calling those additional files, but you can look at them as your custom utilities. Inside the `/backend/additional` you can add any JS file and import it in the Function.\n\n### BCMS Jobs\n\nSimilar to BCMS Events and Function, BCMS Jobs are JavaScript files (JS function) which are executed in specified CRON interval. You can look at them like your custom code which will run on the BCMS backend in specified interval.\n\nCreating a Job is as simple as adding a file to the `/backend/jobs`. This directory is located inside the BCMS container. You can mount it as a volume when running the container and this would look something like this: `-v \u003cpath_to_my_jobs_dir\u003e:/app/backend/jobs`.\n\nIf you are running BCMS locally from this repository you can add a Job file to `/backend/jobs/\u003cmy_job\u003e.{js|ts}`.\n\nHere is a simple example Function which will console log current time every minute:\n\n```js\nconst { createJob } = require('@bcms/selfhosted-backend/job');\n\nmodule.exports = createJob(async () =\u003e {\n    return {\n        cronTime: '* * * * *', // You can use: https://crontab.guru/\n        async handler() {\n            console.log(new Date().toISOString());\n        },\n    };\n});\n```\n\nThis file will be loaded by the BCMS backend and executed which means that it runs in the same scope and you can use internal utilities like `Repo.*` to access database. Execution of this file is unrestricted, all permissions that BCMS backend have, your Job will also have. Isn't this dangerous? Yes and no, with great power comes great responsibility. This means that you should never run untrusted code inside the Job.\n\nNow, what if you want to import some custom module, for example [@paralleldrive/cuid2](https://www.npmjs.com/package/@paralleldrive/cuid2)? You can do this by adding it to `/backend/custom-package.json` which should have structure like this:\n\n```json\n{\n    \"dependencies\": {\n        // Your custom packages\n        \"@paralleldrive/cuid2\": \"^2.2.2\"\n    }\n}\n```\n\nCustom packages will be initialized when BCMS backend starts.\n\nThere is 1 more thing, what if you have some shared logic between your Events, Jobs and Functions? We are calling those additional files, but you can look at them as your custom utilities. Inside the `/backend/additional` you can add any JS file and import it in the Job.\n\n## BCMS Plugins\n\nYou can look at the BCMS Plugin as an application with its own backend and frontend which is served by the BCMS backend and have access to all backend and frontend features. For the Plugin backend you need to follow some patterns but for the Plugin UI you can build any SPA application (you can use React or VanillaJS, but we recommend VueJS because you will be able to use BCMS UI Components).\n\nBest way to explain this is to give you a simple example. This will be the structure of the Plugin:\n\n```text\n/backend/plugins\n    - /hello-world\n        - /_ui\n            - index.html\n        - config.json\n        - controller.js\n        - main.js\n```\n\nTo make this example as simple as possible entire frontend code will be contained in `_ui/index.html` while the backend will contain 1 controller which will greet a user.\n\n\u003e config.json\n```json\n{\n  \"version\": \"1\",\n  \"dependencies\": {}\n}\n```\n\n\u003e controller.js\n\n```js\nconst { createPluginController } = require('@bcms/selfhosted-backend/plugin');\nconst { createControllerMethod } = require('@bcms/selfhosted-backend/_server');\nconst {\n    RP,\n} = require('@bcms/selfhosted-backend/security/route-protection/main');\n\nexports.HelloWorldController = createPluginController({\n    name: 'NameGreet',\n    path: '/greet',\n    methods() {\n        return {\n            greet: createControllerMethod({\n                path: '/:name',\n                type: 'get',\n                preRequestHandler: RP.createJwtCheck(),\n                async handler({ request }) {\n                    const params = request.params;\n                    return {\n                        greet: `Hello ${params.name}!`,\n                    };\n                },\n            }),\n        };\n    },\n});\n```\n\n\u003e main.js\n\n```js\nconst { createPlugin } = require('@bcms/selfhosted-backend/plugin');\nconst { HelloWorldController } = require('./controller');\n\nmodule.exports = createPlugin(async () =\u003e {\n  return {\n    id: 'hello-world',\n    name: 'Hello World',\n    controllers: [HelloWorldController],\n    middleware: [],\n    async policy() {\n      return [\n        {\n          name: 'Full access',\n        },\n      ];\n    },\n  };\n});\n```\n\n\u003e _ui/index.html\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"UTF-8\" /\u003e\n  \u003ctitle\u003eHello world\u003c/title\u003e\n  \u003cstyle\u003e\n    body {\n      color: white;\n    }\n  \u003c/style\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003ch1\u003eHello world\u003c/h1\u003e\n\u003cdiv\u003e\n  \u003cinput id=\"greet-input\" placeholder=\"Greet\" type=\"text\" /\u003e\n  \u003cbutton onclick=\"greet(this)\"\u003eSend\u003c/button\u003e\n  \u003cdiv id=\"greet-result\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2\u003eList of templates\u003c/h2\u003e\n\u003cdiv id=\"templates\"\u003e\u003c/div\u003e\n\u003cscript\u003e\n  async function onLoad() {\n    const templates =\n      await window.parent.bcms.sdk.template.getAll();\n    const el = document.getElementById('templates');\n    if (el) {\n      el.innerHTML = `\u003cpre\u003e${JSON.stringify(\n        templates,\n        null,\n        4,\n      )}\u003c/pre\u003e`;\n    }\n    window.removeEventListener('load', onLoad);\n  }\n  window.addEventListener('load', onLoad);\n\n  async function greet() {\n    const inputEl = document.getElementById('greet-input');\n    const value = inputEl.value;\n    const result = await window.parent.bcms.sdk.send({\n      url: `/api/v4/plugin/hello-world/greet/${value}`,\n    });\n    const el = document.getElementById('greet-result');\n    el.innerHTML = `\u003cpre\u003e${JSON.stringify(result, null, 4)}\u003c/pre\u003e`;\n  }\n\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nTODO: Explain plugins in more details and create example plugins","funding_links":["https://github.com/sponsors/bcms"],"categories":["Cloud Solutions","TypeScript","Official Links"],"sub_categories":["Databases"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbcms%2Fcms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbcms%2Fcms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbcms%2Fcms/lists"}