1. Home
  2. Docs
  3. Engineering
  4. API Backend Frameworks Comparison: Strapi vs Frappe (ERPNext) vs NestJS vs Meteor vs WordPress

API Backend Frameworks Comparison: Strapi vs Frappe (ERPNext) vs NestJS vs Meteor vs WordPress

We use all of these API Backend frameworks: Strapi, Frappe (ERPNext), NestJS, and WordPress. Strapi is a headless CMS. NestJS is a low-level NodeJS microservice framework. Frappe is a complete framework that powers ERPNext. WordPress is a popular CMS which is extensible.

Now, how do they compare (at least in Lovia’s perspective)? For developing our own custom apps, which one should we use? This comparison attempts to review each framework and explain the reasoning behind our choices.

CriteriaStrapiFrappe (ERPNext)NestJSMeteorWordPress
TypeHeadless CMSApplication frameworkMicroservice frameworkCMS
Deployment Cost & ScalabilityManual using Docker / Kubernetes
File uploads can use S3
Manual using Docker / Kubernetes and NFS/CephServerless-ready
Recommended: aws-serverless-fastify
Default: aws-serverless-express
Note: Does not support GraphQL subscriptions
On VM: WordOps
Docker on Kubernetes with NFS/Ceph shared folder
DatabaseMongoDB, MariaDB, PostgreSQL, MySQLRecommended: MariaDB
Alternative: MySQL
Frappe only, not ERPNext: PostgreSQL
MongoDB, MariaDB, PostgreSQL, MySQL, Elasticsearch, Redis, and any other databaseMariaDB / MySQL
Multiple databasesYesNoYesCustom code
Multiple sites per instanceNoYesNoOnly if using Multisite Network
REST API Support for Custom TypesAutomaticAutomaticManual via ControllersPartial: Adding REST API Support to Existing Content Types
OpenAPI DocumentationAutomaticNoneManual via @nestjs/swaggerNone
Contributed plugin: WP API SwaggerUI
GraphQL Support for Custom TypesAutomaticNoneManual via @nestjs/graphqlNone
Contributed plugin (for built-in WordPress objects): WPGraphQL
File upload storageFilesystem / Amazon S3Filesystem / NFS / Ceph
Can mirror to Amazon S3 using contributed app: frappe-attachments-s3
Custom codeFilesystem
PubSub libraryNone.
GraphQL Subscriptions is still PR #6789.
Redis + ?None.
Can be added via Apollo graphql-subscriptions
PubSub transportNoneSocketIO (WebSockets, long-polling)None. Can use Apollo GraphQL Subscriptions over WebSockets.WebSocketsNone
API Backend Frameworks Comparison: Strapi vs Frappe (ERPNext) vs NestJS vs WordPress

Improving Portability

We’re trying to use Strapi by default (hopefully to also get future improvements), but also make it usable by NestJS if needed. The main reason is developer and admin productivity (this is similar reason why we use WordPress). If performance is an issue, the recommended approach is to utilize Elasticsearch and/or Redis.

So even when using NestJS, we’re trying to constrain the schema so that it plays well with Strapi. upload_file collection should also follow Strapi conventions. The REST API and GraphQL syntax should also follow Strapi’s conventions.

Strapi users-permissions_user Collection Structure

Note: password is randomly auto-generated and directly thrown away, never used. We instead use FusionAuth OpenID Connect, through ssoId field.

These user fields are locked by Strapi:

password*Password String
providerEnum: local | …
roleObjectId (of users-permissions_role collection)
created_byObjectId (of strapi_administrator collection)
updated_byObjectId (of strapi_administrator collection)
    "$oid": "5f2634bb855b5c1676e680a1"
  "confirmed": false,
  "blocked": false,
  "gender": "M",
  "relationship": "M",
  "username": "mananana",
  "email": "[email protected]",
  "password": "$2a$10$Px8aGpF8G.cZa.qbnPZBH.d8tjxTrvFzbI0qBL2AEqo6YSX6BhoFa",
  "addressCity": "Jakarta",
  "provider": "local",
    "$date": "2020-08-02T03:36:27.973Z"
    "$date": "2020-08-02T04:40:10.340Z"
  "__v": 0,
    "$oid": "5f26335df01fe80f3fcc8fe1"
  "role": null,
    "$oid": "5f26335df01fe80f3fcc8fe1"
  "addressCityDisplayName": "Jakarta",
    "$oid": "5f263527855b5c1676e680a2"
  "profileVisibility": "P",
  "age": 35

Strapi upload_file Collection Structure


Note: Strapi’s thumbnail for upload_file is mainly useful for backend. For frontend, we use Cloudimage’s transformation features instead, which provides e.g. face cropping.

  "_id": {
    "$oid": "5f263527855b5c1676e680a2"
  "name": "hendy-siete-square.jpg",
  "alternativeText": "",
  "caption": "",
  "hash": "hendy_siete_square_3a3520ac33",
  "ext": ".jpg",
  "mime": "image/jpeg",
  "size": 31.58,
  "width": 374,
  "height": 374,
  "url": "/uploads/hendy_siete_square_3a3520ac33.jpg",
  "formats": {
    "thumbnail": {
      "name": "thumbnail_hendy-siete-square.jpg",
      "hash": "thumbnail_hendy_siete_square_3a3520ac33",
      "ext": ".jpg",
      "mime": "image/jpeg",
      "width": 156,
      "height": 156,
      "size": 6.2,
      "path": null,
      "url": "/uploads/thumbnail_hendy_siete_square_3a3520ac33.jpg"
  "provider": "local",
  "related": [{
    "_id": {
      "$oid": "5f26352e855b5c1676e680a3"
    "ref": {
      "$oid": "5f2634bb855b5c1676e680a1"
    "kind": "UsersPermissionsUser",
    "field": "profilePhoto"
  "createdAt": {
    "$date": "2020-08-02T03:38:15.466Z"
  "updatedAt": {
    "$date": "2020-08-02T03:38:22.867Z"
  "__v": 0,
  "created_by": {
    "$oid": "5f26335df01fe80f3fcc8fe1"
  "updated_by": {
    "$oid": "5f26335df01fe80f3fcc8fe1"

Using default filesystem, the files will be stored inside public/uploads subfolder.

How can we help?

Leave a Reply

Your email address will not be published. Required fields are marked *