A modern, API-first headless content management system built with NestJS, TypeScript, and Prisma.
- 🔐 Authentication & Authorization: JWT-based authentication with role-based access control
- 👥 User Management: Complete user CRUD operations with roles (OWNER, EDITOR, VIEWER)
- 🏢 Organization Management: Multi-tenant support with organization isolation
- 📝 Content Types: Dynamic content type definitions with flexible field system
- 📄 Content Management: Create, read, update, delete content with publishing workflow
- 🏷️ Categories & Tags: Organize content with categories and tags for better content management
- 🖼️ Media Management: File upload and management with metadata
- 🗄️ Database: SQLite for development, PostgreSQL ready for production
- 📚 API Documentation: Interactive Swagger/OpenAPI documentation
- 🔍 Content Filtering: Filter content by content type, status, categories, and tags
- Framework: NestJS with TypeScript
- Database: SQLite (development) / PostgreSQL (production)
- ORM: Prisma with migrations
- Authentication: JWT + Passport
- Documentation: Swagger/OpenAPI
- Validation: class-validator
- File Upload: Multer
- Node.js 18+
- npm or yarn
- Clone and install dependencies:
git clone <repository-url>
cd headlesscontent
npm install- Set up environment variables:
cp .env.example .env
# Edit .env with your configuration- Set up the database:
npm run db:setup- Start the development server:
npm run start:devThe API will be available at http://localhost:3000
API Documentation: http://localhost:3000/api/docs
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "admin123"
}GET /api/v1/organizations- Get all organizationsPOST /api/v1/organizations- Create organizationGET /api/v1/organizations/:id- Get organization by IDGET /api/v1/organizations/slug/:slug- Get organization by slugGET /api/v1/organizations/:id/users- Get users in organizationPATCH /api/v1/organizations/:id- Update organizationDELETE /api/v1/organizations/:id- Delete organization
GET /api/v1/users- Get all usersPOST /api/v1/users- Create userGET /api/v1/users/:id- Get user by IDPATCH /api/v1/users/:id- Update userDELETE /api/v1/users/:id- Delete user
GET /api/v1/content-types- Get all content typesPOST /api/v1/content-types- Create content typeGET /api/v1/content-types/:id- Get content type by IDGET /api/v1/content-types/slug/:slug- Get content type by slugPATCH /api/v1/content-types/:id- Update content typeDELETE /api/v1/content-types/:id- Delete content type
GET /api/v1/categories- Get all categoriesPOST /api/v1/categories- Create categoryGET /api/v1/categories/:id- Get category by IDGET /api/v1/categories/slug/:slug- Get category by slugGET /api/v1/categories/:id/content- Get content by categoryPATCH /api/v1/categories/:id- Update categoryDELETE /api/v1/categories/:id- Delete category
GET /api/v1/tags- Get all tagsPOST /api/v1/tags- Create tagGET /api/v1/tags/:id- Get tag by IDGET /api/v1/tags/slug/:slug- Get tag by slugGET /api/v1/tags/:id/content- Get content by tagPATCH /api/v1/tags/:id- Update tagDELETE /api/v1/tags/:id- Delete tag
GET /api/v1/content- Get all content (with filtering)POST /api/v1/content- Create contentGET /api/v1/content/:id- Get content by IDGET /api/v1/content/slug/:slug- Get content by slugPATCH /api/v1/content/:id- Update contentPOST /api/v1/content/:id/publish- Publish contentDELETE /api/v1/content/:id- Delete content
The content endpoint supports various query parameters:
contentTypeId- Filter by content typestatus- Filter by status (DRAFT, PUBLISHED, ARCHIVED)categoryId- Filter by categorytagId- Filter by taglimit- Number of items per page (default: 10)offset- Number of items to skip (default: 0)
Example:
GET /api/v1/content?categoryId=123&status=PUBLISHED&limit=20POST /api/v1/media/upload- Upload fileGET /api/v1/media- Get all mediaGET /api/v1/media/:id- Get media by IDPATCH /api/v1/media/:id- Update media metadataDELETE /api/v1/media/:id- Delete media
The CMS uses a flexible content type system where you can define custom fields for different types of content.
text- Single line text inputtextarea- Multi-line text inputnumber- Numeric inputboolean- Checkboxdate- Date pickermedia- File/media uploadselect- Dropdown selection
{
"name": "Blog Post",
"slug": "blog-post",
"description": "A blog post content type",
"fields": [
{
"name": "title",
"label": "Title",
"type": "text",
"required": true,
"placeholder": "Enter the post title"
},
{
"name": "content",
"label": "Content",
"type": "textarea",
"required": true,
"placeholder": "Write your blog post content here..."
},
{
"name": "featuredImage",
"label": "Featured Image",
"type": "media",
"required": false
}
]
}The CMS includes a robust categorization system:
- Hierarchical organization: Group content by main topics
- Color coding: Each category can have a color for UI display
- Content filtering: Filter content by category
- SEO friendly: Categories have slugs for URL-friendly access
- Flexible labeling: Add multiple tags to content
- Color coding: Each tag can have a color for UI display
- Content filtering: Filter content by tags
- SEO friendly: Tags have slugs for URL-friendly access
// Create content with categories and tags
{
"title": "My Blog Post",
"slug": "my-blog-post",
"content": {
"title": "My Blog Post",
"content": "This is my blog post content..."
},
"contentTypeId": "content-type-id",
"categoryIds": ["category-1-id", "category-2-id"],
"tagIds": ["tag-1-id", "tag-2-id", "tag-3-id"]
}users- User accounts and authenticationorganizations- Multi-tenant organizationscontent_types- Dynamic content type definitionscategories- Content categoriestags- Content tagscontent- Content entriescontent_categories- Many-to-many relationship between content and categoriescontent_tags- Many-to-many relationship between content and tagsmedia- File metadatacontent_media- Many-to-many relationship between content and media
- Users belong to organizations
- Content belongs to organizations
- Content can belong to multiple categories
- Content can have multiple tags
- Content can have multiple media files
- All entities track creator and updater information
# Database
npm run db:generate # Generate Prisma client
npm run db:push # Push schema changes to database
npm run db:migrate # Create and apply migrations
npm run db:migrate:deploy # Deploy migrations to production
npm run db:studio # Open Prisma Studio
npm run db:seed # Seed database with sample data
npm run db:reset # Reset database
npm run db:setup # Full database setup (reset + seed)
# Development
npm run start:dev # Start development server
npm run build # Build for production
npm run start:prod # Start production server
npm run test # Run tests
npm run test:e2e # Run end-to-end tests
npm run lint # Run ESLint# Database
DATABASE_URL="postgresql://user:password@localhost:5432/headlesscms"
# JWT
JWT_SECRET="your-super-secret-jwt-key"
JWT_EXPIRES_IN="7d"
# Server
PORT=3000
NODE_ENV=production
# File Upload
MAX_FILE_SIZE=10485760
UPLOAD_PATH="./uploads"- Set up PostgreSQL database
- Configure environment variables
- Run database migrations:
npm run db:migrate:deploy - Build the application:
npm run build - Start the server:
npm run start:prod
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
This project is licensed under the MIT License.