| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- export const metadata = { title: "OpenAPI Integration" }
- # OpenAPI Integration
- Use json-render to generate dynamic forms and UIs from [OpenAPI/Swagger](https://swagger.io/specification/) schemas.
- <div className="rounded-lg border border-amber-500/50 bg-amber-500/10 p-4 mb-8">
- <p className="text-sm text-amber-700 dark:text-amber-300">
- <strong>Concept:</strong> This page demonstrates how json-render can support OpenAPI schemas. The examples are illustrative and may require adaptation for production use.
- </p>
- </div>
- ## Why OpenAPI?
- OpenAPI specifications describe your API's endpoints, request bodies, and response schemas. By converting OpenAPI schemas to json-render specs, you can:
- - Automatically generate forms for API endpoints
- - Display API responses with type-aware rendering
- - Keep your UI in sync with your API schema
- - Let AI generate UIs that match your API contracts
- ## Example OpenAPI Schema
- A typical OpenAPI schema for a request body:
- ```json
- {
- "openapi": "3.0.0",
- "paths": {
- "/users": {
- "post": {
- "summary": "Create a new user",
- "operationId": "createUser",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateUserRequest"
- }
- }
- }
- }
- }
- }
- },
- "components": {
- "schemas": {
- "CreateUserRequest": {
- "type": "object",
- "required": ["email", "name"],
- "properties": {
- "name": {
- "type": "string",
- "description": "User's full name",
- "minLength": 1,
- "maxLength": 100
- },
- "email": {
- "type": "string",
- "format": "email",
- "description": "User's email address"
- },
- "age": {
- "type": "integer",
- "minimum": 0,
- "maximum": 150,
- "description": "User's age"
- },
- "role": {
- "type": "string",
- "enum": ["admin", "user", "guest"],
- "default": "user",
- "description": "User's role"
- },
- "preferences": {
- "type": "object",
- "properties": {
- "newsletter": {
- "type": "boolean",
- "default": false
- },
- "theme": {
- "type": "string",
- "enum": ["light", "dark", "system"]
- }
- }
- }
- }
- }
- }
- }
- }
- ```
- ## Define an OpenAPI-to-UI Catalog
- Create components that map to OpenAPI data types:
- ```typescript
- import { defineCatalog } from '@json-render/core';
- import { schema } from '@json-render/react';
- import { z } from 'zod';
- export const openapiCatalog = defineCatalog(schema, {
- components: {
- Form: {
- description: 'API form container',
- props: z.object({
- operationId: z.string(),
- endpoint: z.string(),
- method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
- title: z.string().optional(),
- description: z.string().optional(),
- }),
- },
- StringField: {
- description: 'String input field',
- props: z.object({
- name: z.string(),
- label: z.string(),
- description: z.string().optional(),
- required: z.boolean().optional(),
- format: z.enum(['text', 'email', 'uri', 'uuid', 'date', 'date-time', 'password']).optional(),
- minLength: z.number().optional(),
- maxLength: z.number().optional(),
- pattern: z.string().optional(),
- placeholder: z.string().optional(),
- defaultValue: z.string().optional(),
- }),
- },
- NumberField: {
- description: 'Number input field',
- props: z.object({
- name: z.string(),
- label: z.string(),
- description: z.string().optional(),
- required: z.boolean().optional(),
- type: z.enum(['integer', 'number']).optional(),
- minimum: z.number().optional(),
- maximum: z.number().optional(),
- defaultValue: z.number().optional(),
- }),
- },
- BooleanField: {
- description: 'Boolean toggle field',
- props: z.object({
- name: z.string(),
- label: z.string(),
- description: z.string().optional(),
- defaultValue: z.boolean().optional(),
- }),
- },
- EnumField: {
- description: 'Enum selection field',
- props: z.object({
- name: z.string(),
- label: z.string(),
- description: z.string().optional(),
- required: z.boolean().optional(),
- options: z.array(z.object({
- value: z.string(),
- label: z.string().optional(),
- })),
- defaultValue: z.string().optional(),
- }),
- },
- ObjectField: {
- description: 'Nested object group',
- props: z.object({
- name: z.string(),
- label: z.string(),
- description: z.string().optional(),
- collapsible: z.boolean().optional(),
- }),
- },
- },
- actions: {
- submit: {
- description: 'Submit form to API endpoint',
- params: z.object({ operationId: z.string() }),
- },
- reset: {
- description: 'Reset form to defaults',
- params: z.object({}),
- },
- },
- });
- ```
- ## Convert OpenAPI Schema to Spec
- 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.
- ## Usage Example
- ```tsx
- 'use client';
- import { OpenAPIForm } from './openapi-form';
- import { operationToSpec } from './openapi-to-spec';
- // Your OpenAPI schema (typically loaded from your API)
- const createUserSchema = {
- type: 'object',
- required: ['email', 'name'],
- properties: {
- name: { type: 'string', description: "User's full name" },
- email: { type: 'string', format: 'email', description: "User's email" },
- age: { type: 'integer', minimum: 0, maximum: 150 },
- role: { type: 'string', enum: ['admin', 'user', 'guest'], default: 'user' },
- },
- };
- // Convert to spec
- const spec = operationToSpec(
- 'createUser',
- 'POST',
- '/api/users',
- createUserSchema,
- 'Create User',
- 'Add a new user to the system',
- );
- export function CreateUserForm() {
- const handleSubmit = async (data: Record<string, unknown>) => {
- const response = await fetch('/api/users', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(data),
- });
- if (response.ok) {
- console.log('User created!');
- }
- };
- return <OpenAPIForm spec={spec} onSubmit={handleSubmit} />;
- }
- ```
- ## Auto-generating from OpenAPI Document
- Load and parse an OpenAPI document to generate forms for all operations:
- ```typescript
- import SwaggerParser from '@apidevtools/swagger-parser';
- import { operationToSpec } from './openapi-to-spec';
- export async function loadOpenAPISpecs(specUrl: string) {
- const api = await SwaggerParser.dereference(specUrl);
- const specs: Record<string, any> = {};
- for (const [path, methods] of Object.entries(api.paths)) {
- for (const [method, operation] of Object.entries(methods)) {
- if (!operation.requestBody?.content?.['application/json']?.schema) continue;
- const schema = operation.requestBody.content['application/json'].schema;
- const operationId = operation.operationId || `${method}_${path.replace(/\//g, '_')}`;
- specs[operationId] = operationToSpec(
- operationId,
- method,
- path,
- schema,
- operation.summary,
- operation.description,
- );
- }
- }
- return specs;
- }
- // Usage
- const specs = await loadOpenAPISpecs('https://api.example.com/openapi.json');
- // specs.createUser, specs.updateUser, etc.
- ```
- ## Next
- Learn about [streaming](/docs/streaming) for progressive UI rendering.
|