Skip to content

feat(api): add GET /content/schema/{post,collection,author} endpoints#180

Open
bbornino wants to merge 2 commits into
playfulprogramming:mainfrom
bbornino:94-frontmatter-schema-endpoints
Open

feat(api): add GET /content/schema/{post,collection,author} endpoints#180
bbornino wants to merge 2 commits into
playfulprogramming:mainfrom
bbornino:94-frontmatter-schema-endpoints

Conversation

@bbornino

@bbornino bbornino commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds three new endpoints — GET /content/schema/post, GET /content/schema/collection, GET /content/schema/author — each returning the corresponding TypeBox frontmatter schema (PostMetaSchema, CollectionMetaSchema, AuthorMetaSchema) directly as the response body.
  • One route file per schema (schema-post.ts, schema-collection.ts, schema-author.ts), following the existing profiles.ts pattern and the project's one-route-per-file convention.
  • Registered in createApp.ts alongside the existing content routes.

These three schemas are exposed as-is with no transformation — downstream consumers should treat them as the source of truth for frontmatter shape, since they're the exact schemas the sync worker validates against.

Implementation note

Each route's response[200] schema is a permissive object (additionalProperties: true) with the real MetaSchema attached via examples, rather than using the MetaSchema itself as the response schema. Using the frontmatter schema directly as the Fastify response schema causes a 500 (fast-json-stringify treats it as the shape of post/collection/author data, not a JSON Schema document, and complains about missing required fields like title). The permissive-wrapper approach keeps runtime serialization working and still surfaces the real schema shape in the generated OpenAPI docs.

Design question for James

