Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Strapi Administrator & Developer Guide

Generated May 2026 from strapi/strapi at 4002cdf

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Architecture Overview

Strapi is an open-source, self-hosted headless CMS built on Node.js and TypeScript. It provides a visual admin panel (React SPA), auto-generated REST and GraphQL APIs, a plugin system, and support for multiple databases. The codebase is organized as a monorepo using Yarn workspaces and Nx for build orchestration.

System Architecture

                         ┌─────────────────────────┐
                         │     Admin Panel (SPA)    │
                         │   React + Design System  │
                         └────────────┬────────────┘
                                      │ HTTP
                         ┌────────────▼────────────┐
                         │     Strapi Server        │
                         │   Koa.js HTTP Server     │
                         ├──────────────────────────┤
                         │  Middleware Pipeline      │
                         │  ├── CORS                │
                         │  ├── Body Parser          │
                         │  ├── Session              │
                         │  ├── Security             │
                         │  └── Custom Middlewares   │
                         ├──────────────────────────┤
                         │  Core Services            │
                         │  ├── Document Service     │
                         │  ├── Entity Service       │
                         │  ├── Entity Validator     │
                         │  ├── Cron Service          │
                         │  ├── Webhook Runner       │
                         │  └── Session Manager      │
                         ├──────────────────────────┤
                         │  Plugin System             │
                         │  ├── Content Manager       │
                         │  ├── Content-Type Builder  │
                         │  ├── Upload (Media)        │
                         │  ├── Users & Permissions   │
                         │  ├── i18n                  │
                         │  ├── GraphQL               │
                         │  ├── Documentation          │
                         │  ├── Email                  │
                         │  ├── Review Workflows (EE) │
                         │  ├── Content Releases (EE) │
                         │  └── Sentry                │
                         ├──────────────────────────┤
                         │  Database Layer            │
                         │  Knex.js Query Builder     │
                         │  ├── SQLite                │
                         │  ├── PostgreSQL             │
                         │  └── MySQL                  │
                         └──────────────────────────┘

Component Inventory

Component Package Path Role
Core Runtime @strapi/core packages/core/core/ Container, Strapi class, service registry, lifecycle
Strapi CLI @strapi/strapi packages/core/strapi/ CLI commands (develop, build, start, report), Node.js build tooling
Admin Panel @strapi/admin packages/core/admin/ React SPA (client), Koa server-side routes (auth, roles, tokens, AI)
Database @strapi/database packages/core/database/ Knex-based ORM, schema management, migrations
Content Manager @strapi/content-manager packages/core/content-manager/ CRUD operations, history, preview, homepage
Content-Type Builder @strapi/content-type-builder packages/core/content-type-builder/ Content type schema creation and modification
Upload (Media Library) @strapi/upload packages/core/upload/ File upload, image optimization, AI metadata, provider system
Permissions @strapi/permissions packages/core/permissions/ Permission engine, condition evaluation
Data Transfer @strapi/data-transfer packages/core/data-transfer/ Import/export, remote push/pull via WebSocket
OpenAPI @strapi/openapi packages/core/openapi/ Auto-generated OpenAPI/Swagger specification
Types @strapi/types packages/core/types/ TypeScript type definitions for the entire platform
Utils @strapi/utils packages/core/utils/ Shared utilities (env helper, query param conversion, errors, validation)
Content Releases @strapi/content-releases packages/core/content-releases/ Batch publish/unpublish scheduling (EE)
Review Workflows @strapi/review-workflows packages/core/review-workflows/ Stage-based review workflows (EE)

Plugin Inventory

Plugin Package Description
Users & Permissions @strapi/plugin-users-permissions End-user auth (local + OAuth providers), roles, JWT
GraphQL @strapi/plugin-graphql Auto-generated GraphQL API with playground
i18n @strapi/plugin-i18n Internationalization, locale management, AI-powered translations
Documentation @strapi/plugin-documentation Auto-generated API documentation (OpenAPI/Swagger)
Email @strapi/email Email sending abstraction with multiple providers
Sentry @strapi/plugin-sentry Error tracking via Sentry integration
Color Picker @strapi/plugin-color-picker Custom field type for color values
Cloud @strapi/plugin-cloud Strapi Cloud integration in the admin panel

Provider Inventory

Category Provider Package
Upload Local filesystem @strapi/provider-upload-local
Upload AWS S3 @strapi/provider-upload-aws-s3
Upload Cloudinary @strapi/provider-upload-cloudinary
Email Sendmail @strapi/provider-email-sendmail
Email Sendgrid @strapi/provider-email-sendgrid
Email Amazon SES @strapi/provider-email-amazon-ses
Email Mailgun @strapi/provider-email-mailgun
Email Nodemailer @strapi/provider-email-nodemailer

