page.mdx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. export const metadata = { title: "OpenAPI Integration" }
  2. # OpenAPI Integration
  3. Use json-render to generate dynamic forms and UIs from [OpenAPI/Swagger](https://swagger.io/specification/) schemas.
  4. <div className="rounded-lg border border-amber-500/50 bg-amber-500/10 p-4 mb-8">
  5. <p className="text-sm text-amber-700 dark:text-amber-300">
  6. <strong>Concept:</strong> This page demonstrates how json-render can support OpenAPI schemas. The examples are illustrative and may require adaptation for production use.
  7. </p>
  8. </div>
  9. ## Why OpenAPI?
  10. OpenAPI specifications describe your API's endpoints, request bodies, and response schemas. By converting OpenAPI schemas to json-render specs, you can:
  11. - Automatically generate forms for API endpoints
  12. - Display API responses with type-aware rendering
  13. - Keep your UI in sync with your API schema
  14. - Let AI generate UIs that match your API contracts
  15. ## Example OpenAPI Schema
  16. A typical OpenAPI schema for a request body:
  17. ```json
  18. {
  19. "openapi": "3.0.0",
  20. "paths": {
  21. "/users": {
  22. "post": {
  23. "summary": "Create a new user",
  24. "operationId": "createUser",
  25. "requestBody": {
  26. "required": true,
  27. "content": {
  28. "application/json": {
  29. "schema": {
  30. "$ref": "#/components/schemas/CreateUserRequest"
  31. }
  32. }
  33. }
  34. }
  35. }
  36. }
  37. },
  38. "components": {
  39. "schemas": {
  40. "CreateUserRequest": {
  41. "type": "object",
  42. "required": ["email", "name"],
  43. "properties": {
  44. "name": {
  45. "type": "string",
  46. "description": "User's full name",
  47. "minLength": 1,
  48. "maxLength": 100
  49. },
  50. "email": {
  51. "type": "string",
  52. "format": "email",
  53. "description": "User's email address"
  54. },
  55. "age": {
  56. "type": "integer",
  57. "minimum": 0,
  58. "maximum": 150,
  59. "description": "User's age"
  60. },
  61. "role": {
  62. "type": "string",
  63. "enum": ["admin", "user", "guest"],
  64. "default": "user",
  65. "description": "User's role"
  66. },
  67. "preferences": {
  68. "type": "object",
  69. "properties": {
  70. "newsletter": {
  71. "type": "boolean",
  72. "default": false
  73. },
  74. "theme": {
  75. "type": "string",
  76. "enum": ["light", "dark", "system"]
  77. }
  78. }
  79. }
  80. }
  81. }
  82. }
  83. }
  84. }
  85. ```
  86. ## Define an OpenAPI-to-UI Catalog
  87. Create components that map to OpenAPI data types:
  88. ```typescript
  89. import { defineCatalog } from '@json-render/core';
  90. import { schema } from '@json-render/react';
  91. import { z } from 'zod';
  92. export const openapiCatalog = defineCatalog(schema, {
  93. components: {
  94. Form: {
  95. description: 'API form container',
  96. props: z.object({
  97. operationId: z.string(),
  98. endpoint: z.string(),
  99. method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
  100. title: z.string().optional(),
  101. description: z.string().optional(),
  102. }),
  103. },
  104. StringField: {
  105. description: 'String input field',
  106. props: z.object({
  107. name: z.string(),
  108. label: z.string(),
  109. description: z.string().optional(),
  110. required: z.boolean().optional(),
  111. format: z.enum(['text', 'email', 'uri', 'uuid', 'date', 'date-time', 'password']).optional(),
  112. minLength: z.number().optional(),
  113. maxLength: z.number().optional(),
  114. pattern: z.string().optional(),
  115. placeholder: z.string().optional(),
  116. defaultValue: z.string().optional(),
  117. }),
  118. },
  119. NumberField: {
  120. description: 'Number input field',
  121. props: z.object({
  122. name: z.string(),
  123. label: z.string(),
  124. description: z.string().optional(),
  125. required: z.boolean().optional(),
  126. type: z.enum(['integer', 'number']).optional(),
  127. minimum: z.number().optional(),
  128. maximum: z.number().optional(),
  129. defaultValue: z.number().optional(),
  130. }),
  131. },
  132. BooleanField: {
  133. description: 'Boolean toggle field',
  134. props: z.object({
  135. name: z.string(),
  136. label: z.string(),
  137. description: z.string().optional(),
  138. defaultValue: z.boolean().optional(),
  139. }),
  140. },
  141. EnumField: {
  142. description: 'Enum selection field',
  143. props: z.object({
  144. name: z.string(),
  145. label: z.string(),
  146. description: z.string().optional(),
  147. required: z.boolean().optional(),
  148. options: z.array(z.object({
  149. value: z.string(),
  150. label: z.string().optional(),
  151. })),
  152. defaultValue: z.string().optional(),
  153. }),
  154. },
  155. ObjectField: {
  156. description: 'Nested object group',
  157. props: z.object({
  158. name: z.string(),
  159. label: z.string(),
  160. description: z.string().optional(),
  161. collapsible: z.boolean().optional(),
  162. }),
  163. },
  164. },
  165. actions: {
  166. submit: {
  167. description: 'Submit form to API endpoint',
  168. params: z.object({ operationId: z.string() }),
  169. },
  170. reset: {
  171. description: 'Reset form to defaults',
  172. params: z.object({}),
  173. },
  174. },
  175. });
  176. ```
  177. ## Convert OpenAPI Schema to Spec
  178. Transform OpenAPI schemas into json-render specs by recursively walking the schema properties and mapping each type to the corresponding catalog component. The converter handles nested objects, enums, arrays, and all primitive types.
  179. ## Usage Example
  180. ```tsx
  181. 'use client';
  182. import { OpenAPIForm } from './openapi-form';
  183. import { operationToSpec } from './openapi-to-spec';
  184. // Your OpenAPI schema (typically loaded from your API)
  185. const createUserSchema = {
  186. type: 'object',
  187. required: ['email', 'name'],
  188. properties: {
  189. name: { type: 'string', description: "User's full name" },
  190. email: { type: 'string', format: 'email', description: "User's email" },
  191. age: { type: 'integer', minimum: 0, maximum: 150 },
  192. role: { type: 'string', enum: ['admin', 'user', 'guest'], default: 'user' },
  193. },
  194. };
  195. // Convert to spec
  196. const spec = operationToSpec(
  197. 'createUser',
  198. 'POST',
  199. '/api/users',
  200. createUserSchema,
  201. 'Create User',
  202. 'Add a new user to the system',
  203. );
  204. export function CreateUserForm() {
  205. const handleSubmit = async (data: Record<string, unknown>) => {
  206. const response = await fetch('/api/users', {
  207. method: 'POST',
  208. headers: { 'Content-Type': 'application/json' },
  209. body: JSON.stringify(data),
  210. });
  211. if (response.ok) {
  212. console.log('User created!');
  213. }
  214. };
  215. return <OpenAPIForm spec={spec} onSubmit={handleSubmit} />;
  216. }
  217. ```
  218. ## Auto-generating from OpenAPI Document
  219. Load and parse an OpenAPI document to generate forms for all operations:
  220. ```typescript
  221. import SwaggerParser from '@apidevtools/swagger-parser';
  222. import { operationToSpec } from './openapi-to-spec';
  223. export async function loadOpenAPISpecs(specUrl: string) {
  224. const api = await SwaggerParser.dereference(specUrl);
  225. const specs: Record<string, any> = {};
  226. for (const [path, methods] of Object.entries(api.paths)) {
  227. for (const [method, operation] of Object.entries(methods)) {
  228. if (!operation.requestBody?.content?.['application/json']?.schema) continue;
  229. const schema = operation.requestBody.content['application/json'].schema;
  230. const operationId = operation.operationId || `${method}_${path.replace(/\//g, '_')}`;
  231. specs[operationId] = operationToSpec(
  232. operationId,
  233. method,
  234. path,
  235. schema,
  236. operation.summary,
  237. operation.description,
  238. );
  239. }
  240. }
  241. return specs;
  242. }
  243. // Usage
  244. const specs = await loadOpenAPISpecs('https://api.example.com/openapi.json');
  245. // specs.createUser, specs.updateUser, etc.
  246. ```
  247. ## Next
  248. Learn about [streaming](/docs/streaming) for progressive UI rendering.