CASE STUDY / CONTENT PLATFORM
Linze.pro
Current project
From a Vue Blog to a Bilingual Next.js Content Platform
A server-first rebuild of a Vue blog with Next.js App Router, keeping the Go/chi content API and existing article model while adding Markdown publishing, bilingual fallback, SEO, article engagement and content administration flows.
01 / MIGRATION BRIEF
Why rebuild the blog frontend?
The old Vue frontend had component and rendering boundaries that were difficult to extend, while article display, language switching, SEO and engagement logic were becoming coupled. The goal was not to translate Vue files one by one, but to rebuild the content path from Markdown editing and versioning to public reading without replacing the existing Go backend or article data.
- Keep the Go content API and existing article data while replacing the public frontend incrementally
- Make Markdown Front-matter a validated and versioned content entry point
- Support Chinese and English through independent versions and explicit fallback state
- Treat SEO, reading experience, engagement and observability as one delivery path
02 / CONTENT LIFECYCLE
How an article moves from Markdown to a public page
- 01
Authoring
An authenticated Next.js admin workspace uploads or edits Markdown files, while JWT and RBAC protect publishing and update endpoints.
- 02
Validate
The Go backend parses Front-matter, validates the slug, title, content length, tag count and dates, then normalises duplicate tags.
- 03
Persist
Publishing and updates write post_translations, revision snapshots and engagement records in a transaction, while synchronising legacy posts fields for compatibility.
- 04
Resolve
A Next.js Server Component requests the target locale; the Go store prefers an exact match, falls back to Chinese when needed and returns the available locales and fallback state.
- 05
Render
The server renders Markdown, the table of contents, highlighted code and structured data. The browser then uses a separate engagement endpoint for views and anonymous likes.
03 / ENGINEERING DECISIONS
Decisions visible in the code
- 01
Migrate incrementally instead of rewriting the backend
ImplementationThe public frontend moves to Next.js App Router while continuing to use the Go/chi REST API, PostgreSQL and existing article data. Legacy Vue-compatible endpoints remain available during the transition.
ValueMigration risk stays within frontend rendering and content boundaries, allowing the old site, new site and data layer to coexist while the system changes.
- 02
Model bilingual content as translation records
Implementationpost_translations uses (post_slug, locale) as a composite key, while post_translation_revisions stores snapshots for each language. Reads return requestedLocale, resolvedLocale and fallback explicitly.
ValueChinese and English can be published independently, and missing content is handled by the backend instead of guessed by the frontend.
- 03
Keep Markdown parsing and version rules at the backend boundary
ImplementationGo centrally parses YAML Front-matter and validates slugs, field lengths, tags and dates. Updates include the previous version, increment it on success and append a revision snapshot.
ValueContent format and concurrent update rules stay in one service boundary, preventing silent overwrites between admin sessions.
- 04
Render public content server-first
ImplementationNext.js Server Components fetch article data and generate Metadata, Canonical, alternate links, Open Graph, Article JSON-LD, sitemap and RSS output. Markdown uses GFM, heading slugs, code highlighting and a table of contents.
ValueSEO, first-screen content and reading structure remain available on the server, while personalised likes are isolated to client-side requests.
- 05
Make anonymous engagement idempotent and bounded
ImplementationThe backend issues a signed HttpOnly visitor cookie and stores only an HMAC-derived hash in Redis and PostgreSQL. A unique constraint and transaction make likes idempotent, while daily views use deduplication and Redis provides fast filtering and rate limiting.
ValueVisitors can like without creating an account and see their state again without treating a client-side counter as the source of truth.
- 06
Treat observability as part of the content service
ImplementationThe Go API exposes Prometheus HTTP, database and business metrics and optionally exports HTTP, database and Redis spans through OpenTelemetry OTLP/gRPC. Health, readiness, timeouts and graceful shutdown manage the service lifecycle.
ValueArticle requests, translation fallbacks, engagement writes and infrastructure bottlenecks have traceable entry points instead of relying only on user reports.
04 / CURRENT BOUNDARIES
What still needs to converge
These limitations are verifiable in the current code and provide concrete entry points for the next iteration of the content platform.
- High priority
The legacy and new content models still coexist
post_translations is now the main path for localised reads and versioning, but the legacy posts columns remain and are synchronised on publish and update. The next step is to define one source of truth and retire the compatibility write path.
- Medium priority
Search and filtering need a server-side path at scale
The frontend currently fetches a bounded set of posts and performs part of the filtering locally. A larger archive should move search, tags, years and pagination into the Go API with stable ordering and indexes.
- Medium priority
Content images do not yet have a complete optimisation path
Markdown images currently render as ordinary img elements without intrinsic dimensions. Dimensions, responsive loading and CDN or cache policy should be added to improve CLS and mobile loading.
- Medium priority
Redis rate limiting is a degradable guard, not absolute anti-abuse
Engagement requests fail open when Redis is unavailable to preserve blog availability, and visitors can clear their cookie. This provides baseline deduplication and throttling, not strong identity or complete abuse prevention.
- Medium priority
AI is a development workflow, not a product capability here
AI can assist problem decomposition, option comparison and implementation review, but this project does not currently integrate an LLM API, RAG, embeddings or an agent. The case study does not present those as implemented features.
05 / NEXT ITERATION
What to improve next
- 01
Converge on post_translations as the single content source and retire legacy dual writes
- 02
Move search, tag filtering, archive and pagination into the Go API with precise cache invalidation
- 03
Add image dimensions, responsive loading and observable Core Web Vitals metrics for Markdown media
- 04
Add end-to-end coverage for publishing, locale fallback, version conflicts and engagement idempotency
- 05
Complete publishing audit, rollback and targeted cache refresh flows in the admin workspace