page.mdx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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 { createCatalog } from '@json-render/core';
  90. import { z } from 'zod';
  91. export const openapiCatalog = createCatalog({
  92. components: {
  93. Form: {
  94. description: 'API form container',
  95. props: z.object({
  96. operationId: z.string(),
  97. endpoint: z.string(),
  98. method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
  99. title: z.string().optional(),
  100. description: z.string().optional(),
  101. }),
  102. },
  103. StringField: {
  104. description: 'String input field',
  105. props: z.object({
  106. name: z.string(),
  107. label: z.string(),
  108. description: z.string().optional(),
  109. required: z.boolean().optional(),
  110. format: z.enum(['text', 'email', 'uri', 'uuid', 'date', 'date-time', 'password']).optional(),
  111. minLength: z.number().optional(),
  112. maxLength: z.number().optional(),
  113. pattern: z.string().optional(),
  114. placeholder: z.string().optional(),
  115. defaultValue: z.string().optional(),
  116. }),
  117. },
  118. NumberField: {
  119. description: 'Number input field',
  120. props: z.object({
  121. name: z.string(),
  122. label: z.string(),
  123. description: z.string().optional(),
  124. required: z.boolean().optional(),
  125. type: z.enum(['integer', 'number']).optional(),
  126. minimum: z.number().optional(),
  127. maximum: z.number().optional(),
  128. defaultValue: z.number().optional(),
  129. }),
  130. },
  131. BooleanField: {
  132. description: 'Boolean toggle field',
  133. props: z.object({
  134. name: z.string(),
  135. label: z.string(),
  136. description: z.string().optional(),
  137. defaultValue: z.boolean().optional(),
  138. }),
  139. },
  140. EnumField: {
  141. description: 'Enum selection field',
  142. props: z.object({
  143. name: z.string(),
  144. label: z.string(),
  145. description: z.string().optional(),
  146. required: z.boolean().optional(),
  147. options: z.array(z.object({
  148. value: z.string(),
  149. label: z.string().optional(),
  150. })),
  151. defaultValue: z.string().optional(),
  152. }),
  153. },
  154. ObjectField: {
  155. description: 'Nested object group',
  156. props: z.object({
  157. name: z.string(),
  158. label: z.string(),
  159. description: z.string().optional(),
  160. collapsible: z.boolean().optional(),
  161. }),
  162. },
  163. },
  164. actions: {
  165. submit: {
  166. description: 'Submit form to API endpoint',
  167. params: z.object({ operationId: z.string() }),
  168. },
  169. reset: {
  170. description: 'Reset form to defaults',
  171. params: z.object({}),
  172. },
  173. },
  174. });
  175. ```
  176. ## Convert OpenAPI Schema to Spec
  177. 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.
  178. ## Usage Example
  179. ```tsx
  180. 'use client';
  181. import { OpenAPIForm } from './openapi-form';
  182. import { operationToSpec } from './openapi-to-spec';
  183. // Your OpenAPI schema (typically loaded from your API)
  184. const createUserSchema = {
  185. type: 'object',
  186. required: ['email', 'name'],
  187. properties: {
  188. name: { type: 'string', description: "User's full name" },
  189. email: { type: 'string', format: 'email', description: "User's email" },
  190. age: { type: 'integer', minimum: 0, maximum: 150 },
  191. role: { type: 'string', enum: ['admin', 'user', 'guest'], default: 'user' },
  192. },
  193. };
  194. // Convert to spec
  195. const spec = operationToSpec(
  196. 'createUser',
  197. 'POST',
  198. '/api/users',
  199. createUserSchema,
  200. 'Create User',
  201. 'Add a new user to the system',
  202. );
  203. export function CreateUserForm() {
  204. const handleSubmit = async (data: Record<string, unknown>) => {
  205. const response = await fetch('/api/users', {
  206. method: 'POST',
  207. headers: { 'Content-Type': 'application/json' },
  208. body: JSON.stringify(data),
  209. });
  210. if (response.ok) {
  211. console.log('User created!');
  212. }
  213. };
  214. return <OpenAPIForm spec={spec} onSubmit={handleSubmit} />;
  215. }
  216. ```
  217. ## Auto-generating from OpenAPI Document
  218. Load and parse an OpenAPI document to generate forms for all operations:
  219. ```typescript
  220. import SwaggerParser from '@apidevtools/swagger-parser';
  221. import { operationToSpec } from './openapi-to-spec';
  222. export async function loadOpenAPISpecs(specUrl: string) {
  223. const api = await SwaggerParser.dereference(specUrl);
  224. const specs: Record<string, any> = {};
  225. for (const [path, methods] of Object.entries(api.paths)) {
  226. for (const [method, operation] of Object.entries(methods)) {
  227. if (!operation.requestBody?.content?.['application/json']?.schema) continue;
  228. const schema = operation.requestBody.content['application/json'].schema;
  229. const operationId = operation.operationId || `${method}_${path.replace(/\//g, '_')}`;
  230. specs[operationId] = operationToSpec(
  231. operationId,
  232. method,
  233. path,
  234. schema,
  235. operation.summary,
  236. operation.description,
  237. );
  238. }
  239. }
  240. return specs;
  241. }
  242. // Usage
  243. const specs = await loadOpenAPISpecs('https://api.example.com/openapi.json');
  244. // specs.createUser, specs.updateUser, etc.
  245. ```
  246. ## Next
  247. Learn about [streaming](/docs/streaming) for progressive UI rendering.