Technology Stack

Layer Technology
Runtime Node.js >=20, <=24
Language TypeScript 5.4, JavaScript (ES2020+)
HTTP Server Koa.js
Admin Frontend React 18, @strapi/design-system
Frontend Bundler Vite or Webpack (configurable)
Database ORM Knex.js
Build Orchestration Nx 20.4, Yarn 4.12 workspaces
Testing Jest 29, Vitest, Playwright 1.56
Linting ESLint 8, Prettier 3
Package Publishing Rollup 4, SWC

Note

Strapi follows a dual-license model: core code under MIT, and Enterprise Edition features (code under ee/ directories) under the Strapi Enterprise License. EE features require either a license key (STRAPI_LICENSE env var) or a Strapi Cloud subscription.

Core Data Flow

HTTP Request
  → Koa Middleware Pipeline (CORS, body parsing, auth)
    → Router (Content API or Admin API)
      → Controller (validates input, calls service)
        → Service (business logic, calls Document/Entity Service)
          → Document Service (document-level CRUD, draft/publish)
            → Entity Service (entity-level CRUD)
              → Database Layer (Knex query builder)
                → Database (SQLite/PostgreSQL/MySQL)
  ← HTTP Response (JSON)

Content API routes are auto-generated from content type schemas and follow the pattern /api/{pluralApiId}. Admin API routes serve the admin panel and use /admin/ prefix with JWT-based authentication.


2. System Requirements

Requirement Minimum Recommended
Node.js 20.0.0 Latest LTS (20.x or 22.x)
Node.js max 24.x.x —
npm 6.0.0 Latest
RAM 2 GB 4 GB+
Disk 1 GB 10 GB+ (with media uploads)
Database SQLite 3 PostgreSQL >=11
OS Linux, macOS, Windows Ubuntu 22.04+ LTS

Database options:

Database Version Best For
SQLite 3.x Development, small single-instance deployments
PostgreSQL >=11 Production, multi-instance deployments
MySQL >=8.0 Production (alternative to PostgreSQL)

3. Configuration Reference

Configuration Files

File Format Location Purpose
config/server.js JS/TS Project root Server settings (host, port, cron, webhooks, logging)
config/database.js JS/TS Project root Database connection settings
config/admin.js JS/TS Project root Admin panel settings (auth, API tokens)
config/middlewares.js JS/TS Project root Middleware pipeline configuration
config/plugins.js JS/TS Project root Plugin configuration (email, upload, graphql, etc.)
config/api.js JS/TS Project root Content API settings (responses, REST params)
.env dotenv Project root Environment variables (secrets, host, port)
src/index.js JS/TS Project root App lifecycle hooks (register, bootstrap, destroy)
src/admin/app.tsx TS/JSX Project root Admin panel customization

Environment-specific overrides: Place config files under config/env/{environment}/ (e.g., config/env/production/database.js). Strapi loads the matching directory based on NODE_ENV.

Environment Variables — Complete Reference

Required Secrets

Variable Type Description Required
APP_KEYS Comma-separated strings Session signing keys (minimum 2) Yes
API_TOKEN_SALT String Salt for hashing API tokens Yes
ADMIN_JWT_SECRET String Secret for admin panel JWT tokens Yes
TRANSFER_TOKEN_SALT String Salt for data transfer tokens Yes
JWT_SECRET String Secret for Users & Permissions JWT Yes
ENCRYPTION_KEY String Key for field-level encryption Yes

Important

All six secrets must be unique, random strings in production. Failing to set them leaves the application vulnerable to token forgery and data exposure. Generate with openssl rand -base64 32.

Server Configuration

Variable Type Default Description
HOST String 0.0.0.0 Server bind address
PORT Integer 1337 Server port
NODE_ENV String development Runtime environment (development, production)
STRAPI_TELEMETRY_DISABLED Boolean false Disable anonymous telemetry
STRAPI_ADMIN_BACKEND_URL String — Custom backend URL for admin panel (for reverse proxies)
WEBHOOKS_POPULATE_RELATIONS Boolean true Include populated relations in webhook payloads

Database Configuration

Variable Type Default Description
DATABASE_CLIENT String sqlite Database client (sqlite, postgres, mysql)
DATABASE_HOST String — Database host
DATABASE_PORT Integer — Database port
DATABASE_NAME String — Database name
DATABASE_USERNAME String — Database username
DATABASE_PASSWORD String — Database password
DATABASE_SSL Boolean false Enable SSL for database connections
DATABASE_FILENAME String .tmp/data.db SQLite file path

Enterprise & AI

Variable Type Default Description
STRAPI_LICENSE String — Enterprise license key
STRAPI_DISABLE_EE Boolean — Force-disable Enterprise features
STRAPI_AI_URL String — AI service endpoint URL

