Fetching Content
Fetching Content
Recipes for retrieving published content from ModulaCMS. Content is served through slug-based routing -- each route maps a slug to a content tree. The content delivery endpoint assembles the tree, resolves references, and returns structured JSON.
For background on how routes and content trees work, see routing and content trees.
Get a Page by Slug
The primary frontend use case. Given a route slug, returns the full published content tree.
curl:
curl http://localhost:8080/api/v1/content/homepage
Go SDK:
import (
"context"
"encoding/json"
"fmt"
modula "github.com/hegner123/modulacms/sdks/go"
)
client, err := modula.NewClient(modula.ClientConfig{
BaseURL: "http://localhost:8080",
APIKey: "01HXK4N2F8RJZGP6VTQY3MCSW9",
})
if err != nil {
// handle error
}
raw, err := client.Content.GetPage(ctx, "homepage", "", "")
if err != nil {
if modula.IsNotFound(err) {
fmt.Println("no published content at this slug")
return
}
// handle error
}
fmt.Println(string(raw))
TypeScript SDK (read-only):
import { ModulaClient } from '@modulacms/sdk'
const client = new ModulaClient({
baseUrl: 'https://cms.example.com',
apiKey: 'YOUR_API_KEY',
})
const page = await client.getPage('homepage')
console.log(page)
TypeScript SDK (admin):
import { createAdminClient } from '@modulacms/admin-sdk'
const admin = createAdminClient({
baseUrl: 'https://cms.example.com',
apiKey: 'YOUR_API_KEY',
})
const page = await admin.contentDelivery.getPage('homepage')
console.log(page)
Get a Page with a Specific Output Format
The format parameter controls the response shape. Supported values: clean, raw, contentful, sanity, strapi, wordpress.
| Format | Description |
|---|---|
clean |
Flat object with resolved fields and children |
raw |
Internal tree structure with all IDs and metadata |
contentful |
Contentful-compatible structure |
sanity |
Sanity.io-compatible structure |
strapi |
Strapi-compatible structure |
wordpress |
WordPress-compatible structure |
curl:
curl "http://localhost:8080/api/v1/content/homepage?format=clean"
Go SDK:
raw, err := client.Content.GetPage(ctx, "homepage", "clean", "")
TypeScript SDK (read-only):
const page = await client.getPage('homepage', { format: 'clean' })
TypeScript SDK (admin):
const page = await admin.contentDelivery.getPage('homepage', 'clean')
Get a Page with Locale
Request content translated to a specific locale. When the locale has no translation, the CMS falls back along the locale's fallback chain (e.g., fr-CA falls back to fr, then to the default locale).
curl:
curl "http://localhost:8080/api/v1/content/homepage?locale=fr"
Go SDK:
raw, err := client.Content.GetPage(ctx, "homepage", "", "fr")
TypeScript SDK (read-only):
const page = await client.getPage('homepage', { locale: 'fr' })
Combine format and locale:
curl "http://localhost:8080/api/v1/content/homepage?format=clean&locale=fr"
raw, err := client.Content.GetPage(ctx, "homepage", "clean", "fr")
const page = await client.getPage('homepage', { format: 'clean', locale: 'fr' })
Get a Single Content Data Entry by ID
Fetch a single content node by its ULID. Useful when you already have the content ID from a relation or query result.
curl:
curl "http://localhost:8080/api/v1/contentdata/?q=01HXK4N2F8RJZGP6VTQY3MCSW9" \
-H "Authorization: Bearer YOUR_API_KEY"
Go SDK:
content, err := client.ContentData.Get(ctx, modula.ContentID("01HXK4N2F8RJZGP6VTQY3MCSW9"))
TypeScript SDK (admin):
const content = await admin.contentData.get('01HXK4N2F8RJZGP6VTQY3MCSW9' as ContentID)
List All Content for a Route
List all content data entries, then filter by route ID client-side. Or use the content delivery endpoint to get the full assembled tree for a route.
curl (list all content data):
curl http://localhost:8080/api/v1/contentdata \
-H "Authorization: Bearer YOUR_API_KEY"
Go SDK:
allContent, err := client.ContentData.List(ctx)
if err != nil {
// handle error
}
// Filter to a specific route
targetRouteID := modula.RouteID("01HXK4N2F8RJZGP6VTQY3MCSW9")
for _, c := range allContent {
if c.RouteID != nil && *c.RouteID == targetRouteID {
fmt.Printf("Content: %s (status: %s)\n", c.ContentDataID, c.Status)
}
}
TypeScript SDK (admin):
const allContent = await admin.contentData.list()
const targetRouteID = '01HXK4N2F8RJZGP6VTQY3MCSW9'
const routeContent = allContent.filter(c => c.route_id === targetRouteID)
Get Content Fields for a Content Data Entry
Retrieve all field values for a specific content node. Fields are stored separately from content data and linked by content_data_id.
curl:
curl http://localhost:8080/api/v1/contentfields \
-H "Authorization: Bearer YOUR_API_KEY"
Go SDK:
allFields, err := client.ContentFields.List(ctx)
if err != nil {
// handle error
}
targetContentID := modula.ContentID("01HXK4N2F8RJZGP6VTQY3MCSW9")
for _, f := range allFields {
if f.ContentDataID != nil && *f.ContentDataID == targetContentID {
fmt.Printf("Field %s = %s\n", f.FieldID, f.FieldValue)
}
}
TypeScript SDK (read-only):
const fields = await client.listContentFields()
const targetID = '01HXK4N2F8RJZGP6VTQY3MCSW9'
const nodeFields = fields.filter(f => f.content_data_id === targetID)
for (const f of nodeFields) {
console.log(`${f.field_id} = ${f.field_value}`)
}
TypeScript SDK (admin):
const fields = await admin.contentFields.list()
const nodeFields = fields.filter(f => f.content_data_id === targetID)
Error Handling
All SDKs return typed errors that can be inspected for HTTP status codes.
Go SDK:
raw, err := client.Content.GetPage(ctx, "nonexistent", "", "")
if err != nil {
if modula.IsNotFound(err) {
// 404 -- no published content at this slug
}
if modula.IsUnauthorized(err) {
// 401 -- invalid or missing API key
}
var apiErr *modula.ApiError
if errors.As(err, &apiErr) {
fmt.Printf("HTTP %d: %s\n", apiErr.StatusCode, apiErr.Message)
}
}
TypeScript SDK:
import { ApiError, isNotFound, isUnauthorized } from '@modulacms/sdk'
try {
const page = await client.getPage('nonexistent')
} catch (err) {
if (isNotFound(err)) {
// 404
}
if (isUnauthorized(err)) {
// 401
}
}