FluentCMS 0.1.7
See the version list below for details.
dotnet add package FluentCMS --version 0.1.7
NuGet\Install-Package FluentCMS -Version 0.1.7
<PackageReference Include="FluentCMS" Version="0.1.7" />
paket add FluentCMS --version 0.1.7
#r "nuget: FluentCMS, 0.1.7"
// Install FluentCMS as a Cake Addin #addin nuget:?package=FluentCMS&version=0.1.7 // Install FluentCMS as a Cake Tool #tool nuget:?package=FluentCMS&version=0.1.7
Fluent CMS - CRUD (Create, Read, Update, Delete) for any entities
Welcome to Fluent CMS If you find it useful, please give it a star ⭐
What is it
Fluent CMS is an open-source content management system designed to streamline web development workflows. It proves valuable even for non-CMS projects by eliminating the need for tedious CRUD API and page development.
- CRUD APIs: It offers a set of RESTful CRUD (Create, Read, Update, Delete) APIs for any entities based on your configuration, easily set up using the Schema Builder.
- Admin Panel UI: The system includes an Admin Panel UI for data management, featuring a rich set of input types such as datetime, dropdown, image, rich text, and a flexible query builder for data searching.
- Easy Integration: The Systems can be seamlessly integrated into your ASP.NET Core project via a NuGet package. You can extend your business logic by registering hook functions that execute before or after database access.
- Performance: The system is designed with performance in mind, boasting speeds 100 times faster than Strapi (details in the performance vs Strapi test). It is also as faster than manually written APIs using Entity Framework (details in the performance vs EF test).
- Easily Extensible The system can automatically generate
EntityCreated
,EntityUpdated
, andEntityDeleted
events and publish them to an event broker (such as Kafka). This makes it simple to extend functionality, such as adding consumers for OpenSearch, Elasticsearch, or document databases.
Live Demo - A blog website based on Fluent CMS
source code Example Blog Project.
- Admin Panel https://fluent-cms-admin.azurewebsites.net/
- Email:
admin@cms.com
- Password:
Admin1!
- Email:
- Public Site : https://fluent-cms-ui.azurewebsites.net/
Add Fluent CMS to your own project
The example project can be found at Example Blog Project.
Create your own Asp.net Core WebApplication.
Add FluentCMS package
dotnet add package FluentCMS
Modify Program.cs, add the following line before builder.Build(), the input parameter is the connection string of database.
builder.AddSqliteCms("Data Source=cms.db"); var app = builder.Build();
Currently FluentCMS support
AddSqliteCms
,AddSqlServerCms
,AddPostgresCMS
.Add the following line After builder.Build()
await app.UseCmsAsync();
this function bootstrap router, initialize Fluent CMS schema table Now that the web server is up and running, the next chapter will guide you through building the schema and managing data.
Develop a simple educational system use Fluent CMS
When designing a database schema for a simple educational system, you typically need to create tables for Teachers
, Courses
, and Students
.
Database Schema
1. Teachers Table
This table stores information about the teachers.
Column Name | Data Type | Description |
---|---|---|
Id |
INT |
Primary Key, unique ID for each teacher. |
FirstName |
VARCHAR |
Teacher's first name. |
LastName |
VARCHAR |
Teacher's last name. |
Email |
VARCHAR |
Teacher's email address. |
PhoneNumber |
VARCHAR |
Teacher's contact number. |
2. Courses Table
This table stores information about the courses.
Column Name | Data Type | Description |
---|---|---|
Id |
INT |
Primary Key, unique ID for each course. |
CourseName |
VARCHAR |
Name of the course. |
Description |
TEXT |
Brief description of the course. |
TeacherId |
INT |
Foreign Key, references TeacherId in the Teachers table. |
3. Students Table
This table stores information about the students.
Column Name | Data Type | Description |
---|---|---|
Id |
INT |
Primary Key, unique ID for each student. |
FirstName |
VARCHAR |
Student's first name. |
LastName |
VARCHAR |
Student's last name. |
Email |
VARCHAR |
Student's email address. |
EnrollmentDate |
DATE |
Date when the student enrolled. |
4. Enrollments Table (Junction Table)
This table manages the many-to-many relationship between Students
and Courses
, since a student can enroll in multiple courses, and a course can have multiple students.
Column Name | Data Type | Description |
---|---|---|
EnrollmentId |
INT |
Primary Key, unique ID for each enrollment. |
StudentId |
INT |
Foreign Key, references StudentId in the Students table. |
CourseId |
INT |
Foreign Key, references CourseId in the Courses table. |
Relationships:
- Teachers to Courses: One-to-Many (A teacher can teach multiple courses, but a course is taught by only one teacher).
- Students to Courses: Many-to-Many (A student can enroll in multiple courses, and each course can have multiple students).
Build Schema use Fluent CMS Schema builder
After starting your ASP.NET Core application, you will find a menu item labeled "Schema Builder" on the application's home page.
In the Schema Builder UI, you can add entities such as "Teacher" and "Student."
When adding the "Course" entity, start by adding basic attributes like "Name" and "Description." You can then define relationships by adding attributes as follows:
Teacher Attribute:
Configure it with the following settings:{ "DataType": "Int", "Field": "teacher", "Header": "Teacher", "InList": true, "InDetail": true, "IsDefault": false, "Type": "lookup", "Options": "teacher" }
Students Attribute:
Configure it with these settings:{ "DataType": "Na", "Field": "students", "Header": "Students", "InList": false, "InDetail": true, "IsDefault": false, "Type": "crosstable", "Options": "student" }
With these configurations, your minimal viable product is ready to use.
Extent functionality by add Hook functions
You need to add your own Business logic, for examples, you want to verify if the email and phone number of entity teacher
is valid.
you can register a cook function before insert or update teacher
app.RegisterCmsHook("teacher", [Occasion.BeforeInsert, Occasion.BeforeUpdate],(IDictionary<string,object> teacher) =>
{
var (email, phoneNumber) = ((string)teacher["email"], (string)teacher["phone_number"]);
if (!IsValidEmail())
{
throw new InvalidParamException($"email `{email}` is invalid");
}
if (!IsValidPhoneNumber())
{
throw new InvalidParamException($"phone number `{phoneNumber}` is invalid");
}
}
Permissions Control
Fluent CMS's permission control module is decoupled from the Content Management module, allowing you to implement your own permission logic or forgo permission control entirely.
The built-in permission control in Fluent CMS offers four privilege types for each entity:
- ReadWrite: Full access to read and write.
- RestrictedReadWrite: Users can only modify records they have created.
- Readonly: View-only access.
- RestrictedReadonly: Users can only view records they have created.
Additionally, Fluent CMS supports custom roles, where a user's privileges are a combination of their individual entity privileges and the privileges assigned to their role.
To enable fluentCMS' build-in permission control feature, add the following line to builder.
//add fluent cms' permission control service
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlite(connectionString));
builder.AddCmsAuth<IdentityUser, IdentityRole, AppDbContext>();
And add the follow line after app was built
//user fluent permission control feature
app.UseCmsAuth<IdentityUser>();
InvalidParamExceptionFactory.CheckResult(await app.EnsureCmsUser("sadmin@cms.com", "Admin1!", [Roles.Sa]));
InvalidParamExceptionFactory.CheckResult(await app.EnsureCmsUser("admin@cms.com", "Admin1!", [Roles.Admin]));
Behind the scene, fluentCMS leverage the hook mechanism.
Design Query
Here’s a text-based layout representation of the web page of the course introduction page.
Introduction to Web Development
Description:
This course provides an overview of web development...
Teacher: John Doe
- Skills:
- C++ (3 years)
- C# (5 years)
- HTML (7 years)
- Database (4 years)
Materials:
The data comes from several entities,
- course
- teacher
- skills
- teacher_skill
- material
- course_material
Fluent CMS offers Query
APIs to meet the following needs, similar to GraphQL queries:
- Single API Call: Allows the frontend to retrieve all related data with just one API call.
- Protection of Sensitive Information: Prevents sensitive data, like the teacher's phone number, from being exposed to the frontend.
- Performance: Reduces resource-intensive database queries, making it more suitable for public access.
To create or edit a query, navigate to Schema Builder
> Queries
.
A query has 3 parts
Selection Set
In the examples below, the main entity is course
:
teacher
is alookup
attribute ofcourse
.skills
is acrosstable
attributes ofteacher
.`materials
is acrosstable
attributes ofcourse
.
{
name,
id,
description,
teacher{
firstname,
lastname,
skills{
name,
years
}
},
materials{
name,
link,
file
}
}
Sorts
FluentCMS uses cursor-based pagination, unlike GraphQL, which supports both cursor- and offset-based pagination. Offset-based pagination is less stable and unsuitable for large datasets.
Cursor-based pagination retrieves the next page based on the last cursor. FluentCMS calculates the cursor and sorts data as shown below:
{
"sorts": [
{
"fieldName": "id",
"order": "Desc"
}
]
}
Filter
To prevent resource-intensive queries from the frontend, limit the number of exposed parameters.
In the filter definition below, qs.id
tries to resolve the ID from the query string parameter id
.
The qs.
prefix indicates that the value should be fetched from the query string, with the part after qs.
representing the key of the query string parameter.
For example, the API call /api/queries/<query-name>/one?id=3 corresponds to the SQL query:
select * from courses where level='advanced' and id=3
{
"filters": [
{
"fieldName": "level",
"operator": "and",
"omitFail": false,
"constraints": [
{
"match": "in",
"value": "advanced"
}
]
},
{
"fieldName": "id",
"operator": "and",
"omitFail": true,
"constraints": [
{
"match": "in",
"value": "qs.id"
}
]
}
]
}
Query Endpoints
Each query definition corresponds to three endpoints:
List: /api/queries/<query-name>
- retrieves a paginated list
- To view next page:
/api/queries/<query-name>?last=***
- To view previous page:
/api/queries/<query-name>?first=***
Example response:
{
"items": [],
"first": "",
"hasPrevious": false,
"last": "eyJpZCI6M30",
"hasNext": true
}
Single Record: /api/queries/<query-name>/one - Returns the first record
Example: /api/queries/<query-name>/one?id=***
Multiple Record: /api/queries/<query-name>/many
- Returns multiple records based on specified values.
Example:
/api/queries/<query-name>/one?id=1&id=2&id=3
.
If the number of IDs exceeds the allowed page size, only the first set of records will be returned.
Cache Settings:
- Query Settings are cached in memory for 1 minutes.
- Query Result are not cached because caching large data to memory is tricky and I intend implement stand alone cache module.
Produce Events to Kafka
The producing event functionality is implemented by adding hook functions behind the scene, to enable this functionality, you need add two line of code,
builder.AddKafkaMessageProducer("localhost:9092");
and app.RegisterMessageProducerHook()
.
builder.AddSqliteCms("Data Source=cmsapp.db").PrintVersion();
builder.AddKafkaMessageProducer("localhost:9092");
var app = builder.Build();
await app.UseCmsAsync(false);
app.RegisterMessageProducerHook();
We welcome contributions!
If you're interested in improving FluentCMS, please read our CONTRIBUTING.md guide.
Development
System Overviews
Web Server
- Tools:
- ASP.NET Core
- SqlKata: SqlKata
Admin Panel UI
- Tools:
- React
- PrimeReact: PrimeReact UI Library
- SWR: Data Fetching/State Management
Schema Builder UI
- Tools:
- jsoneditor: JSON Editor
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- AutoMapper (>= 13.0.1)
- Confluent.Kafka (>= 2.5.2)
- DynamicExpresso.Core (>= 2.16.1)
- FluentMigrator.Runner.Postgres (>= 5.2.0)
- FluentMigrator.Runner.Sqlite (>= 5.2.0)
- FluentResults (>= 3.16.0)
- GraphQL-Parser (>= 9.5.0)
- Handlebars.Net (>= 2.1.6)
- HtmlAgilityPack (>= 1.11.65)
- Microsoft.AspNetCore.Identity.EntityFrameworkCore (>= 8.0.8)
- Microsoft.AspNetCore.OpenApi (>= 8.0.6)
- Microsoft.AspNetCore.WebUtilities (>= 8.0.6)
- Microsoft.EntityFrameworkCore.Sqlite (>= 8.0.6)
- Microsoft.EntityFrameworkCore.SqlServer (>= 8.0.7)
- MongoDB.Driver (>= 2.28.0)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 8.0.4)
- SqlKata (>= 2.4.0)
- SqlKata.Execution (>= 2.4.0)
- Swashbuckle.AspNetCore (>= 6.4.0)
- System.Data.SqlClient (>= 4.8.6)
- System.Runtime.Caching (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on FluentCMS:
Repository | Stars |
---|---|
fluent-cms/fluent-cms
A headless CMS with GraphQL API and drag-and-drop page designer
|
Version | Downloads | Last updated |
---|---|---|
0.3.2 | 41 | 12/23/2024 |
0.3.1 | 42 | 12/17/2024 |
0.3.0.2 | 40 | 12/12/2024 |
0.3.0.1 | 43 | 12/12/2024 |
0.3.0 | 47 | 12/11/2024 |
0.2.8.1 | 85 | 11/29/2024 |
0.2.8 | 80 | 11/29/2024 |
0.2.7 | 84 | 11/29/2024 |
0.2.6 | 96 | 11/11/2024 |
0.2.5 | 95 | 10/30/2024 |
0.2.4 | 102 | 10/23/2024 |
0.2.3 | 113 | 10/2/2024 |
0.2.2 | 106 | 10/2/2024 |
0.2.1 | 109 | 9/26/2024 |
0.2.0 | 109 | 9/8/2024 |
0.1.8 | 109 | 9/8/2024 |
0.1.7 | 101 | 9/8/2024 |
0.1.6 | 115 | 9/8/2024 |
0.1.5 | 107 | 9/8/2024 |
0.1.4 | 122 | 8/30/2024 |
0.1.3 | 140 | 8/23/2024 |
0.1.2 | 124 | 8/23/2024 |
0.1.1 | 136 | 8/22/2024 |
0.1.0 | 135 | 8/22/2024 |
0.0.6 | 142 | 8/15/2024 |
0.0.5 | 126 | 8/15/2024 |
0.0.4 | 90 | 8/5/2024 |
0.0.3 | 157 | 7/31/2024 |