Miscellaneous

Variable Type Default Description
STRAPI_HOSTING String — Hosting platform identifier (analytics)
STRAPI_ANALYTICS_URL String — Custom analytics endpoint
STRAPI_UUID_PREFIX String — UUID generation prefix
STRAPI_GRAPHQL_V4_COMPATIBILITY_MODE Boolean — Enable v4 GraphQL compatibility
USE_EXPERIMENTAL_DEPENDENCIES Boolean — Enable experimental dependency resolution
DOCKER Boolean — Set when running inside Docker

Warning

Setting STRAPI_DISABLE_EE=true disables all Enterprise Edition features regardless of license status. This is useful for CE-only deployments but will break workflows and features that depend on EE functionality.

Server Configuration — Full Schema

// config/server.js
module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),

  // Session/cookie signing keys (minimum 2)
  app: {
    keys: env.array('APP_KEYS'),
  },

  // Webhook settings
  webhooks: {
    populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', true),
  },

  // HTTP proxy for outbound requests
  globalProxy: env('http_proxy'),

  // Node.js http.Server options
  http: {
    serverOptions: {
      requestTimeout: 600000, // milliseconds
    },
  },

  // Data transfer settings
  transfer: {
    remote: {
      enabled: true,
    },
  },

  // Logging configuration
  logger: {
    config: {
      level: 'info', // silly, debug, info, warn, error
    },
    updates: {
      enabled: true,
    },
    startup: {
      enabled: true,
    },
  },

  // Cron job scheduling
  cron: {
    enabled: false,
    tasks: {
      // '*/5 * * * *': async ({ strapi }) => { ... }
    },
  },
});

Database Configuration — Full Schema

// config/database.js — PostgreSQL
module.exports = ({ env }) => ({
  connection: {
    client: 'postgres',
    connection: {
      host: env('DATABASE_HOST', '127.0.0.1'),
      port: env.int('DATABASE_PORT', 5432),
      database: env('DATABASE_NAME', 'strapi'),
      user: env('DATABASE_USERNAME', 'strapi'),
      password: env('DATABASE_PASSWORD', ''),
      ssl: env.bool('DATABASE_SSL', false) && {
        rejectUnauthorized: env.bool('DATABASE_SSL_REJECT_UNAUTHORIZED', true),
      },
    },
    pool: {
      min: env.int('DATABASE_POOL_MIN', 2),
      max: env.int('DATABASE_POOL_MAX', 10),
    },
  },
});
// config/database.js — MySQL
module.exports = ({ env }) => ({
  connection: {
    client: 'mysql',
    connection: {
      host: env('DATABASE_HOST', '127.0.0.1'),
      port: env.int('DATABASE_PORT', 3306),
      database: env('DATABASE_NAME', 'strapi'),
      user: env('DATABASE_USERNAME', 'strapi'),
      password: env('DATABASE_PASSWORD', ''),
      ssl: env.bool('DATABASE_SSL', false),
    },
  },
});

Caution

The DATABASE_CLIENT value must be one of sqlite, postgres, or mysql. Any other value produces Error: Unsupported database client. The MySQL driver requires mysql2 as a peer dependency.

Plugin Configuration Examples

// config/plugins.js
module.exports = ({ env }) => ({
  // GraphQL plugin
  graphql: {
    config: {
      endpoint: '/graphql',
      shadowCRUD: true,
      playgroundAlways: false,
      depthLimit: 10,
      amountLimit: 100,
    },
  },

  // Email plugin
  email: {
    config: {
      provider: 'sendgrid',
      providerOptions: {
        apiKey: env('SENDGRID_API_KEY'),
      },
      settings: {
        defaultFrom: '[email removed]',
        defaultReplyTo: '[email removed]',
      },
    },
  },

  // Upload plugin
  upload: {
    config: {
      provider: 'aws-s3',
      providerOptions: {
        accessKeyId: env('AWS_ACCESS_KEY_ID'),
        secretAccessKey: env('AWS_SECRET_ACCESS_KEY'),
        region: env('AWS_REGION'),
        params: {
          Bucket: env('AWS_BUCKET'),
        },
      },
    },
  },

  // i18n plugin
  i18n: {
    enabled: true,
  },

  // Sentry plugin
  sentry: {
    enabled: true,
    config: {
      dsn: env('SENTRY_DSN'),
    },
  },

  // Documentation plugin
  documentation: {
    enabled: true,
  },
});

4. Deployment & Scaling

