Website Development on Directus CMS
Directus is a headless CMS on top of an existing relational database. Key difference: Directus does not create an abstract data schema — it works directly with PostgreSQL/MySQL tables you define. If there is a products table in the DB, Directus automatically provides REST and GraphQL API, admin panel for CRUD.
Architecture
Next.js / Nuxt / Mobile App
↕ REST API / GraphQL
Directus (Node.js)
↕
PostgreSQL / MySQL / SQLite / MS SQL
Directus is a layer on top of the database. You can connect it to an existing database without changing the schema.
Installation
npx create-directus-project@latest my-project
# Choose: SQLite (for development) or PostgreSQL
cd my-project
npx directus start
# Admin: http://localhost:8055/admin
Or via Docker:
# docker-compose.yml
version: '3'
services:
directus:
image: directus/directus:latest
ports:
- "8055:8055"
environment:
SECRET: your-random-secret-key
DB_CLIENT: pg
DB_HOST: postgres
DB_PORT: 5432
DB_DATABASE: directus
DB_USER: directus
DB_PASSWORD: directus
ADMIN_EMAIL: [email protected]
ADMIN_PASSWORD: admin-password
depends_on:
- postgres
postgres:
image: postgres:15
environment:
POSTGRES_DB: directus
POSTGRES_USER: directus
POSTGRES_PASSWORD: directus
volumes:
- postgres_data:/var/lib/postgresql/data
Collections (Tables)
In Directus admin via Settings → Data Model → Create Collection:
Collections (DB tables):
- articles (id, title, slug, content, status, author, published_at)
- categories (id, name, slug, description, parent_id)
- media (built-in)
- users (built-in via Users system collection)
Fields correspond to table columns. Directus supports all standard PostgreSQL types + special ones: uuid, hash, geometry, json.
REST API
# Get list of records
GET /items/articles?fields=id,title,slug,published_at&filter[status][_eq]=published&sort=-published_at&limit=10
# Get single record
GET /items/articles/123?fields=*,author.name,category.name
# Create record
POST /items/articles
{ "title": "New Article", "status": "draft" }
# Update
PATCH /items/articles/123
{ "status": "published" }
Authentication
# Get token
POST /auth/login
{ "email": "[email protected]", "password": "password" }
# Response: { "data": { "access_token": "...", "refresh_token": "...", "expires": 900000 } }
# Refresh token
POST /auth/refresh
{ "refresh_token": "..." }
# Usage
GET /items/articles
Authorization: Bearer <access_token>
Typical Project Stack
| Layer | Technology |
|---|---|
| CMS | Directus 10.x |
| Frontend | Next.js 14 |
| Database | PostgreSQL |
| Media | S3 / Cloudflare R2 |
| Cache | Redis |
| Deploy | Railway / VPS |
Integration with Next.js
// lib/directus.ts
import { createDirectus, rest, authentication, readItems, readItem } from '@directus/sdk'
const directus = createDirectus(process.env.DIRECTUS_URL!)
.with(rest())
.with(authentication())
// Static token for server requests
const staticToken = process.env.DIRECTUS_STATIC_TOKEN!
export async function getArticles(limit = 12) {
return directus.request(
readItems('articles', {
fields: ['id', 'title', 'slug', 'excerpt', 'published_at', { thumbnail: ['id', 'filename_disk'] }],
filter: { status: { _eq: 'published' } },
sort: ['-published_at'],
limit,
})
)
}
Flows (Automation)
Directus Flows — visual automation builder (trigger → actions):
- On article publish → send email
- On schedule → clear cache
- On order create → POST to CRM webhook
This does not require code — configured in admin panel.
Differences from Strapi
| Criterion | Directus | Strapi |
|---|---|---|
| Data schema | Directly in DB | In JSON files |
| Connection to existing DB | Yes | No |
| Extensibility | Extensions (TypeScript) | Plugins |
| Admin UI | More flexible | Standard |
| GraphQL | Yes | Via plugin |
Timeline
Basic website with 4–6 collections and Next.js frontend — 2–3 weeks. Complex project with Extensions, Flows, multilanguage — 4–6 weeks.







