Website Development on Strapi CMS

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Website Development with Strapi CMS

Strapi is a Node.js headless CMS with automatic REST and GraphQL API generation from content-type configuration. Content types are described via JSON schema (in src/api/*/content-types/*.json), and schema changes made through the GUI are saved to code. It suits projects with partially technical teams: developers define the structure, editors populate content.

Architecture

Browser / Mobile App / Next.js
        ↕ REST API / GraphQL
    Strapi (Node.js)
        ↕
   PostgreSQL / MySQL / SQLite
        ↕
   Cloudinary / S3 (media)

Strapi runs as a separate process. There is no monolithic integration with Next.js — only HTTP requests.

Installation

npx create-strapi-app@latest my-project --quickstart
# Quickstart: SQLite, no customization

# Or with PostgreSQL
npx create-strapi-app@latest my-project \
  --dbclient=postgres \
  --dbhost=localhost \
  --dbport=5432 \
  --dbname=strapi_db \
  --dbusername=strapi \
  --dbpassword=pass

cd my-project
npm run develop  # dev mode with hot reload
# Admin: http://localhost:1337/admin

Content Types

Content types are created via Content-Type Builder in admin or manually via JSON:

// src/api/article/content-types/article/schema.json
{
  "kind": "collectionType",
  "collectionName": "articles",
  "info": {
    "singularName": "article",
    "pluralName": "articles",
    "displayName": "Article"
  },
  "attributes": {
    "title": { "type": "string", "required": true },
    "slug": { "type": "uid", "targetField": "title" },
    "content": { "type": "richtext" },
    "excerpt": { "type": "text", "maxLength": 500 },
    "publishedAt": { "type": "datetime" },
    "cover": { "type": "media", "multiple": false, "required": false, "allowedTypes": ["images"] },
    "category": { "type": "relation", "relation": "manyToOne", "target": "api::category.category" },
    "tags": { "type": "relation", "relation": "manyToMany", "target": "api::tag.tag" },
    "author": { "type": "relation", "relation": "manyToOne", "target": "plugin::users-permissions.user" }
  }
}

Typical Project Stack

Layer Technology
CMS Strapi 5.x
Frontend Next.js 14 / Nuxt 3
Database PostgreSQL
Media Cloudinary / AWS S3
CMS Deploy Railway / Render / VPS
Frontend Deploy Vercel / Netlify
Cache Redis (for production)

REST API Out of the Box

# Get list of articles (public if permissions configured)
GET http://localhost:1337/api/articles?populate=cover,category,author

# Filtering
GET /api/articles?filters[category][slug][$eq]=tech&sort=publishedAt:desc&pagination[pageSize]=10

# Specific entry
GET /api/articles/123?populate=deep

# Search
GET /api/articles?filters[title][$containsi]=javascript

Integration with Next.js

// lib/strapi.ts
const STRAPI_URL = process.env.STRAPI_URL || 'http://localhost:1337'
const API_TOKEN = process.env.STRAPI_API_TOKEN

export async function fetchStrapi<T>(
  endpoint: string,
  options?: RequestInit
): Promise<T> {
  const response = await fetch(`${STRAPI_URL}/api${endpoint}`, {
    headers: {
      Authorization: `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json',
    },
    next: { tags: [endpoint.split('/')[1]] },  // ISR tag
    ...options,
  })

  if (!response.ok) {
    throw new Error(`Strapi API error: ${response.status}`)
  }

  const data = await response.json()
  return data
}

// Usage
const { data: articles } = await fetchStrapi<{ data: Article[] }>(
  '/articles?populate=cover,category&sort=publishedAt:desc&pagination[pageSize]=10'
)

Webhooks for ISR

// app/api/revalidate/strapi/route.ts
import { revalidateTag } from 'next/cache'
import { NextRequest, NextResponse } from 'next/server'

export async function POST(req: NextRequest) {
  const body = await req.json()

  // Strapi sends: { event, uid, model, entry }
  const { model } = body

  // Invalidate cache by model
  revalidateTag(model)

  return NextResponse.json({ revalidated: true })
}

In Strapi admin: Settings → Webhooks → Add new webhook → URL of your Next.js endpoint.

Development Features

Strapi Response Format: all data is wrapped in { data: { id, attributes: {...} } }. In Strapi 5, this changed — data is returned flat.

Populate: by default, relationships are not filled. You must explicitly specify ?populate=* or ?populate[category][populate][0]=icon.

Drafts: Draft/Publish system is built-in. ?publicationState=live — published only, ?publicationState=preview + API token — drafts.

Timeline

A basic website with 4–6 content types, permissions setup, and Next.js integration takes 2–3 weeks. Complex projects with custom controllers, plugins, and multilingual support take 4–6 weeks.