Production Checklist

  • [x] Set NODE_ENV=production
  • [x] Generate and set all 6 required secrets
  • [x] Configure a production database (PostgreSQL recommended)
  • [x] Build the admin panel: NODE_ENV=production strapi build
  • [x] Configure upload provider (S3/Cloudinary for multi-instance)
  • [x] Set up reverse proxy (Nginx/Traefik) with SSL
  • [ ] Configure monitoring and log aggregation
  • [ ] Set up database backups
  • [ ] Configure rate limiting
  • [ ] Review CORS and security middleware settings

Production Start

NODE_ENV=production npm run start

Important

Always run strapi build before strapi start in production. The start command does not auto-reload or rebuild the admin panel. Changes to content types require a rebuild and restart.

Docker Deployment

Create a Dockerfile:

FROM node:20-alpine

WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production

COPY . .
RUN NODE_ENV=production yarn build

EXPOSE 1337
CMD ["yarn", "start"]

Docker Compose with PostgreSQL:

version: '3'
services:
  strapi:
    build: .
    ports:
      - '1337:1337'
    environment:
      HOST: 0.0.0.0
      PORT: 1337
      DATABASE_CLIENT: postgres
      DATABASE_HOST: db
      DATABASE_PORT: 5432
      DATABASE_NAME: strapi
      DATABASE_USERNAME: strapi
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      APP_KEYS: ${APP_KEYS}
      API_TOKEN_SALT: ${API_TOKEN_SALT}
      ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
      TRANSFER_TOKEN_SALT: ${TRANSFER_TOKEN_SALT}
      JWT_SECRET: ${JWT_SECRET}
      ENCRYPTION_KEY: ${ENCRYPTION_KEY}
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: strapi
      POSTGRES_USER: strapi
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Reverse Proxy (Nginx)

