Free & open source — no account required
Free & open source — no account required
This technical guide provides an in-depth analysis of the json to openapi definition engine, best practices for implementation, and data security standards.
A JSON-to-OpenAPI converter generates the schema layer, but a production-quality OpenAPI definition requires more: proper $ref organization so schemas are reusable across operations, links to model hypermedia relationships between responses, callbacks for webhook-style APIs, a multi-environment server object, and Prism mock server setup so frontend teams can develop against the API before the backend is ready. This guide covers the full design-first workflow from JSON samples to a complete, lintable, mock-servable API contract.
# Complete OpenAPI 3.1 definition structure
openapi: 3.1.0
info:
title: Product Catalog API
description: REST API for managing products and categories
version: 2.1.0
contact:
name: API Support
email: api@example.com
license:
name: MIT
servers:
- url: https://api.example.com/v2
description: Production
- url: https://staging.api.example.com/v2
description: Staging
- url: http://localhost:3000/v2
description: Local development
tags:
- name: Products
description: Product CRUD and search
- name: Categories
description: Category hierarchy management
paths:
/products:
get:
operationId: listProducts
summary: List products with pagination
tags: [Products]
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/PageSizeParam'
- name: category_id
in: query
schema: { type: string }
- name: in_stock
in: query
schema: { type: boolean }
responses:
'200':
description: Product list with pagination metadata
content:
application/json:
schema: { $ref: '#/components/schemas/ProductListResponse' }
links:
GetProductById:
$ref: '#/components/links/GetProductById'
/products/{id}:
parameters:
- $ref: '#/components/parameters/IdParam'
get:
operationId: getProductById
summary: Get a single product
tags: [Products]
responses:
'200':
description: Product details
content:
application/json:
schema: { $ref: '#/components/schemas/Product' }
'404':
$ref: '#/components/responses/NotFound'
# components/ holds all reusable definitions
components:
# ── Schemas ───────────────────────────────────────────────────
schemas:
Product:
type: object
required: [id, name, price_cents, slug, in_stock]
properties:
id: { type: string, format: uuid, readOnly: true }
name: { type: string, minLength: 1, maxLength: 200 }
slug: { type: string, pattern: '^[a-z0-9-]+$', readOnly: true }
price_cents:
type: integer
minimum: 0
description: Price in smallest currency unit (cents)
description:
type: ['string', 'null']
maxLength: 2000
category_id: { type: string, format: uuid }
in_stock: { type: boolean }
created_at: { type: string, format: date-time, readOnly: true }
ProductListResponse:
type: object
required: [data, pagination]
properties:
data: { type: array, items: { $ref: '#/components/schemas/Product' } }
pagination: { $ref: '#/components/schemas/Pagination' }
Pagination:
type: object
required: [page, page_size, total, has_more]
properties:
page: { type: integer, minimum: 1 }
page_size: { type: integer, minimum: 1, maximum: 100 }
total: { type: integer, minimum: 0 }
has_more: { type: boolean }
ProblemDetail:
description: RFC 7807 Problem Details
type: object
required: [type, title, status]
properties:
type: { type: string, format: uri }
title: { type: string }
status: { type: integer }
detail: { type: string }
instance: { type: string, format: uri }
# ── Parameters ────────────────────────────────────────────────
parameters:
IdParam:
name: id
in: path
required: true
schema: { type: string, format: uuid }
PageParam:
name: page
in: query
schema: { type: integer, minimum: 1, default: 1 }
PageSizeParam:
name: page_size
in: query
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
# ── Responses ─────────────────────────────────────────────────
responses:
NotFound:
description: Resource not found
content:
application/json:
schema: { $ref: '#/components/schemas/ProblemDetail' }
Unauthorized:
description: Authentication required
headers:
WWW-Authenticate: { schema: { type: string } }
content:
application/json:
schema: { $ref: '#/components/schemas/ProblemDetail' }
# ── Links ─────────────────────────────────────────────────────
links:
GetProductById:
operationId: getProductById
parameters:
id: '$response.body#/data/0/id'
description: Get the first product from the list response
# Callbacks document the shape of webhooks your API sends to client URLs
paths:
/subscriptions:
post:
operationId: createSubscription
summary: Subscribe to product events
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [webhook_url, events]
properties:
webhook_url: { type: string, format: uri }
events:
type: array
items:
type: string
enum: [product.created, product.updated, product.deleted]
responses:
'201':
description: Subscription created
callbacks:
ProductEvent:
'{$request.body#/webhook_url}':
post:
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [event, product]
properties:
event: { type: string }
product: { $ref: '#/components/schemas/Product' }
responses:
'200':
description: Client acknowledged the webhook
// Prism generates a mock server from your OpenAPI spec
// npm install -D @stoplight/prism-cli
// Run mock server on port 4010
// npx prism mock openapi.yaml --port 4010
// Dynamic vs static mock behavior:
// Static examples in spec → Prism returns them directly
// No examples → Prism generates random data matching the schema
// Add examples to make mocks realistic:
components:
schemas:
Product:
example:
id: "prod_550e8400-e29b-41d4-a716-446655440000"
name: "Wireless Mouse"
slug: "wireless-mouse"
price_cents: 2999
in_stock: true
created_at: "2024-01-15T08:00:00Z"
// Frontend can now develop against the mock:
const api = new ApiClient('http://localhost:4010');
const products = await api.get('/products'); // returns mock data from spec
// Prism also validates requests against the spec:
// POST /products with missing required fields → 422 response with validation details
// This catches integration bugs before the backend is built
// Configure Prism for CORS (needed for browser-based frontend):
// npx prism mock openapi.yaml --port 4010 --cors
{ type, title, status, detail, instance } structure. It's supported by most frameworks (Spring, FastAPI, ASP.NET Core), parseable by OpenAPI generators, and familiar to API consumers.$ref for everything reused more than once: Inline schemas are fine for one-off shapes, but any schema used in more than one place (even in just two operations) should be in components/schemas. This pays off when generating TypeScript clients — inline schemas produce anonymous types, while $ref schemas produce named interfaces.info.version is the API version for humans; put the actual path version (/v2) in the servers array URL. Clients use the server URL for requests; they use info.version for display and change tracking only.Q: Design-first vs code-first — which approach is better?
A: Design-first (write spec → generate stubs → implement) produces cleaner, more intentional APIs because you make naming and structure decisions before implementation constraints influence them. Code-first (implement → generate spec) is easier to keep in sync but often exposes internal implementation details. For public APIs or APIs shared across teams, design-first is worth the discipline. For internal microservices, code-first with strict linting is pragmatic.
Q: How do I keep the OpenAPI spec in sync with the implementation?
A: Use contract testing. Generate the spec from code (code-first), or test the implementation against the spec (design-first) using tools like Dredd or schemathesis. Schemathesis generates test cases from your spec and runs them against the real server — it catches response shape mismatches, missing required fields, and invalid status codes automatically.
Q: Can I split a large OpenAPI spec across multiple files?
A: Yes, using $ref with relative file paths: $ref: './schemas/product.yaml'. Tools like Redocly CLI can bundle multiple files into a single spec for deployment. This keeps the spec maintainable — one file per resource is a common pattern for specs with 50+ endpoints.
Is the processing local-only?
Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.
Can I use this for enterprise projects?
Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.