Laraue.CmsBackend больше не развивается. Это была .NET библиотека, которая превращала Markdown файлы с frontmatter в типизированный REST API, и около года на ней работал этот блог. В октябре 2026 мы отказались от неё и перенесли контент в приложение на Nuxt: поддерживать в согласованном состоянии API и фронтенд оказалось дороже, чем пользы от API. Подробно об этом в статье.
Репозиторий остаётся открытым, но мы больше не развиваем и не используем библиотеку. Эта страница — всё, что осталось от проекта: что умела библиотека, как выглядел код и одна ошибка, о которой больше нигде не написано.
| Язык | C#, .NET 10 |
| Тип проекта | Библиотека |
| Статус | Открыта, не в архиве, новых коммитов нет |
| Лицензия | MIT |
| NuGet | |
| Загрузки | |
| GitHub | Laraue.CmsBackend |
Что умела библиотека
Идея была такой: хранить блог в .md файлах в Git, отдельно от фронтенда, и забирать их через API без базы данных. Библиотека делала четыре вещи:
- Типизированные схемы контента. C# класс с
requiredсвойствами описывает frontmatter. Файл без обязательного поля роняет приложение при запуске, а не превращается позже в сломанную страницу. - Фильтрация и сортировка. Запрос по любому полю frontmatter, список тегов, постраничная выдача.
- Рендеринг. Тело Markdown превращалось в HTML, а для оглавления строился список внутренних ссылок.
- Проекция свойств. Запрос перечислял нужные поля, и возвращались только они, поэтому списки оставались лёгкими.
Как это выглядело в коде
Тип контента, Markdown файл и хост. Тип наследуется от BaseContentType:
public class Article : BaseContentType
{
public required string[] Projects { get; init; }
public required string Description { get; init; }
}
Файлы повторяют структуру адресов фронтенда (blog/articles/article1.md):
---
title: О моём проекте
projects: [Project1, Project2]
description: Короткое описание
---
Здесь текст статьи.
Хост читает папку один раз при запуске:
var cmsBackend = new CmsBackendBuilder(
new MarkdownParser(
new MarkdownToHtmlTransformer(),
new ArticleInnerLinksGenerator()),
new MarkdownProcessor())
.AddContentType<Article>()
.AddContentFolder("blog")
.Build();
Эндпоинт сам выбирает, какие поля вернуть и в какой DTO их положить. Так выглядел список статей в контроллере настоящего блога (полный код):
[HttpPost("list")]
public IShortPaginatedResult<CardItem> GetList([FromBody] GetCardsRequest request)
{
return cmsBackend.GetEntities<CardItem>(new GetEntitiesRequest
{
FromPath = request.Path,
LanguageCode = request.LanguageCode,
Properties = ["fileName", "title", "description", "path", "length(content)", "tags"],
Pagination = request.Pagination,
});
}
В Properties можно было писать и выражения: length(content) возвращало размер отрендеренного текста, а format(createdAt, "dd MMM yyyy") форматировало дату на сервере. Второе оказалось ошибкой, о ней ниже.
Чему мы научились
Не отказывайтесь от SSR на сайте с контентом. Первая версия блога работала без серверного рендеринга. Рассуждение казалось разумным: Google умеет выполнять JavaScript, API отвечал быстро, а без SSR проще инфраструктура. На деле новые статьи неделями не попадали в индекс. Исправили включением рендеринга на сервере: теперь поисковый робот получает готовый HTML с первого запроса. Если людей к вам приводит поиск, не рассчитывайте, что робот дождётся вашего JavaScript.
Не заставляйте API форматировать данные для страницы. Из-за форматирования даты на сервере русские страницы показывали «26 Jun 2026», а чтобы это исправить, пришлось менять контракт бэкенда, а не страницу. То же повторилось с названиями проектов, ошибками рендерера и каждым новым DTO для списка. Полный перечень — в статье.
Отдельный API для контента оправдан не всегда. Он нужен, когда контент правят не разработчики через админку, когда один и тот же контент берут несколько клиентов (сайт, приложение, рассылка) или когда у контента свой цикл публикации. У нас ничего из этого не было: небольшая команда, один сайт, контент в Git.
Если вы выбираете сейчас
Если ваш сайт на Nuxt, сначала посмотрите Nuxt Content. Это та же идея, но её поддерживают другие люди, а контент лежит рядом со страницами. Мы заменили библиотеку собственным небольшим каталогом, около 700 строк на TypeScript, потому что наши задачи были простыми.
Код бэкенда блога, который работал на этой библиотеке, по-прежнему доступен в репозитории Laraue.Apps.Blog, если нужен живой пример для чтения. С самими файлами помогут наши инструменты: конвертер Markdown в HTML показывает живой предпросмотр, а переводчик Markdown переводит текст .md файла и сохраняет заголовки, блоки кода и таблицы.