server {
    listen 80;
    server_name api.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name api.example.com;

    ssl_certificate /etc/ssl/certs/cert.pem;
    ssl_certificate_key /etc/ssl/private/key.pem;

    client_max_body_size 256M;

    location / {
        proxy_pass http://127.0.0.1:1337;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Tip

When using a reverse proxy, set STRAPI_ADMIN_BACKEND_URL to your public URL so the admin panel correctly resolves API endpoints. For WebSocket-based features (data transfer), ensure the proxy supports WebSocket upgrade.

Strapi Cloud

Deploy directly to Strapi's managed hosting:

strapi deploy

The Cloud CLI handles authentication, project creation, environment configuration, and deployment. Projects link to your Strapi Cloud account via browser-based OAuth.


5. Operations

Lifecycle Hooks

The src/index.js (or src/index.ts) file exposes three lifecycle hooks:

module.exports = {
  // Runs before the app is initialized — extend registries
  register({ strapi }) {
    // Register custom content API parameters, custom fields, etc.
  },

  // Runs before the app starts — seed data, set up integrations
  async bootstrap({ strapi }) {
    // Seed initial data, configure services
  },

  // Runs before the app shuts down — clean up
  destroy({ strapi }) {
    // Close connections, flush buffers
  },
};

Note

The register hook runs synchronously before any plugin is loaded. Use it for extending the Strapi registry (custom fields, query params). The bootstrap hook runs after all plugins are loaded and can be async.

Cron Jobs

Configure scheduled tasks in config/server.js:

module.exports = ({ env }) => ({
  cron: {
    enabled: true,
    tasks: {
      // Standard cron expression
      '0 */6 * * *': async ({ strapi }) => {
        // Runs every 6 hours
        const entries = await strapi.documents('api::article.article').findMany({
          filters: { status: 'draft' },
        });
        strapi.log.info(`Found ${entries.length} draft articles`);
      },
    },
  },
});

Cron jobs can also be defined in separate files and imported. Removing a cron job by name requires calling it with a non-empty string name — Error: You must provide a name to remove a cron job.

Webhooks

Strapi supports outbound webhooks that fire on content lifecycle events:

  • Manage via admin panel: Settings > Webhooks
  • Events: create, update, delete, publish, unpublish
  • Payload includes the full entry with optionally populated relations

The webhook runner (WebhookRunner class) dispatches events asynchronously.

Tip

Set webhooks.populateRelations: false in config/server.js (or WEBHOOKS_POPULATE_RELATIONS=false) to reduce webhook payload size in high-volume environments.

Data Transfer

Strapi's data transfer system supports three modes:

  1. Export — export data to a local file
  2. Import — import data from a file
  3. Remote Transfer — push/pull data between Strapi instances via WebSocket

Remote transfer architecture:

Source Strapi Instance                      Target Strapi Instance
┌─────────────┐                            ┌─────────────┐
│ Push Handler │──── WebSocket (wss) ────→  │ Pull Handler │
│             │                            │             │
│ Stages:     │                            │ Stages:     │
│ - schemas   │                            │ - schemas   │
│ - entities  │                            │ - entities  │
│ - assets    │                            │ - assets    │
│ - links     │                            │ - links     │
│ - config    │                            │ - config    │
└─────────────┘                            └─────────────┘

Transfer proceeds in stages: schemas → entities → assets → links → configuration. Each stage uses streaming with checksums for asset integrity.

Caution

Remote transfer must be explicitly enabled in config/server.js (transfer.remote.enabled: true). The transfer token salt (TRANSFER_TOKEN_SALT) secures the connection. Unauthorized access attempts return ProviderTransferError: Bad transfer ID provided.

Monitoring & Health

Monitor Strapi with logging and service inspection:

Log levels: silly, debug, info, warn, error (configured in config/server.js under logger.config.level).

Service listing: The CLI commands strapi services:list and strapi controllers:list enumerate registered services and controllers.

Startup logging can be controlled via logger.startup.enabled and logger.updates.enabled.

Admin User Management

API Token management:

Token Type Capabilities
Read-only Read access to Content API
Full access Full CRUD access to Content API
Custom Per-content-type permissions

Token lifecycle: tokens can have unlimited or time-limited lifespans. The lifespan must be a positive number or null (unlimited).

Admin role management:

  • The Super Admin role cannot be deleted: ApplicationError: You cannot delete the super admin role
  • At least one Super Admin user must exist: ValidationError: You must have at least one user with super admin role
  • Roles with assigned users cannot be deleted: ApplicationError: Some roles are still assigned to some users
  • Admin users cannot delete themselves: ApplicationError: You cannot delete your own user

Warning

Enterprise Edition enforces license seat limits. Attempting to create users beyond the limit returns ForbiddenError: License seat limit reached. You cannot create a new user. Similarly, re-activating blocked users may fail with ForbiddenError: License seat limit reached. You cannot active this user.


6. Performance

Build Optimization

The admin panel build supports two bundlers:

Bundler Flag Characteristics
Vite --bundler vite Faster builds, ES module output
Webpack --bundler webpack Legacy support, broader plugin compatibility

Build flags:

# Production build with minification and no sourcemaps
strapi build --minify --silent

# Development build with sourcemaps and stats
strapi build --sourcemap --stats

Database Performance

  • Connection pooling: Configure pool.min and pool.max in config/database.js to match your concurrency needs
  • SQLite limitations: SQLite uses file locking and is not suitable for multi-instance or high-concurrency production deployments
  • PostgreSQL recommended: For production, use PostgreSQL with connection pooling (e.g., PgBouncer) for best performance
  • Query optimization: Use fields and populate parameters to limit data retrieval to only what's needed

File Polling

For network-mounted directories (NFS, Docker volumes on some platforms), enable file polling:

strapi develop --polling

This uses filesystem polling instead of native file watchers, which is slower but more reliable on network filesystems.


7. Security

Authentication Architecture

Strapi has two separate authentication systems:

  1. Admin Authentication — JWT-based, managed by @strapi/admin

    • Login via /admin/login
    • Registration via one-time registration tokens
    • Passwords hashed with bcrypt
    • API tokens for programmatic access (3 types: read-only, full-access, custom)
    • Enterprise: SSO support via configurable providers
  2. Users & Permissions Authentication — JWT-based, managed by @strapi/plugin-users-permissions

    • Local auth (email/password) and OAuth providers
    • Email confirmation flow
    • Password reset with time-limited codes
    • Rate limiting configurable per-provider

Warning

Admin registration tokens are single-use. Using an invalid token returns ValidationError: Invalid registrationToken. New super admin accounts cannot be created through the registration flow: ApplicationError: You cannot register a new super admin.

API Token Security

API tokens are hashed before storage using the API_TOKEN_SALT. The raw token is shown only once at creation time.

Token validation errors:

Error Cause
Non-custom tokens should not reference permissions Attempting to set permissions on a read-only or full-access token
Missing permissions attribute for custom token Custom token created without specifying permissions
Unknown permissions provided Referencing permissions that don't exist
lifespan must be a positive number or null Invalid token lifetime value

Encryption

Strapi includes a versioned encryption service for field-level encryption:

  • Uses the ENCRYPTION_KEY environment variable
  • Versioned format for future algorithm upgrades
  • Errors: Unsupported encryption version (future-proofing), Invalid encrypted value format (corruption/tampering)

Content API Security

  • CORS: Configured via config/middlewares.js (strapi::cors middleware)
  • Request validation: Content API parameters are validated with Zod schemas when api.rest.strictParams is enabled
  • File upload security:
    • URL validation: only http/https protocols allowed (ApplicationError: Invalid URL protocol)
    • SSRF protection: resolved addresses checked against blocklist (ApplicationError: URL resolves to a blocked address)
    • DNS resolution validation: hostnames must resolve (ApplicationError: Could not resolve hostname)
    • Request timeouts: upload URL fetches have configurable timeouts (ApplicationError: Request timed out while fetching URL)
    • File name validation: invalid characters rejected (ApplicationError: File name contains invalid characters)

Caution

The upload provider validation prevents SSRF attacks by blocking requests to private IP ranges and unresolvable hostnames. Do not disable these checks in production. The AWS S3 provider logs detailed errors prefixed with Upload AWS S3 provider: for debugging upload failures.

SSO (Enterprise)

Enterprise Edition supports Single Sign-On via configurable providers:

  • Provider validation: ValidationError: Invalid provider supplied
  • SSO lock: prevents local login when SSO is enforced (Missing user object check)

8. Developer Internals

Repository Structure

strapi/strapi (monorepo)
├── packages/
│   ├── core/
│   │   ├── core/           # Strapi class, Container, service registry
│   │   ├── strapi/          # CLI, Node.js build tooling
│   │   ├── admin/           # Admin panel (React SPA + Koa server)
│   │   ├── database/        # Knex-based database layer
│   │   ├── content-manager/ # CRUD, history, preview, homepage
│   │   ├── content-type-builder/ # Schema builder
│   │   ├── content-releases/ # Batch publish scheduling (EE)
│   │   ├── review-workflows/ # Review stages (EE)
│   │   ├── upload/          # Media library + AI metadata
│   │   ├── email/           # Email abstraction
│   │   ├── data-transfer/   # Import/export/remote transfer
│   │   ├── openapi/         # OpenAPI spec generation
│   │   ├── permissions/     # Permission engine
│   │   ├── types/           # TypeScript type definitions
│   │   └── utils/           # Shared utilities
│   ├── cli/
│   │   ├── create-strapi/   # `create-strapi` package (thin wrapper)
│   │   ├── create-strapi-app/ # Project scaffolding CLI
│   │   └── cloud/           # Strapi Cloud CLI
│   ├── plugins/
│   │   ├── users-permissions/ # End-user auth
│   │   ├── graphql/         # GraphQL API
│   │   ├── i18n/            # Internationalization
│   │   ├── documentation/   # API documentation
│   │   ├── sentry/          # Error tracking
│   │   ├── color-picker/    # Color field type
│   │   └── cloud/           # Cloud admin plugin
│   ├── providers/
│   │   ├── email-*/         # Email providers (5 providers)
│   │   └── upload-*/        # Upload providers (3 providers)
│   ├── generators/          # Plop-based code generators
│   └── utils/
│       ├── logger/          # Logging utilities
│       ├── typescript/      # TS codegen helpers
│       └── upgrade/         # Upgrade CLI + codemods
├── examples/
│   ├── getstarted/          # Basic starter project
│   ├── complex/             # Complex schemas example
│   ├── kitchensink/         # All field types example
│   ├── experimental-dev/    # Experimental features
│   ├── empty/               # Minimal skeleton
│   └── plugins/             # Example plugins (todo, workspace)
├── tests/
│   ├── api/                 # API integration tests
│   ├── e2e/                 # End-to-end tests (Playwright)
│   ├── app-template/        # Test app template
│   └── utils/               # Test utilities
├── scripts/                 # Build, release, and utility scripts
├── docs/                    # Contributor documentation (Docusaurus)
└── templates/
    └── website/             # Website starter template

Core Architecture Deep Dive

The Strapi Class

The main Strapi class (packages/core/core/src/Strapi.ts) extends Container and is the central service registry. It provides:

  • Service registration and resolution via the Container pattern
  • Lifecycle management (register → bootstrap → start → destroy)
  • Plugin loading and initialization
  • Content type registration
  • Provider factory pattern for extensible subsystems

The Container class (packages/core/core/src/container.ts) implements a simple IoC container:

class Container {
  register(name, service) { /* throws if already registered */ }
  resolve(name) { /* throws if not found */ }
}

Service Layer

Key core services in packages/core/core/src/services/:

Service File Purpose
Document Service document-service/ Document-level CRUD with draft/publish, locale support
Entity Validator entity-validator/ Schema validation for content entries
Cron cron.ts Scheduled task execution
Webhook Store webhook-store.ts Webhook registration and event management
Webhook Runner webhook-runner.ts Async webhook dispatch
Session Manager session-manager.ts Database session/transaction management

The SessionManager uses three layers:

  1. DatabaseSessionProvider — wraps Knex transactions
  2. OriginSessionManager — tracks session origin for audit
  3. SessionManager — top-level session coordination

Database Layer

The Database class (packages/core/database/src/index.ts) wraps Knex.js:

  • Schema management with schema/ module
  • Connection factory supporting SQLite, PostgreSQL, MySQL
  • Model registration: Error: Model ${uid} not found on invalid UIDs
  • Type mapping: Error: Unknown type ${attribute.type} for unsupported attribute types

Content-Type Builder

The server-side Content-Type Builder (packages/core/content-type-builder/server/src/) handles:

  • Schema creation and modification via admin API
  • API handler with rollback support: Error: Cannot rollback api that was not backed up
  • Attribute validation: ApplicationError: component.notFound for invalid component references
  • Content type scaffolding (controllers, routes, services directories)

OpenAPI Generation

The OpenAPI module (packages/core/openapi/) auto-generates Swagger specifications using:

  • RouteCollector — gathers all registered routes
  • RouteMatcher — matches routes to content types
  • Context factories: DocumentContextFactory, OperationContextFactory, PathItemContextFactory, PathContextFactory
  • Route providers: AdminRoutesProvider, ApiRoutesProvider, PluginRoutesProvider

Data Transfer System

The data transfer module (packages/core/data-transfer/src/) implements:

Push handler (strapi/remote/handlers/push.ts):

  • Stage-based flow: init → schemas → entities → assets → links → config
  • Streaming with checksum validation: ProviderTransferError: Missing checksum for asset
  • Transfer state machine with atomic transitions
  • WebSocket protocol for real-time transfer

Pull handler (strapi/remote/handlers/pull.ts):

  • Mirror of push handler for data retrieval
  • Stream creation for each transfer stage
  • Asset streaming with read stream management

Error hierarchy:

DataTransferError (base)
├── ProviderError
│   ├── ProviderInitializationError
│   ├── ProviderValidationError
│   └── ProviderTransferError

Error Classes

All Strapi errors extend from base classes in packages/core/utils/src/errors.ts:

Error Class HTTP Status Usage
ApplicationError 400 General application errors
ValidationError 400 Input validation failures
NotFoundError 404 Resource not found
ForbiddenError 403 Insufficient permissions
UnauthorizedError 401 Authentication required
RateLimitError 429 Rate limit exceeded
PayloadTooLargeError 413 Request body too large
PolicyError 403 Policy check failures
NotImplementedError 501 Feature not implemented
PaginationError 400 Invalid pagination parameters
YupValidationError 400 Yup schema validation failures

Query Parameter Conversion

The packages/core/utils/src/convert-query-params.ts module handles REST API query parameter parsing with strict validation:

Error Cause
Invalid sort query Malformed sort parameter
Field cannot be empty Empty field in sort/filter
convertStartQueryParams expected a positive integer Negative or non-integer start
convertLimitQueryParams expected a positive integer Negative or non-integer limit
Using fragments is not permitted to populate Fragment syntax in populate
Invalid nested populate Malformed nested populate object
Invalid fields parameter Non-string/non-array fields
The filters parameter must be an object or an array Invalid filter format

Plugin Architecture

Each plugin follows a standard structure:

plugin-name/
├── admin/
│   └── src/
│       └── index.ts     # Admin panel registration
├── server/
│   └── src/
│       ├── index.ts     # Server-side entry (register, bootstrap, destroy)
│       ├── config/      # Plugin default configuration
│       ├── controllers/ # Route handlers
│       ├── routes/      # Route definitions
│       ├── services/    # Business logic
│       ├── models/       # Data models
│       └── utils/        # Shared utilities
└── package.json         # With strapi.kind: "plugin" metadata

Plugin metadata in package.json:

{
  "strapi": {
    "name": "plugin-name",
    "displayName": "Plugin Display Name",
    "description": "Plugin description",
    "kind": "plugin"
  }
}

Plugin exports use the standard exports map:

{
  "exports": {
    "./strapi-admin": { "source": "./admin/src/index.ts" },
    "./strapi-server": { "source": "./server/src/index.ts" },
    "./package.json": "./package.json"
  }
}

Admin Panel Architecture

The admin panel (packages/core/admin/admin/src/) is a React SPA using:

  • StrapiApp class — main application bootstrap, plugin registration, routing
  • React Router v6 — client-side routing
  • Redux Toolkit — global state management with adminApi (RTK Query)
  • @strapi/design-system — component library
  • React DnD — drag-and-drop for content ordering
  • react-query — server state management

Key features:

  • RBAC with condition-based permissions (rulesEngine.ts)
  • Guided tour system
  • Injection zones for plugin UI integration
  • Custom field registration
  • Theme support (light/dark)

Note

The admin panel mounts at /admin by default. If the root element is not found, it throws Error: [@strapi/admin]: Could not find the root element to mount the admin app. Ensure your hosting serves the admin HTML shell.

Code Generators

Strapi uses Plop.js (packages/generators/generators/) for scaffolding:

  • Content type generators
  • Plugin generators
  • API generators

Generator utilities validate config: Error: Invalid config: singularName and type are required.

TypeScript Support

The packages/utils/typescript/ module generates TypeScript definitions:

  • Model type generation from content type schemas
  • Attribute type mapping
  • Common model interfaces

The type generator handles: Error: typeof min/max values mismatch (type validation) and Error: Cannot convert to object literal. Unknown type (unsupported attribute types).

Testing Strategy

Test Type Tool Command Scope
Unit tests Jest 29, Vitest yarn test:unit Individual modules
Frontend tests Jest + Testing Library yarn test:front React components
API integration Jest + Supertest yarn test:api REST API endpoints
E2E tests Playwright 1.56 yarn test:e2e Full browser workflows
CLI tests Custom runner yarn test:cli CLI commands
TypeScript tsc --noEmit yarn test:ts Type checking

Edition-specific testing:

# Test Community Edition only
yarn test:e2e:ce

# Test Enterprise Edition
yarn test:e2e:ee

Tip

The test app template (tests/app-template/) provides a pre-configured Strapi project with complex content type schemas (articles, authors, categories, shops, teams, countries, conditions, etc.) for comprehensive integration testing.

Contributing

Setup the monorepo:

git clone https://github.com/strapi/strapi.git
cd strapi
yarn setup  # install, clean, build

Development workflow:

# Watch all packages for changes
yarn watch

# Run a specific example
cd examples/getstarted
yarn develop

# Run tests
yarn test:unit
yarn test:front

Commit conventions: Strapi uses Commitlint with conventional commits (@commitlint/config-conventional). Use yarn commit for interactive commit message creation.

Code style:

  • ESLint with Airbnb + TypeScript configs
  • Prettier for formatting
  • Lint-staged with Husky pre-commit hooks
  • File naming conventions enforced via eslint-plugin-check-file

9. Integration Points

Upload Providers

Custom upload providers must implement the provider interface:

module.exports = {
  init(providerOptions) {
    return {
      async upload(file) { /* upload file, set file.url */ },
      async uploadStream(file) { /* stream upload variant */ },
      async delete(file) { /* delete file */ },
      checkFileSize(file, sizeLimit) { /* validate file size */ },
      // Optional:
      async isPrivate() { /* return boolean */ },
      async getSignedUrl(file) { /* return signed URL for private files */ },
    };
  },
};

The upload system validates that providers expose the required methods: Error: The upload provider must implement the following methods: upload, uploadStream, delete.

Email Providers

Custom email providers implement:

module.exports = {
  init(providerOptions, settings) {
    return {
      async send(options) {
        // options: { to, from, subject, text, html }
      },
      // Optional:
      async verifyConnection() {
        // Throws if connection fails
      },
    };
  },
};

Connection verification: ApplicationError: This email provider does not support connection verification if verifyConnection is not implemented.

GraphQL Integration

The GraphQL plugin (@strapi/plugin-graphql) auto-generates:

  • Queries for all content types (find, findOne)
  • Mutations (create, update, delete)
  • Type definitions from content type schemas
  • Relay-style pagination
  • Filter input types

Configuration:

// config/plugins.js
module.exports = {
  graphql: {
    config: {
      endpoint: '/graphql',
      shadowCRUD: true,
      playgroundAlways: false,
      depthLimit: 10,
      amountLimit: 100,
    },
  },
};

Content API Custom Parameters

Register custom query and body parameters for the Content API:

// src/index.js
module.exports = {
  register({ strapi }) {
    // Custom query parameter with Zod validation
    strapi.contentAPI.addQueryParams({
      customParam: {
        schema: (z) => z.string().max(200).optional(),
        matchRoute: (route) => route.path.includes('articles'),
      },
    });

    // Custom body parameter
    strapi.contentAPI.addInputParams({
      clientMutationId: {
        schema: (z) => z.string().max(100).optional(),
      },
    });
  },
};

Webhook Events

Supported webhook events:

Event Trigger
entry.create New content entry created
entry.update Content entry updated
entry.delete Content entry deleted
entry.publish Content entry published
entry.unpublish Content entry unpublished

Custom events can be registered. Invalid events produce: ValidationError: Webhook event ${event} is not supported.

Strapi Cloud CLI

The Cloud CLI (@strapi/cloud-cli) provides:

Command Description
strapi deploy Deploy project to Strapi Cloud
strapi cloud login Authenticate with Strapi Cloud
strapi cloud logout Clear authentication

The CLI uses browser-based OAuth for authentication, stores tokens locally via xdg-app-paths, and communicates with the Strapi Cloud API for project management, environment configuration, and deployment.

Cloud API errors:

Error Cause
Error fetching cloud CLI config from the server Network/auth issue with cloud API
Error fetching cloud projects from the server Cannot list projects
Error fetching cloud environments from the server Cannot list environments
Error creating trial Trial creation failed
No configuration data found to patch Missing local project config