{"id":13726517,"url":"https://github.com/getkirby/kql","last_synced_at":"2025-05-16T04:05:37.083Z","repository":{"id":37394220,"uuid":"234099248","full_name":"getkirby/kql","owner":"getkirby","description":"Kirby's Query Language API combines the flexibility of Kirby's data structures, the power of GraphQL and the simplicity of REST.","archived":false,"fork":false,"pushed_at":"2025-02-12T14:39:16.000Z","size":221,"stargazers_count":146,"open_issues_count":6,"forks_count":5,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-04-09T11:08:01.114Z","etag":null,"topics":["api","cms","file-based","flat-file","headless-cms","json","kirby","kql","query","query-language"],"latest_commit_sha":null,"homepage":"https://getkirby.com","language":"PHP","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/getkirby.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"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":{"custom":["https://getkirby.com/buy"]}},"created_at":"2020-01-15T14:33:39.000Z","updated_at":"2025-03-31T11:02:49.000Z","dependencies_parsed_at":"2023-01-27T18:15:31.890Z","dependency_job_id":"6700c357-a56d-4b43-a558-494f3f42361b","html_url":"https://github.com/getkirby/kql","commit_stats":{"total_commits":87,"total_committers":4,"mean_commits":21.75,"dds":0.367816091954023,"last_synced_commit":"42e32cb1fa3e295086af99ace08b4510f684db4f"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getkirby%2Fkql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getkirby%2Fkql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getkirby%2Fkql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getkirby%2Fkql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/getkirby","download_url":"https://codeload.github.com/getkirby/kql/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254464895,"owners_count":22075570,"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","cms","file-based","flat-file","headless-cms","json","kirby","kql","query","query-language"],"created_at":"2024-08-03T01:03:09.628Z","updated_at":"2025-05-16T04:05:34.411Z","avatar_url":"https://github.com/getkirby.png","language":"PHP","funding_links":["https://getkirby.com/buy"],"categories":["PHP"],"sub_categories":[],"readme":"# Kirby QL\n\nKirby's Query Language API combines the flexibility of Kirby's data structures, the power of GraphQL and the simplicity of REST.\n\nThe Kirby QL API takes POST requests with standard JSON objects and returns highly customized results that fit your application.\n\n## Playground\n\nYou can play in our [KQL sandbox](https://kql.getkirby.com). The sandbox is based on the Kirby starterkit.\n\n\u003e ℹ️ Source code of the playground is [available on GitHub](https://github.com/getkirby/kql.getkirby.com).\n\n## Example\n\nGiven a POST request to: `/api/query`\n\n```json\n{\n  \"query\": \"page('photography').children\",\n  \"select\": {\n    \"url\": true,\n    \"title\": true,\n    \"text\": \"page.text.markdown\",\n    \"images\": {\n      \"query\": \"page.images\",\n      \"select\": {\n        \"url\": true\n      }\n    }\n  },\n  \"pagination\": {\n    \"limit\": 10\n  }\n}\n```\n\n\u003cdetails open\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```json\n{\n  \"code\": 200,\n  \"result\": {\n    \"data\": [\n      {\n        \"url\": \"https://example.com/photography/trees\",\n        \"title\": \"Trees\",\n        \"text\": \"Lorem \u003cstrong\u003eipsum\u003c/strong\u003e …\",\n        \"images\": [\n          {\n            \"url\": \"https://example.com/media/pages/photography/trees/1353177920-1579007734/cheesy-autumn.jpg\"\n          },\n          {\n            \"url\": \"https://example.com/media/pages/photography/trees/1940579124-1579007734/last-tree-standing.jpg\"\n          },\n          {\n            \"url\": \"https://example.com/media/pages/photography/trees/3506294441-1579007734/monster-trees-in-the-fog.jpg\"\n          }\n        ]\n      },\n      {\n        \"url\": \"https://example.com/photography/sky\",\n        \"title\": \"Sky\",\n        \"text\": \"\u003ch1\u003eDolor sit amet\u003c/h1\u003e …\",\n        \"images\": [\n          {\n            \"url\": \"https://example.com/media/pages/photography/sky/183363500-1579007734/blood-moon.jpg\"\n          },\n          {\n            \"url\": \"https://example.com/media/pages/photography/sky/3904851178-1579007734/coconut-milkyway.jpg\"\n          }\n        ]\n      }\n    ],\n    \"pagination\": {\n      \"page\": 1,\n      \"pages\": 1,\n      \"offset\": 0,\n      \"limit\": 10,\n      \"total\": 2\n    }\n  },\n  \"status\": \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n## Installation\n\n### Manual\n\n[Download](https://github.com/getkirby/kql/releases) and copy this repository to `/site/plugins/kql` of your Kirby installation.\n\n### Composer\n\n```bash\ncomposer require getkirby/kql\n```\n\n## Documentation\n\n### API Endpoint\n\nKQL adds a new `query` API endpoint to your Kirby API (i.e. `yoursite.com/api/query`). This endpoint [requires authentication](https://getkirby.com/docs/guide/api/authentication).\n\nYou can switch off authentication in your config at your own risk:\n\n```php\nreturn [\n  'kql' =\u003e [\n    'auth' =\u003e false\n  ]\n];\n```\n\n### Sending POST Requests\n\nYou can use any HTTP request library in your language of choice to make regular POST requests to your `/api/query` endpoint. In this example, we are using [the `fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and JavaScript to retrieve data from our Kirby installation.\n\n```js\nconst api = \"https://yoursite.com/api/query\";\nconst username = \"apiuser\";\nconst password = \"strong-secret-api-password\";\n\nconst headers = {\n  Authorization: \"Basic \" + Buffer.from(`${username}:${password}`).toString(\"base64\"),\n  \"Content-Type\": \"application/json\",\n  Accept: \"application/json\",\n};\n\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"page('notes').children\",\n    select: {\n      title: true,\n      text: \"page.text.kirbytext\",\n      slug: true,\n      date: \"page.date.toDate('d.m.Y')\",\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n### `query`\n\nWith the query, you can fetch data from anywhere in your Kirby site. You can query fields, pages, files, users, languages, roles and more.\n\n#### Queries Without Selects\n\nWhen you don't pass the select option, Kirby will try to come up with the most useful result set for you. This is great for simple queries.\n\n##### Fetching the Site Title\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"site.title\",\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: \"Kirby Starterkit\",\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n##### Fetching a List of Page IDs\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"site.children\",\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: [\n    \"photography\",\n    \"notes\",\n    \"about\",\n    \"error\",\n    \"home\"\n  ],\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n#### Running Field Methods\n\nQueries can even execute field methods.\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"site.title.upper\",\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: \"KIRBY STARTERKIT\",\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n### `select`\n\nKQL becomes really powerful by its flexible way to control the result set with the select option.\n\n#### Select Single Properties and Fields\n\nTo include a property or field in your results, list them as an array. Check out our [reference for available properties](https://getkirby.com/docs/reference) for pages, users, files, etc.\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"site.children\",\n    select: [\"title\", \"url\"],\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: {\n    data: [\n      {\n        title: \"Photography\",\n        url: \"/photography\"\n      },\n      {\n        title: \"Notes\",\n        url: \"/notes\"\n      },\n      {\n        title: \"About us\",\n        url: \"/about\"\n      },\n      {\n        title: \"Error\",\n        url: \"/error\"\n      },\n      {\n        title: \"Home\",\n        url: \"/\"\n      }\n    ],\n    pagination: {\n      page: 1,\n      pages: 1,\n      offset: 0,\n      limit: 100,\n      total: 5\n    }\n  },\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\nYou can also use the object notation and pass true for each key/property you want to include.\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"site.children\",\n    select: {\n      title: true,\n      url: true,\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: {\n    data: [\n      {\n        title: \"Photography\",\n        url: \"/photography\"\n      },\n      {\n        title: \"Notes\",\n        url: \"/notes\"\n      },\n      {\n        title: \"About us\",\n        url: \"/about\"\n      },\n      {\n        title: \"Error\",\n        url: \"/error\"\n      },\n      {\n        title: \"Home\",\n        url: \"/\"\n      }\n    ],\n    pagination: { ... }\n  },\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n#### Using Queries for Properties and Fields\n\nInstead of passing true, you can also pass a string query to specify what you want to return for each key in your select object.\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"site.children\",\n    select: {\n      title: \"page.title\",\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: {\n    data: [\n      {\n        title: \"Photography\",\n      },\n      {\n        title: \"Notes\",\n      },\n      ...\n    ],\n    pagination: { ... }\n  },\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n#### Executing Field Methods\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"site.children\",\n    select: {\n      title: \"page.title.upper\",\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: {\n    data: [\n      {\n        title: \"PHOTOGRAPHY\",\n      },\n      {\n        title: \"NOTES\",\n      },\n      ...\n    ],\n    pagination: { ... }\n  },\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n#### Creating Aliases\n\nString queries are a perfect way to create aliases or return variations of the same field or property multiple times.\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"page('notes').children\",\n    select: {\n      title: \"page.title\",\n      upperCaseTitle: \"page.title.upper\",\n      lowerCaseTitle: \"page.title.lower\",\n      guid: \"page.id\",\n      date: \"page.date.toDate('d.m.Y')\",\n      timestamp: \"page.date.toTimestamp\",\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: {\n    data: [\n      {\n        title: \"Explore the universe\",\n        upperCaseTitle: \"EXPLORE THE UNIVERSE\",\n        lowerCaseTitle: \"explore the universe\",\n        guid: \"notes/explore-the-universe\",\n        date: \"21.04.2018\",\n        timestamp: 1524316200\n      },\n      { ... },\n      { ... },\n      ...\n    ],\n    pagination: { ... }\n  },\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n#### Subqueries\n\nWith such string queries you can of course also include nested data\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"page('photography').children\",\n    select: {\n      title: \"page.title\",\n      images: \"page.images\",\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: {\n    data: [\n      {\n        title: \"Trees\",\n        images: [\n          \"photography/trees/cheesy-autumn.jpg\",\n          \"photography/trees/last-tree-standing.jpg\",\n          \"photography/trees/monster-trees-in-the-fog.jpg\",\n          \"photography/trees/sharewood-forest.jpg\",\n          \"photography/trees/stay-in-the-car.jpg\"\n        ]\n      },\n      { ... },\n      { ... },\n      ...\n    ],\n    pagination: { ... }\n  },\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n#### Subqueries With Selects\n\nYou can also pass an object with a `query` and a `select` option\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"page('photography').children\",\n    select: {\n      title: \"page.title\",\n      images: {\n        query: \"page.images\",\n        select: {\n          filename: true,\n        },\n      },\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: {\n    data: [\n      {\n        title: \"Trees\",\n        images: {\n          {\n            filename: \"cheesy-autumn.jpg\"\n          },\n          {\n            filename: \"last-tree-standing.jpg\"\n          },\n          {\n            filename: \"monster-trees-in-the-fog.jpg\"\n          },\n          {\n            filename: \"sharewood-forest.jpg\"\n          },\n          {\n            filename: \"stay-in-the-car.jpg\"\n          }\n        }\n      },\n      { ... },\n      { ... },\n      ...\n    ],\n    pagination: { ... }\n  },\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n### Pagination\n\nWhenever you query a collection (pages, files, users, roles, languages) you can limit the resultset and also paginate through entries. You've probably already seen the pagination object in the results above. It is included in all results for collections, even if you didn't specify any pagination settings.\n\n#### `limit`\n\nYou can specify a custom limit with the limit option. The default limit for collections is 100 entries.\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"page('notes').children\",\n    pagination: {\n      limit: 5,\n    },\n    select: {\n      title: \"page.title\",\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: {\n    data: [\n      {\n        title: \"Across the ocean\"\n      },\n      {\n        title: \"A night in the forest\"\n      },\n      {\n        title: \"In the jungle of Sumatra\"\n      },\n      {\n        title: \"Through the desert\"\n      },\n      {\n        title: \"Himalaya and back\"\n      }\n    ],\n    pagination: {\n      page: 1,\n      pages: 2,\n      offset: 0,\n      limit: 5,\n      total: 7\n    }\n  },\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n#### `page`\n\nYou can jump to any page in the resultset with the `page` option.\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"page('notes').children\",\n    pagination: {\n      page: 2,\n      limit: 5,\n    },\n    select: {\n      title: \"page.title\",\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e🆗 Response\u003c/summary\u003e\n\n```js\n{\n  code: 200,\n  result: {\n    data: [\n      {\n        title: \"Chasing waterfalls\"\n      },\n      {\n        title: \"Exploring the universe\"\n      }\n    ],\n    pagination: {\n      page: 2,\n      pages: 2,\n      offset: 5,\n      limit: 5,\n      total: 7\n    }\n  },\n  status: \"ok\"\n}\n```\n\n\u003c/details\u003e\n\n### Pagination in Subqueries\n\nPagination settings also work for subqueries.\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"page('photography').children\",\n    select: {\n      title: \"page.title\",\n      images: {\n        query: \"page.images\",\n        pagination: {\n          page: 2,\n          limit: 5,\n        },\n        select: {\n          filename: true,\n        },\n      },\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n### Multiple Queries in a Single Call\n\nWith the power of selects and subqueries you can basically query the entire site in a single request\n\n```js\nconst response = await fetch(api, {\n  method: \"post\",\n  body: JSON.stringify({\n    query: \"site\",\n    select: {\n      title: \"site.title\",\n      url: \"site.url\",\n      notes: {\n        query: \"page('notes').children.listed\",\n        select: {\n          title: true,\n          url: true,\n          date: \"page.date.toDate('d.m.Y')\",\n          text: \"page.text.kirbytext\",\n        },\n      },\n      photography: {\n        query: \"page('photography').children.listed\",\n        select: {\n          title: true,\n          images: {\n            query: \"page.images\",\n            select: {\n              url: true,\n              alt: true,\n              caption: \"file.caption.kirbytext\",\n            },\n          },\n        },\n      },\n      about: {\n        text: \"page.text.kirbytext\",\n      },\n    },\n  }),\n  headers,\n});\n\nconsole.log(await response.json());\n```\n\n### Allowing Methods\n\nKQL is very strict with allowed methods by default. Custom page methods, file methods or model methods are not allowed to make sure you don't miss an important security issue by accident. You can allow additional methods though.\n\n#### Allow List\n\nThe most straight forward way is to define allowed methods in your config.\n\n```php\nreturn [\n  'kql' =\u003e [\n    'methods' =\u003e [\n      'allowed' =\u003e [\n        'MyCustomPage::cover'\n      ]\n    ]\n  ]\n];\n```\n\n#### DocBlock Comment\n\nYou can also add a comment to your methods' doc blocks to allow them:\n\n```php\nclass MyCustomPage extends Page\n{\n  /**\n   * @kql-allowed\n   */\n  public function cover()\n  {\n    return $this-\u003eimages()-\u003efindBy('name', 'cover') ?? $this-\u003eimage();\n  }\n}\n```\n\nThis works for model methods as well as for custom page methods, file methods or other methods defined in plugins.\n\n```php\nKirby::plugin('your-name/your-plugin', [\n  'pageMethods' =\u003e [\n    /**\n     * @kql-allowed\n     */\n    'cover' =\u003e function () {\n      return $this-\u003eimages()-\u003efindBy('name', 'cover') ?? $this-\u003eimage();\n    }\n  ]\n]);\n```\n\n### Blocking Methods\n\nYou can block individual class methods that would normally be accessible by listing them in your config:\n\n```php\nreturn [\n  'kql' =\u003e [\n    'methods' =\u003e [\n      'blocked' =\u003e [\n        'Kirby\\Cms\\Page::url'\n      ]\n    ]\n  ]\n];\n```\n\n### Blocking Classes\n\nSometimes you might want to reduce access to various parts of the system. This can be done by blocking individual methods (see above) or by blocking entire classes.\n\n```php\nreturn [\n  'kql' =\u003e [\n    'classes' =\u003e [\n      'blocked' =\u003e [\n        'Kirby\\Cms\\User'\n      ]\n    ]\n  ]\n];\n```\n\nNow, access to any user is blocked.\n\n### Custom Classes and Interceptors\n\nIf you want to add support for a custom class or a class in Kirby's source that is not supported yet, you can list your own interceptors in your config\n\n```php\nreturn [\n  'kql' =\u003e [\n    'interceptors' =\u003e [\n      'Kirby\\Cms\\System' =\u003e 'SystemInterceptor'\n    ]\n  ]\n];\n```\n\nYou can put the class for such a custom interceptor in a plugin for example.\n\n```php\nclass SystemInterceptor extends Kirby\\Kql\\Interceptors\\Interceptor\n{\n  public const CLASS_ALIAS = 'system';\n\n  protected $toArray = [\n    'isInstallable',\n  ];\n\n  public function allowedMethods(): array\n  {\n    return [\n      'isInstallable',\n    ];\n  }\n}\n```\n\nInterceptor classes are pretty straight forward. With the `CLASS_ALIAS` you can give objects with that class a short name for KQL queries. The `$toArray` property lists all methods that should be rendered if you don't run a subquery. I.e. in this case `kirby.system` would render an array with the `isInstallable` value.\n\nThe `allowedMethods` method must return an array of all methods that can be access for this object. In addition to that you can also create your own custom methods in an interceptor that will then become available in KQL.\n\n```php\nclass SystemInterceptor extends Kirby\\Kql\\Interceptors\\Interceptor\n{\n  ...\n\n  public function isReady()\n  {\n    return 'yes it is!';\n  }\n}\n```\n\nThis custom method can now be used with `kirby.system.isReady` in KQL and will return `yes it is!`\n\n### Unintercepted Classes\n\nIf you want to fully allow access to an entire class without putting an interceptor in between, you can add the class to the allow list in your config:\n\n```php\nreturn [\n  'kql' =\u003e [\n    'classes' =\u003e [\n      'allowed' =\u003e [\n        'Kirby\\Cms\\System'\n      ]\n    ]\n  ]\n];\n```\n\nThis will introduce full access to all public class methods. This can be very risky though and you should avoid this if possible.\n\n### No Mutations\n\nKQL only offers access to data in your site. It does not support any mutations. All destructive methods are blocked and cannot be accessed in queries.\n\n## Plugins\n\n- [KQL + 11ty](https://github.com/getkirby/eleventykit)\n- [KQL + Nuxt](https://nuxt-kql.jhnn.dev)\n\n## What's Kirby?\n- **[getkirby.com](https://getkirby.com)** – Get to know the CMS.\n- **[Try it](https://getkirby.com/try)** – Take a test ride with our online demo. Or download one of our kits to get started.\n- **[Documentation](https://getkirby.com/docs/guide)** – Read the official guide, reference and cookbook recipes.\n- **[Issues](https://github.com/getkirby/kirby/issues)** – Report bugs and other problems.\n- **[Feedback](https://feedback.getkirby.com)** – You have an idea for Kirby? Share it.\n- **[Forum](https://forum.getkirby.com)** – Whenever you get stuck, don't hesitate to reach out for questions and support.\n- **[Discord](https://chat.getkirby.com)** – Hang out and meet the community.\n- **[Mastodon](https://mastodon.social/@getkirby)** – Spread the word.\n- **[Bluesky](https://bsky.app/profile/getkirby.com)** – Spread the word.\n\n---\n\n## License\n\n[MIT](./LICENSE) License © 2020-2023 [Bastian Allgeier](https://getkirby.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetkirby%2Fkql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgetkirby%2Fkql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetkirby%2Fkql/lists"}