These three route files import PostMetaSchema/CollectionMetaSchema/AuthorMetaSchema directly from apps/worker/src/tasks/*/types.ts via a relative path, since that's where the schemas already live. This is the first cross-app import in the codebase — elsewhere, sharing goes through packages/*, with apps depending on packages rather than on each other directly. Flagging in case you'd rather these schemas move into packages/common (or a new small package) as a follow-up; Docker builds both apps from the same checkout today, but that's not a guarantee this pattern should rely on long-term.

Test plan

  • pnpm test:unit passes (lint, knip, publint, sherif, vitest across all NX projects)
  • pnpm prettier --check . passes on all changed files
  • Manually hit all three endpoints locally and confirmed 200 responses matching the exported schema objects
  • Inspected generated /openapi.json to confirm the response is documented sensibly (permissive schema + concrete example) rather than misleadingly requiring frontmatter fields on the response itself

Summary by CodeRabbit

  • New Features

    • Added new API endpoints that return JSON schema metadata for post, collection, and author frontmatter.
    • These routes are now available under /content/schema/*.
  • Tests

    • Added Jest test coverage for each new schema endpoint to confirm HTTP 200 responses and exact JSON payload matching the expected schema metadata.

Exposes the sync worker's TypeBox frontmatter schemas directly so
downstream consumers have a single source of truth for valid post,
collection, and author frontmatter shape.
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a3333e53-ea84-4e1b-8f32-2ea6015d9d26

📥 Commits

Reviewing files that changed from the base of the PR and between 09d6595 and afc39a5.

📒 Files selected for processing (4)
  • apps/api/src/routes/content/createSchemaRoute.ts
  • apps/api/src/routes/content/schema-author.ts
  • apps/api/src/routes/content/schema-collection.ts
  • apps/api/src/routes/content/schema-post.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/api/src/routes/content/createSchemaRoute.ts

📝 Walkthrough

Walkthrough

Three new content schema routes are added for post, collection, and author frontmatter, all built through a shared route factory. The app registers the new routes, and each endpoint has a corresponding test.

Changes

Content Schema Routes

Layer / File(s) Summary
Schema route factory
apps/api/src/routes/content/createSchemaRoute.ts
Adds a shared Fastify plugin factory that defines the 200 JSON response schema and serves the provided schema on GET requests.
Content schema routes and tests
apps/api/src/routes/content/schema-post.ts, apps/api/src/routes/content/schema-collection.ts, apps/api/src/routes/content/schema-author.ts, apps/api/src/routes/content/*.test.ts
Defines the post, collection, and author schema route plugins using the shared factory, and adds Fastify tests that assert each endpoint returns the expected schema.
App wiring
apps/api/src/createApp.ts
Imports and registers the three new schema route modules in createApp.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
    participant createApp
    participant createSchemaRoute
    participant Fastify
    participant Client

    createApp->>Fastify: register schema route plugins
    createSchemaRoute->>Fastify: define GET /content/schema/*
    Client->>Fastify: GET request
    Fastify-->>Client: 200 JSON schema response
Loading

Related PRs: None.

Suggested labels: api, enhancement

Suggested reviewers: None.

Poem:
Three schemas bloom in route-lined rows,
A shared factory quietly glows,
Post, collection, author sing,
Tests confirm each serving thing,
And createApp gives them wings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the three new GET schema endpoints added to the API.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/api/src/routes/content/schema-post.ts (2)

5-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stashing the real schema in examples is semantically off.

examples is meant to hold sample response instances, not the schema definition itself. Consumers/tools generating OpenAPI docs or client SDKs from examples may render the schema object as if it were example data, which is confusing. If avoiding Fastify's response validation is the real goal, consider a dedicated OpenAPI vendor extension (e.g. x-schema) instead of repurposing examples.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/routes/content/schema-post.ts` around lines 5 - 12, The
SchemaPostResponseSchema definition is using examples to store the schema
itself, which is the wrong semantic for response examples. Update the
SchemaPostResponseSchema in schema-post.ts so the real schema is not placed in
examples; instead, use a dedicated OpenAPI vendor extension like x-schema or
another non-example metadata field while keeping the response shape and
description intact.

5-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate this route-registration boilerplate across all three schema route files.

schema-post.ts, schema-collection.ts, and schema-author.ts are structurally identical apart from path, description, and schema reference. Consider extracting a small factory, e.g. createSchemaRoute(path, description, schema), to avoid triplicated logic and keep future changes (e.g., adjusting the response-schema workaround) in one place.

♻️ Example factory approach
// apps/api/src/routes/content/createSchemaRoute.ts
import type { FastifyPluginAsync } from "fastify";
import { Type, type TSchema } from "typebox";

export function createSchemaRoute(
	path: string,
	description: string,
	schema: TSchema,
): FastifyPluginAsync {
	const ResponseSchema = Type.Object(
		{},
		{ additionalProperties: true, description, examples: [schema] },
	);

	return async (fastify) => {
		fastify.get(
			path,
			{
				schema: {
					description,
					response: {
							description: "Successful",
							content: { "application/json": { schema: ResponseSchema } },
						},
					},
				},
			},
			async (_request, reply) => {
				reply.code(200);
				reply.send(schema);
			},
		);
	};
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/routes/content/schema-post.ts` around lines 5 - 38, The three
schema route files duplicate the same Fastify route-registration logic, so
extract a shared factory like createSchemaRoute and use it from schema-post,
schema-collection, and schema-author. Move the common fastify.get setup,
response-schema workaround, and reply.send behavior into the factory,
parameterized by path, description, and the schema reference (e.g.
PostMetaSchema/CollectionMetaSchema/AuthorMetaSchema), so future changes only
happen in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/api/src/routes/content/schema-post.ts`:
- Around line 5-12: The SchemaPostResponseSchema definition is using examples to
store the schema itself, which is the wrong semantic for response examples.
Update the SchemaPostResponseSchema in schema-post.ts so the real schema is not
placed in examples; instead, use a dedicated OpenAPI vendor extension like
x-schema or another non-example metadata field while keeping the response shape
and description intact.
- Around line 5-38: The three schema route files duplicate the same Fastify
route-registration logic, so extract a shared factory like createSchemaRoute and
use it from schema-post, schema-collection, and schema-author. Move the common
fastify.get setup, response-schema workaround, and reply.send behavior into the
factory, parameterized by path, description, and the schema reference (e.g.
PostMetaSchema/CollectionMetaSchema/AuthorMetaSchema), so future changes only
happen in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f2f16cea-4877-4b83-ad4b-7b4e7b52e628

📥 Commits

Reviewing files that changed from the base of the PR and between 086e182 and 09d6595.

📒 Files selected for processing (7)
  • apps/api/src/createApp.ts
  • apps/api/src/routes/content/schema-author.test.ts
  • apps/api/src/routes/content/schema-author.ts
  • apps/api/src/routes/content/schema-collection.test.ts
  • apps/api/src/routes/content/schema-collection.ts
  • apps/api/src/routes/content/schema-post.test.ts
  • apps/api/src/routes/content/schema-post.ts

Addresses CodeRabbit's suggestion on PR playfulprogramming#180 to de-duplicate the
identical route/response-wrapper boilerplate across schema-post.ts,
schema-collection.ts, and schema-author.ts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant