Strapi Headless CMS Guide — Build APIs and Content Management
Strapi is an open-source headless CMS that lets you build content APIs visually — create content types through the admin panel, get REST and GraphQL endpoints automatically, and manage users, roles, and plugins without writing backend code.
What You’ll Learn
- Installing and setting up Strapi for development
- Building content types with the Content-Type Builder
- Using the auto-generated REST and GraphQL APIs
- Configuring roles, permissions, and public access
- Extending Strapi with plugins
- Deploying Strapi to production
Why Strapi Matters
Traditional CMS platforms like WordPress tightly couple content management with frontend presentation. Strapi separates them — you manage content in the admin panel, and your frontend (React, Vue, Astro, mobile app) fetches it via API. This means one Strapi backend can serve a website, a mobile app, and a smart TV interface from the same content repository. DodaZIP uses Strapi to manage its knowledge base articles — the same content serves the web help center and the in-app help panel through different frontends.
flowchart LR
A[Node.js & API Basics] --> B[Strapi]
B --> C[Content Types]
B --> D[REST / GraphQL API]
B --> E[Roles & Permissions]
B --> F[Plugins]
C --> G[Admin Panel UI]
D --> H[Automatic Endpoints]
E --> I[Access Control]
style B fill:#4945ff,color:#fff
Core Concepts
Strapi Setup
# Create a new Strapi project
npx create-strapi-app@latest my-project --quickstart
# Output:
# ✔ Creating project
# ✔ Installing dependencies
# ✔ Starting server
# Strapi is running at http://localhost:1337/admin
# Or use the manual setup for more control
npx create-strapi-app@latest my-project --typescriptOutput: The --quickstart flag uses SQLite and starts immediately. Visit http://localhost:1337/admin to create an admin user and enter the content management panel.
Content-Type Builder
Strapi’s Content-Type Builder is a visual tool for defining data models:
// Behind the scenes, Strapi generates a schema like this for a "Article" type
// src/api/article/content-types/article/schema.json
{
"kind": "collectionType",
"collectionName": "articles",
"info": {
"singularName": "article",
"pluralName": "articles",
"displayName": "Article"
},
"options": {
"draftAndPublish": true
},
"attributes": {
"title": {
"type": "string",
"required": true,
"maxLength": 200
},
"content": {
"type": "richtext"
},
"publishedAt": {
"type": "datetime"
},
"author": {
"type": "relation",
"relation": "manyToOne",
"target": "api::author.author",
"inversedBy": "articles"
},
"tags": {
"type": "json"
}
}
}Output: This schema automatically creates a database table and API endpoints. No SQL or route definitions needed.
REST and GraphQL APIs
Strapi generates CRUD endpoints for every content type:
// REST API — auto-generated
// GET /api/articles — List articles
// GET /api/articles/:id — Get single article
// POST /api/articles — Create article
// PUT /api/articles/:id — Update article
// DELETE /api/articles/:id — Delete article
// Fetch articles with filtering and relations
const response = await fetch(
"http://localhost:1337/api/articles?" +
"populate=author,tags" +
"&filters[title][$containsi]=strapi" +
"&sort[0]=publishedAt:desc" +
"&pagination[pageSize]=10"
);
const data = await response.json();
console.log(data.data);
// [{ id: 1, title: "Getting Started with Strapi", author: {...}, ... }]
// GraphQL — after installing the GraphQL plugin
const query = `
query {
articles(filters: { title: { contains: "strapi" } }) {
title
content
author { name }
tags
}
}
`;
const graphqlResponse = await fetch("http://localhost:1337/graphql", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query }),
});Output: The REST API returns JSON with data (array of items), meta (pagination), and included (relations). The GraphQL endpoint provides a full GraphQL schema with queries and mutations generated from your content types.
Roles and Permissions
// Define roles in Strapi Admin:
// Settings > Roles > Create Role
// "Authenticated" — logged-in users
// "Public" — unauthenticated visitors
// Set permissions per content type and action
// Example: Public can "find" and "findOne" articles
// Authenticated can "create", "update", "delete" their own articles
// In your frontend, include the JWT for authenticated requests
const loginResponse = await fetch("http://localhost:1337/api/auth/local", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
identifier: "alice@example.com",
password: "secure-password-123",
}),
});
const { jwt, user } = await loginResponse.json();
const createResponse = await fetch("http://localhost:1337/api/articles", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwt}`,
},
body: JSON.stringify({ data: { title: "New Article" } }),
});Output: The JWT-based authentication works like any secure API. Unauthenticated requests see only public content. Authenticated requests can create, update, or delete based on their role permissions.
Plugins
# Install the GraphQL plugin
npm run strapi install graphql
# Install the SEO plugin for better metadata management
npm run strapi install seo
# Upload provider for cloud storage (S3, Cloudinary, etc.)
npm install @strapi/provider-upload-aws-s3
# Configure in config/plugins.js
module.exports = {
upload: {
config: {
provider: "aws-s3",
providerOptions: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: "us-east-1",
params: { Bucket: "my-strapi-uploads" },
},
},
},
};Deployment
# Build the admin panel for production
NODE_ENV=production npm run build
# Start in production mode
NODE_ENV=production npm run start
# Deploy to a VPS, Railway, Heroku, or DigitalOcean App Platform
# Use PostgreSQL in production (not SQLite)
# Set environment variables for database, JWT secret, and upload providerCommon Mistakes
Using SQLite in production: SQLite works for development but fails under concurrent writes. Use PostgreSQL or MySQL for production deployments.
Exposing the admin panel to the public: By default,
/adminis accessible to anyone. Restrict it behind a VPN, IP whitelist, or SSO for production.Not configuring CORS for your frontend domain: Strapi blocks requests from unknown origins by default. Set
cors.origininconfig/middlewares.jsto your frontend URL.Over-fetching with
populate=*: The wildcard populate loads every relation, causing massive response sizes. Specify only the relations you need:populate=author,tags.Ignoring Strapi’s draft/publish workflow: Every content type has draft and publish states by default. Use them — don’t publish unfinished content directly.
Practice Questions
What makes Strapi a “headless” CMS? Answer: Strapi only provides content management and APIs — it has no frontend rendering layer. Your frontend fetches content via REST or GraphQL.
How do you add a custom field type in Strapi? Answer: Use the Content-Type Builder in the admin panel to add fields. For custom field types not in the builder, create a custom plugin.
What is the purpose of the JWT in Strapi? Answer: The JWT (JSON Web Token) authenticates API requests. It proves the user’s identity and determines what content they can access based on their role.
How does Strapi handle media uploads? Answer: By default, media uploads to the local filesystem. Use provider plugins for S3, Cloudinary, or other cloud storage.
Challenge
Build a headless blog: create content types for Articles (title, content, cover image, author) and Authors (name, bio, avatar), configure permissions (public can read, authenticated can write), install the GraphQL plugin, build a React frontend that fetches articles via GraphQL, and deploy both Strapi and the frontend.
FAQ
Try It Yourself
npx create-strapi-app@latest strapi-demo --quickstart
# 1. Create admin user at http://localhost:1337/admin
# 2. Use Content-Type Builder to create a "Recipe" type
# Fields: title (string), ingredients (json), instructions (richtext)
# 3. Add a few recipes in the Content Manager
# 4. Fetch via API:
curl http://localhost:1337/api/recipes
# 5. Install GraphQL plugin:
npm run strapi install graphql
# Query at http://localhost:1337/graphqlWhat’s Next
| Topic | Description |
|---|---|
| Compare Strapi with traditional CMS | |
| Manipulate Strapi API data in JavaScript |
Related topics: JavaScript, Node.js, REST API, GraphQL, React
What’s Next
Congratulations on completing this Strapi tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro