{"id":18450799,"url":"https://github.com/patrixr/pocket-cms","last_synced_at":"2025-07-19T10:34:03.693Z","repository":{"id":57326532,"uuid":"151804303","full_name":"patrixr/pocket-cms","owner":"patrixr","description":"☁️ A pocket sized CMS written for nodejs","archived":false,"fork":false,"pushed_at":"2019-02-16T05:02:16.000Z","size":703,"stargazers_count":17,"open_issues_count":3,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-07-04T02:44:09.013Z","etag":null,"topics":["backend","cms","express","hacktoberfest","headless-cms","node","nosql","schema"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/patrixr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-06T04:20:27.000Z","updated_at":"2025-06-11T16:20:30.000Z","dependencies_parsed_at":"2022-09-21T01:50:21.567Z","dependency_job_id":null,"html_url":"https://github.com/patrixr/pocket-cms","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/patrixr/pocket-cms","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrixr%2Fpocket-cms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrixr%2Fpocket-cms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrixr%2Fpocket-cms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrixr%2Fpocket-cms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/patrixr","download_url":"https://codeload.github.com/patrixr/pocket-cms/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrixr%2Fpocket-cms/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265917073,"owners_count":23848853,"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":["backend","cms","express","hacktoberfest","headless-cms","node","nosql","schema"],"created_at":"2024-11-06T07:26:30.863Z","updated_at":"2025-07-19T10:34:03.628Z","avatar_url":"https://github.com/patrixr.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pocket CMS\n\n![](https://forthebadge.com/images/badges/uses-js.svg)\n![](https://forthebadge.com/images/badges/made-with-vue.svg)\n![](https://travis-ci.org/patrixr/pocket-cms.svg?branch=master)\n\n### A pocket sized CMS written for Node and Express.\n\nDesigned as a non-intrusive middleware. Pocket offers :\n\n* Automatic API generation\n* Schema validation ([pocket schema](https://github.com/patrixr/pocket-schema))\n* User and session management\n* Admin panel\n* Multi-db support (Currently Mongo and Nedb)\n* File uploads\n* Server stats\n\nFeatures planned: \n\n* Logs\n* API Keys\n* Plugin support\n\n![](http://g.recordit.co/nCibS69Xzw.gif)\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*\n\n- [Getting started](#getting-started)\n- [Configuration](#configuration)\n  - [Datastores](#datastores)\n    - [Mongo](#mongo)\n    - [Disk](#disk)\n  - [Filestores](#filestores)\n    - [Disk](#disk-1)\n- [Using the CMS](#using-the-cms)\n  - [Resources](#resources)\n    - [Methods](#methods)\n      - [Creating a record](#creating-a-record)\n      - [Updating a record](#updating-a-record)\n      - [Updating multiple records](#updating-multiple-records)\n      - [Reading a single record](#reading-a-single-record)\n      - [Reading multiple records](#reading-multiple-records)\n      - [Removing a record](#removing-a-record)\n      - [Removing multiple records](#removing-multiple-records)\n      - [Attaching a file to a record](#attaching-a-file-to-a-record)\n      - [Deleting an attachment](#deleting-an-attachment)\n      - [Reading an attachment](#reading-an-attachment)\n  - [Schema](#schema)\n    - [CMS properties](#cms-properties)\n    - [Indexes](#indexes)\n    - [Hooks](#hooks)\n  - [Users](#users)\n    - [Groups](#groups)\n  - [REST API](#rest-api)\n    - [Authentication](#authentication)\n    - [Resource management](#resource-management)\n    - [ACL](#acl)\n      - [Admins](#admins)\n      - [Schema access configuration](#schema-access-configuration)\n      - [Group access configuration](#group-access-configuration)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Getting started\n\nFirstly, install `pocket-cms` as a dependency of your project.\n\n```bash\nnpm install --save pocket-cms\n```\n\nIn your Node project, hook pocket to your express server :\n\n```javascript\nvar express = require(\"express\");\nvar Pocket = require(\"pocket-cms\");\n\nvar app = express();\nvar cms = new Pocket();\n\napp.use(cms.middleware());\n\napp.listen(3000, () =\u003e { console.log('Server running'); })\n```\nDone ! A pocket cms is now running in your server.\n\n## Configuration\n\nPocket takes an optional configuration object during construction.\n\ne.g\n\n```javascript\nnew Pocket({\n    datastore: {\n        adapter: 'mongo',\n        options: {\n            dbName: 'pocket_dev',\n            url: 'localhost:27017'\n        }\n    }\n})\n```\n\nThe following options are available:\n\n| Key  | Description | Type | Default value |\n| ------------- | ------------- | ------------- | ------------- |\n| **session**  | Authentication configuration |||\n| session.secret  | JWT secret  | String | random |\n| session.expiresIn | Session expiry time in seconds | Number | 60 days |\n| **datastore** | Database configuration |||\n| datastore.adapter | Available options : mongo, disk | String | disk |\n| datastore.options | Datastore specific options | Object | |\n| **filestore** | File upload configuration |||\n| filestore.adapter | Available options: disk | String | disk |\n| filestore.options | Filestore specific options | Object | |\n\n### Datastores\n\nCurrently the following stores are available :\n\n#### Mongo\n\nThe mongodb adapter **requires** the following options :\n\n| Key  | Description | Type |\n| ------------- | ------------- | ------------- |\n| **dbName**  | Name of the mongo database | String |\n| **url** | Database url (e.g `user:password@localhost:27017`) | String |\n\n#### Disk\n\nThe disk adapter supports the following options :\n\n| Key  | Description | Type | Defaults |\n| ------------- | ------------- | ------------- | ------------- |\n| **dataFolder**  | Folder in which the data will be stored | String | `${POCKET_HOME}/${ENV}_db` |\n\n### Filestores\n\nCurrently Pocket only supports files saved on disk.\nS3 support is on the roadmap\n\n#### Disk\n\n| Key  | Description | Type | Defaults |\n| ------------- | ------------- | ------------- | ------------- |\n| **uploadFolder**  | Folder in which the files will be stored | String | `${POCKET_HOME}/${ENV}_db/uploads` |\n\n\n\n## Using the CMS\n\n### Resources\n\nPocket uses the generic term `Resource` to define a data type within the CMS.\nEach resource has :\n\n* A schema\n\t* Enables validation of posted data\n\t* Allows the admin panel to generate a form to edit the records\n* An automatically generated rest endpoint\n\nResources are created using the following CMS api :\n\n```javascript\nlet cms = new Pocket();\n\ncms.resource('cars', schema);\n```\n\n\nResources are retrieved using the following CMS api :\n\n```javascript\nconst cars = cms.resource('cars');\n\nawait cars.find({});\n```\n\n#### Methods\n\n\n##### Creating a record\n\n`await resource.create(payload, opts = {})` will create a record matching the payload parameter. Options :\n\n* `skipValidation`  allows the record to be created without being validated against its schema. *Use with care*\n* `userId` will set the `_createdBy` property of a record\n\n##### Updating a record\n\n`await resource.mergeOne(id, payload, opts = {})` will update the record specified by `id` by overriding the properties set in `payload`. Options :\n\n* `skipValidation`  allows the record to be created without being validated against its schema. *Use with care*\n\n##### Updating multiple records\n\n`await resource.update(query, operations, options = {})` will update records specified by `query` with the `operations` formatted in a mongo like syntax. Options :\n\n* `multi` will enable the update to run on multiple records. Defaults to true.\n\n##### Reading a single record\n\n`await resource.get(id)` will return the record specified by `id`\n\n##### Reading multiple records\n\n`await resource.find(query = {}, opts = {})` will return records that match the `query` argument. Options :\n\n* `pageSize` and `page` will allow to retrieve paginated records\n\n##### Removing a record\n\n`await resource.removeOne(id)` will delete the record specified by `id`\n\n##### Removing multiple records\n\n`await resource.remove(query, opts = {})` will remove all elements that match the `query` argument. Options :\n\n* `multi` will allow multiple records to be removed if set to true. Otherwise only one of them will be deleted\n\n##### Attaching a file to a record\n\n`await resource.attach(recordId, fileName, file)` will save the file specified by the `file` argument in the file store, add add it to the record's `_attachments` list. `file` can either be a **String** pointing to the file on disk or a **Stream**\n\n##### Deleting an attachment\n\n`await resource.deleteAttachment(recordId, attachmentId)` will delete the file from the file store and remove the attachment from its record\n\n##### Reading an attachment\n\n`await resource.readAttachment(attachmentId)` will return a node stream of the file\n\n\n### Schema\n\nPocket exposes a `Schema` class which can be used to create a resource's schema.\nMore details can be found on the [Pocket Schema](https://github.com/patrixr/pocket-schema) repo.\n\ne.g\n\n```javascript\nconst Pocket = require('pocket-cms');\nconst { Schema } = Pocket;\n\nconst carSchema = new Schema({\n    additionalProperties: false,\n    fields: {\n        name: {\n\t    type: 'string',\n\t    index: {\n\t\tunique: true\n\t    }\n\t}\n\tbrand: {\n\t    type: 'text',\n\t    maxLength: 64\n\t},\n\tnoOfWheels: {\n\t    type: 'number',\n\t    required: true,\n\t},\n\tcolor: {\n\t    type: 'select',\n\t    options: ['red', 'yellow', 'magenta']\n\t},\n\ttags: {\n\t    type: 'array',\n\t    items: { type: 'string' }\n\t}\n    }\n});\n\n```\n\nThe following types are available :\n\n* `any`\n\n* `array|list`  - options:\n\t* `items?` A field definition of the expected array items\n\n* `checkbox|boolean`\n\n* `date`  - options:\n\t* `format?` The expected date format (defaults to YYYY-MM-DD)\n\n* `datetime`\n\n* `email`  - options:\n\t* `match?` A regular expression to match the email against\n\n* `map`  - options:\n\t* `items?` A field definition of the expected map items\n\n* `multiselect`  - options:\n\t* `options` List or options to select from. An async function can also be passed\n\n* `number`  - options:\n\t* `min?` Minimum allowed value\n\t* `max?` Maximum allowed value\n\n* `object|json`  - options:\n\t* `schema?` Schema used to validate the object against\n\n* `password`  - options:\n\t* `minLength?` The minimum length of the password\n\n* `select|enum`  - options:\n\t* `options` List or options to select from. An async function can also be passed\n\n* `text|string`  - options:\n\t* `minLength?` The minimum length of the string\n\t* `maxLength?` The maximum length of the string\n\t* `match?` A regular expression to match the string against\n\n* `time`\n\n* `timestamp`\n\n#### CMS properties\n\nWhen records are created/updated the CMS automatically adds and keeps track of a number or *private* properties which **cannot** be updated manually. All those private properties start by underscore `_`.\n\nCurrently those are:\n\n* `_id`\n* `_createdAt`\n* `_updatedAt`\n* `_createdBy`\n* `_attachments` the list of attachments\n\n#### Indexes\n\nTo mark a field as a database index, its schema field supports the `index` parameter. Which can either be :\n\n* A `true|false` value\n* An object with the following properties :\n\t* **unique** `true|false`\n\n\nExample :\n\n```javascript\nconst Pocket = require('pocket-cms');\nconst { Schema } = Pocket;\n\nconst person = new Schema({\n    fields: {\n        name: {\n            type: 'string',\n\t    index: {\n\t        unique: true\n\t}\n    }\n});\n```\n\n#### Hooks\n\nPre and Post hooks can be added to a schema which allow adding extra functionality and ACL to the CMS.\n\nAvailable api :\n\n* `schema.before(action, method)`\n* `schema.after(action, method)`\n\nAvailable methods\n\n* `create`\n* `remove`\n* `update`\n* `save`\n* `validate`\n* `read`\n\nExample usage\n\n```javascript\nconst Pocket = require('pocket-cms');\nconst { Schema } = Pocket;\n\nconst postSchema = new Schema({\n    fields: {\n        message: {\n\t    type: 'string',\n\t}\n    }\n})\n.before('save', async ({ record }, ctx) =\u003e {\n    const { user } = ctx;\n\n    if (await userCheck(user, record) === false) {\n        throw 'User should not save this record';\n    }\n});\n\nconst cms = new Pocket();\n\npocket.resource('posts', postSchema);\n```\n\n### Users\n\nThe Pocket CMS class exposes a user manager that can be used to create/remove and authenticate users.\n\ne.g\n\n```javascript\nconst Pocket = require('pocket-cms');\n\nconst cms = new Pocket();\n\n// Creating a user\nawait pocket.users.create('username', 'password', [ 'users' ]);\n\n// Authenticating a user\nconst user = await pocket.users.auth('username', 'password');\n\n// Extracting a user from the JWT auth token\nconst user = await pocket.users.fromJWT(token)\n```\n\nThe underlying resource is named `_users`\n\n#### Groups\n\nBy default the following groups are created :\n\n* `admins`\n* `users`\n\nGroups can be added by using the underlying resource `_groups`  \n\n### REST API\n\nFor each resource created, a generic rest api is automatically created for it.\n\nHere's a rundown of the different endpoints\n\n#### Authentication\n\n* `POST /users/signup` to create a user. The following JSON body is expected\n\t* `username`\n\t* `password`\n\t* `groups` (defaults to `['users']`)\n\n* `POST /users/login` to log in a user.   \n**Important**: This endpoint will return a Java Web Token, which should be included into following requests -\u003e `Authorization: Bearer \u003ctoken\u003e`. The following JSON body is expected\n\t* `username`\n\t* `password`  \n\n* `POST /users/logout` to logout out. **NOTE:** As authentication is done via JWT, this endpoint doesn't actually do anything. It exists as a placeholder for future additions (hooks/logs/etc)\n\n* `GET /users/status` to retrieve the user and status of an existing JWT Token\n\n#### Resource management\n\n* `GET /rest/{resource}` lists records for the given resource. Available options :\n\t* `pageSize` - The number of records to return per page\n\t* `page` - The page to return\n\n* `GET /rest/{resource}/{id}` returns a single record specified by `id`\n\n* `POST /rest/{resource}` creates a record of the `resource` type\n\n* `PUT /rest/{resource}/{id}` updates the record specified by `id` of the `resource` type\n\n* `DELETE /rest/{resource}/{id} deletes the record specified by `id`\n\n* `POST /rest/{resource}/{id}/attachments` uploads a file and a attach it to the record specified by `id`\n\n* `GET /rest/{resource}/{id}/attachments/{attachmentId}` downloads the attachment of a record\n\n* `DELETE /rest/{resource}/{id}/attachments/{attachmentId}` deletes the attachment of a record\n\n\nThe resource key of the endpoints listed above can all be prefixed with a user id to filter on records createdBy that user.  \n  \ne.g  \n  \n`GET /rest/users/:userId/{resource}/{id}` will return only records created by the user specified by `userId`\n\n\n#### ACL\n\nThere are multiple rules and ways to control the access of resources by users.\n\nWe either `allow` or `deny` actions on certain resources.\nThe following actions exist:\n\n* read\n* create\n* update\n* remove\n\n##### Admins\n\nUsers from the `admins` group are whitelisted and have permission to make any change to any resources.\n\nPrivate CMS resources (prefixed with `_`) cannot be modified by any other group. Currently those are :\n\n* `_users`\n* `_groups`\n\n\n##### Schema access configuration\n\nA entire resource can be configured to only be accessible to a certain set of groups.\n\nThat is done on the schema level with the following 2 methods :\n* `schema.allow(group, actions[])` \n* `deny(group, actions[])`  \n\n**Note:** A wildcard `*` can be used as a group name to represent all of them\n\nExample :\n\n```javascript\nconst Pocket = require('pocket-cms');\nconst { Schema } = Pocket;\n\nconst postSchema = new Schema({\n    fields: {\n        message: {\n        type: 'string'\n        }\n    }\n})\n.allow('users', [ 'read' ])\n.allow('moderators', [ 'read', 'create', 'update', 'delete' ])\n```\n  \n\n##### Group access configuration\n\nA group can be given access to a resource through its `permissions` field.\n\ne.g\n\n```javascript\n\npocket.resource('_groups').create({\n    name: 'moderators',\n    permissions: {\n        '*': ['read'],\n        'posts': ['read', 'create', 'update', 'delete' ]\n    }\n});\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatrixr%2Fpocket-cms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpatrixr%2Fpocket-cms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatrixr%2Fpocket-cms/lists"}