A document-based headless CMS with API + back office.
Made primarly for editorial content management.
Stock definitions are modelled after Schema.org vocabulary.
- 📑 Paper CMS 📝
- 🏁 Usage
- Some views 👀
- ❄️ Features
- Auto-generated forms from OpenAPI definitions
- JSON Schema validation on run-time for both client and server sides
- UI Schema augmentation (non-standard)
- JSON files database
- Image(s) upload + dynamic resize while fetching + caching
- JWT authentication for both human editors and API consumers
- Full live reload for models / client / server
- Document revisions history
- Soft deletion, restore from trash
- "Has many" relationship via references
- Nested referenced documents editing
- Schema.org inspired default definitions
- OpenAPI 3 UI (Swagger)
- Wysiwyg HTML Editor
- Custom fields and widgets
- 🤔 Why?
- ℹ️ Project insights
👉 Check here for a Paper CMS online tryout.
🔁 Default dataset is reseted periodically.
🔑 Use good ol' admin
/ password
.
# Clone this repo
git clone git@github.com:JulianCataldo/paper-cms.git
cd paper-cms
code ./
# Install all dependencies
npm run deps:i
# Run and watch API + back office
npm run dev
# Open back office in browser
open http://localhost:7777
# Default credentials
# > User : `admin`
# > Password : `password`
# Edit API definition
code -r ./models/api-v1.yaml
# Created entries and uploaded medias + variations
# are stored in root project by default in `.data`
tree ./.data
cd ./docker
make rdc-std
-
# Data persistence folder
tree ./docker/std/.volume/.data
See the Makefile
for all containers variants and shorthands.
auto-formgen.mp4
client-validation.mp4
Browser and everything in between cannot be trusted for data transmission.
In Paper CMS, both sides are taken care of, thanks to AJV and JSON Schema specs.
For example, you might need to do manual data transformation, directly on entry files, skipping the back-office.
Use cases : search and replace, refactoring fields names, automatic external data ingestion…
Everytime a file is read OR written, it will pass through schema validation, so no silent models shifting while refactoring them.
additionalProperties: false
can be leveraged to be warn about orphaned fields.
ui-schema.mp4
Pure JSON Schema isn't designed (yet?) for declarative UI parameters.
Special dialects to cater for this are experimented here and here.
In Paper CMS, we're using react-jsonschema-form
so see
➠ General uiSchema Reference - react-jsonschema-form documentation
Settings are splitted in two different schemas at runtime.
Standard compliant JSON Schema alongside non standard UI Schema.
All your textual data is hosted unded ./data/docs
(root can be overriden with PAPER_DATA_DIR
).
Run multiple instances of Paper CMS easily by setting different PAPER_PORT
.
Quickly swip datasets : demo / tests / staging / production…
…
auth.mp4
When editing model definitions, server or client code, everything is re-runned/builded/bundled. Also, browser is automatically refreshed thanks to a long polling web socket.
You can also trigger browser refresh by hitting curl localhost:${PAPER_PORT:-7777}/reload
.
…
…
has-many-diverse-refs.mp4
When using an array of items for a property, and thanks to a special:
'ui:field': 'hyperlink'
combined with a:
pattern: '/(Person|Organization|Whatever)/(.*).json'
you will get an "has-many" heterogeneous references dynamic widget.
See the api-v1.yaml
for full examples.
A "sub" document is just another document reference.
There is no hierarchy or inheritance,
only cross-references betweens entities.
While you can create, edit or associate an existing document from the main document form,
only $refs
URI for child references actually gets recorded, not the entire subtree.
Everything can be a document collection, as soon as it make sense to reference the entity from elsewhere. Self-sufficient data should live embedded in the document itself.
There is no limit for nesting, though stacked modal editors force us to save only one (sub)document at a time: no full tree update propagation. This design choice is meant to prevent data-loss / infinite recursive nesting.
While Schema.org states that:
The type hierarchy presented on this site is not intended to be a 'global ontology' of the world.
While true, it provides a great starting point with commonly used taxonomies and entities relationships.
Schema.org is shining for content-driven web sites where SEO is crucial (but that's just an excuse).
While still in its infancy, JSON/LD is already bringing HATEOS concepts
into real-life applications, step-by-step, organically.
Regardless, feature-rich widgets are democratizing, and data crawling between third-parties is becoming more insightful.
The endgoal is to make Paper CMS a non-deterministic data ingestion / organization / retrieval
system.
At the end, an hypermedia API should be auto-discoverable.
Still, it's useful to rely on Swagger niceties for on-boarding an API.
Moreover, the OpenAPI 3 specs is adding links
, a notable concept for Hypermedia minded APIs.
richt-text-editor.mp4
For rich text editing, Paper CMS uses Quill under the hood.
Basic additional HTML sanitization is done server-side.
…
We are seeing the emergence of different patterns in the content management world like:
- Shared entities vocabularies
- Automatic web form generation
- Isomorphic user input runtime validation,
- Entity / Collection oriented information architecture
- Headless non-opinionated CMS APIs for JAMStack consumption
- Hypermedia for non-deterministic data fetching via refs. | links | URIs…
- Plain files data storage for operations convenience (with some trade-offs)
- Conventions over configuration, with extendabilities (models, widgets…)
All these concepts are explored at different levels of implementations
in Paper CMS.
While it's still an experiment, the end-goal is to provide a lightweight
solution suitable for projects which:
- Has ten-ish max. editors in charge
- Needs moderate authoring concurrency with silo-ed document edits
- Might needs frequent content updates
- Low needs for user-land data input
- Are in an eco-system of focused services where caching / CDNs / whatever are dedicated
To sum up: Paper CMS is good for editors-driven web sites, but is not a good fit for users-driven web apps.
Warning: Work in progress: NOT FOR PRODUCTION
Mono-repo. glued with PNPM recursive modules installation.
- Node.js
- ESBuild
- React
- MUI
- React Quill
- JSON Schema React
- AJV
- Express
- Sharp
- Single media management
- Batch media management
- JSON/LD API output conformance
- OpenAPI conformance
- Swagger integration
- Wider Schema.org support for stock definitions
- Basic users account management
- Media metadata with EXIF + IPTC support
- API collections' pagination
- Automatic bi-directional relationships ("Is part of many" <=> "Has many")
- Custom data fetching and population widgets for APIs / social networks content retrieval
- Might propose fully dynamic OpenAPI GUI builder right inside the back office, instead OR alongside YAML config.
- Might propose MongoDB/pSQL/S3 alternative to bare file storage, for increased mass and/or concurrency needs, but decreased portability.
- Might propose an option for storing media binaries in an S3 bucket.
,ggggggggggg,
dP""88""""""Y8,
Yb, 88 `8b
`" 88 ,8P
88aaaad8P"
88""""",gggg,gg gg,gggg, ,ggg, ,gggggg,
88 dP" "Y8I I8P" "Yb i8" "8i dP""""8I
88 i8' ,8I I8' ,8i I8, ,8I ,8' 8I
88 ,d8, ,d8b,,I8 _ ,d8' `YbadP' ,dP Y8,
88 P"Y8888P"`Y8PI8 YY88888P888P"Y8888P `Y8
I8
I8 ,gggg, ,ggg, ,ggg,_,ggg, ,gg,
I8 ,88"""Y8b,dP""Y8dP""Y88P""Y8b i8""8i
I8 d8" `Y8Yb, `88' `88' `88 `8,,8'
I8 d8' 8b d8 `" 88 88 88 `88'
I8 ,8I "Y88P' 88 88 88 dP"8,
I8' 88 88 88 dP' `8a
d8 88 88 88 dP' `Yb
Y8, 88 88 88 _ ,dP' I8
`Yba,,_____, 88 88 Y8,"888,,____,dP
`"Y8888888 88 88 `Y8a8P"Y88888P"