DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  • From Zero to Meme Hero: How I Built an AI-Powered Meme Generator in React
  • Migrating from React Router v5 to v6: A Comprehensive Guide
  • How to Build an OpenAI Custom GPT With a Third-Party API

Trending

  • Product-Led Software Delivery: Intelligent Platforms for DevOps at Scale
  • Genkit Middleware: Intercept, Extend, and Harden your Gen AI Pipelines
  • 5 Layers of Prompt Injection Defense You Can Wire Into Any Node.js App
  • LLM Integration in Enterprise Applications: A Practical Guide
  1. DZone
  2. Data Engineering
  3. Databases
  4. Building a Unified API Documentation Portal with React, Redoc, and Automatic RAML-to-OpenAPI Conversion

Building a Unified API Documentation Portal with React, Redoc, and Automatic RAML-to-OpenAPI Conversion

Learn how to build a modern static API documentation portal that supports both OpenAPI 3.x and RAML 1.0 specifications with automatic conversion.

By 
Sreedhar Pamidiparthi user avatar
Sreedhar Pamidiparthi
·
Mar. 11, 26 · Tutorial
Likes (0)
Comment
Save
Tweet
Share
5.4K Views

Join the DZone community and get the full member experience.

Join For Free

In today’s microservices-driven world (even with the evolution of AI), organizations often maintain dozens or even hundreds of APIs that are critical to building many software applications. These APIs may use different specification formats: some teams prefer OpenAPI 3.x for its widespread tooling support, whereas others maintain legacy RAML specifications that still power critical services.

The challenge? Providing a unified, professional documentation experience without requiring teams to manually convert their specifications or maintain multiple documentation systems.

In this article, I will walk you through building an API Documentation Portal that:

  • Renders both OpenAPI 3.x and RAML 1.0 specifications
  • Automatically converts RAML to OpenAPI at build time
  • Provides beautiful, interactive documentation using Redoc
  • Deploys as a static site to any CDN or cloud storage

Let’s dive in.

The Technology Stack

Before we start coding, here is what we are working with:

Technology

Purpose

React 18

UI framework

TypeScript

Type safety

Vite

Lightning-fast build tool

Redoc

API documentation rendering

Tailwind CSS

Styling

webapi-parser

RAML-to-OpenAPI conversion


Why this stack? React and Vite provide a modern development experience with hot module replacement. Redoc provides a polished three-panel documentation layout that developers love. The webapi-parser handles the heavy lifting of format conversion.

Project Architecture

The portal follows a simple but effective architecture.

Browser

 

Sidebar

Category A
  • API 1
  • API 2
Category B
  • API 3

→

Redoc Viewer (Main Content)

• API Info
• Endpoints
• Request/Response Schemas
• Code Samples


The key insight is that RAML files are converted to the OpenAPI format at build time rather than at runtime. This means:

  • No server-side processing is required
  • Faster page loads
  • Simpler deployment (pure static files)

Setting Up the Project

Step 1: Initialize the Project

Shell
 
npm create vite@latest apidoc -- --template react-ts
cd apidoc
npm install


Step 2: Install Dependencies

Core dependencies:

Shell
 
npm install react-router-dom redoc mobx styled-components 


Dev dependencies:

Shell
 
npm install -D tailwindcss postcss autoprefixer webapi-parser
npx tailwindcss init -p


Step 3: Define the API Configuration Type

Create a type definition for the API registry:

// src/types/api.ts

TypeScript
 
export interface ApiSpec {
  id: string;
  name: string;
  version: string;
  category: string;
  specPath: string;
  type: 'openapi' | 'raml';
  description?: string;
}

export interface ApisConfig {
  apis: ApiSpec[];
}


This interface enforces consistency across all API entries and enables TypeScript to detect configuration errors at compile time.

Building the Core Components

The API Configuration

Store your API registry in a JSON file that is easy to update:

// src/config/apis.json

JSON
 
{
  "apis": [
    {
      "id": "petstore-api",
      "name": "Petstore API",
      "version": "1.0.0",
      "category": "Sample APIs",
      "specPath": "/specs/petstore.yaml",
      "type": "openapi",
      "description": "Sample OpenAPI 3.0 specification"
    },
    {
      "id": "users-api",
      "name": "Users API",
      "version": "1.0.0",
      "category": "Sample APIs",
      "specPath": "/specs/users.raml",
      "type": "raml",
      "description": "Sample RAML 1.0 specification"
    }
  ]
}


The Redoc Wrapper Component

Here is where the magic happens. The ApiDoc component wraps Redoc and handles the RAML-to-OpenAPI path transformation.

// src/components/ApiDoc.tsx 

TypeScript
 
import { RedocStandalone } from 'redoc';
import { ApiSpec } from '../types/api';

interface ApiDocProps {
  api: ApiSpec;
}

export function ApiDoc({ api }: ApiDocProps) {
  // For RAML files, use the converted OpenAPI version
  const specUrl = api.type === 'raml'
    ? api.specPath.replace(/\.raml$/, '.converted.yaml')
    : api.specPath;

  return (
    <div className="h-full overflow-auto">
      <RedocStandalone
        specUrl={specUrl}
        options={{
          scrollYOffset: 0,
          hideDownloadButton: false,
          pathInMiddlePanel: true,
          theme: {
            colors: {
              primary: { main: '#3b82f6' },
            },
            sidebar: {
              backgroundColor: '#1f2937',
              textColor: '#f3f4f6',
            },
          },
        }}
      />
    </div>
  );
}


The key line is the specUrl calculation: if the API type is raml, we swap the .raml extension for .converted.yaml. This assumes that our build process has already generated the converted file.

The Navigation Sidebar

A categorized sidebar makes it easy to navigate between APIs:

// src/components/Sidebar.tsx

TypeScript
 
import { NavLink } from 'react-router-dom';
import { ApiSpec } from '../types/api';

interface SidebarProps {
  apis: ApiSpec[];
  collapsed: boolean;
  onToggle: () => void;
}

export function Sidebar({ apis, collapsed, onToggle }: SidebarProps) {
  // Group APIs by category
  const groupedApis = apis.reduce((acc, api) => {
    if (!acc[api.category]) {
      acc[api.category] = [];
    }
    acc[api.category].push(api);
    return acc;
  }, {} as Record<string, ApiSpec[]>);

  return (
    <aside className={`bg-gray-900 text-white ${collapsed ? 'w-16' : 'w-64'}`}>
      <div className="p-4 border-b border-gray-700">
        {!collapsed && <h1 className="text-xl font-bold">API Docs</h1>}
      </div>
      <nav className="p-2">
        {Object.entries(groupedApis).map(([category, categoryApis]) => (
          <div key={category} className="mb-4">
            {!collapsed && (
              <h2 className="px-3 py-2 text-xs font-semibold text-gray-400 uppercase">
                {category}
              </h2>
            )}
            <ul>
              {categoryApis.map((api) => (
                <li key={api.id}>
                  <NavLink
                    to={`/docs/${api.id}`}
                    className={({ isActive }) =>
                      `block px-3 py-2 rounded-md text-sm ${
                        isActive ? 'bg-blue-600' : 'hover:bg-gray-800'
                      }`
                    }
                  >
                    <div className="font-medium">{api.name}</div>
                    <div className="text-xs text-gray-400">
                      v{api.version} • {api.type.toUpperCase()}
                    </div>
                  </NavLink>
                </li>
              ))}
            </ul>
          </div>
        ))}
      </nav>
    </aside>
  );
}


The RAML Conversion Script

This is the secret sauce that enables RAML support. The script runs at build time and converts all RAML files to OpenAPI 3.0.

// scripts/convert-raml.js

JavaScript
 
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import wap from 'webapi-parser';

const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = join(__dirname, '..');

// Read the APIs config
const configPath = join(rootDir, 'src/config/apis.json');
const config = JSON.parse(readFileSync(configPath, 'utf-8'));

// Find RAML APIs
const ramlApis = config.apis.filter(api => api.type === 'raml');

async function convertRamlFiles() {
  const { WebApiParser } = wap;

  for (const api of ramlApis) {
    const ramlPath = join(rootDir, 'public', api.specPath);
    const outputPath = ramlPath.replace(/\.raml$/, '.converted.yaml');

    if (!existsSync(ramlPath)) {
      console.warn(`RAML file not found: ${ramlPath}`);
      continue;
    }

    console.log(`Converting ${api.name}...`);

    try {
      const ramlContent = readFileSync(ramlPath, 'utf-8');

      // Parse RAML 1.0
      const model = await WebApiParser.raml10.parse(ramlContent);

      // Resolve all references
      const resolved = await WebApiParser.raml10.resolve(model);

      // Generate OpenAPI 3.0
      const oas30 = await WebApiParser.oas30.generateString(resolved);

      writeFileSync(outputPath, oas30);
      console.log(`  ✓ Converted to ${outputPath}`);
    } catch (error) {
      console.error(`  ✗ Error: ${error.message}`);
    }
  }
}

convertRamlFiles();


Wire it into your build process:

// package.json

JSON
 
{
  "scripts": {
    "convert-raml": "node scripts/convert-raml.js",
    "dev": "npm run convert-raml && vite",
    "build": "npm run convert-raml && tsc -b && vite build"
  }
}


Now, every time you run npm run dev or npm run build, the RAML files are converted automatically.

Deployment with GitHub Actions

To automate deployment to AWS S3, a GitHub Actions workflow can be created as follows (optional):

# .github/workflows/deploy.yml

YAML
 
name: Build and Deploy

on:
  push:
    branches: [main, master]
  pull_request:
    branches: [main, master]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build project
        run: npm run build

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ vars.AWS_REGION }}

      - name: Deploy to S3
        run: aws s3 sync dist/ s3://${{ vars.S3_BUCKET }} --delete


Adding a New API

Once the portal is set up, adding a new API is straightforward:

  1. Drop your spec file into public/specs/
  2. Add an entry to src/config/apis.json
  3. Commit and push — the CI/CD pipeline handles everything else
JSON
 
{
  "id": "my-new-api",
  "name": "My New API",
  "version": "2.0.0",
  "category": "Production",
  "specPath": "/specs/my-new-api.yaml",
  "type": "openapi",
  "description": "My awesome new API"
}


Final Outcome

Once the application is deployed and hosted as a static site, the final outcome looks as shown below.

Final Outcome


Key Takeaways

  • Unified Experience: Users see a consistent documentation interface regardless of whether the underlying specification is OpenAPI or RAML.
  • Build-Time Conversion: Converting RAML to OpenAPI at build time eliminates runtime complexity and enables static hosting.
  • Configuration-Driven: Adding new APIs requires only a JSON entry and a spec file — no code changes.
  • Static Deployment: The entire portal is static HTML/JS/CSS, making it perfect for S3, CloudFront, Netlify, or any other static host.
  • Developer Experience: Vite’s hot module replacement and TypeScript’s type checking make development fast and safe.

Conclusion

Building a unified API documentation portal does not have to be complicated. By combining React, Redoc, and automatic RAML conversion, you can deliver a professional documentation experience that scales with your organization’s API ecosystem.

The complete source code is available under the MIT License. You can fork it, customize the theme, and make it your own.

What challenges have you faced regarding API documentation? Feel free to share your thoughts in the comments section.

References

Official Documentation & Specifications

  1. OpenAPI Specification v3.2.0
    https://spec.openapis.org/oas/v3.2.0.html
  2. OpenAPI Initiative
    https://www.openapis.org/
  3. OpenAPI Specification GitHub Repository
    https://github.com/OAI/OpenAPI-Specification
  4. RAML Official Website
    https://raml.org/
  5. RAML 1.0 Specification
    https://github.com/raml-org/raml-spec/blob/master/versions/raml-10/raml-10.md
  6. Redoc GitHub Repository
    https://github.com/Redocly/redoc
  7. Redoc Official Documentation
     https://redocly.com/docs/redoc
  8. React Official Documentation
    https://react.dev/
  9. Vite Official Documentation
    https://vite.dev/
  10. Tailwind CSS Documentation
    https://tailwindcss.com/docs
  11. GitHub Actions Documentation
    https://docs.github.com/en/actions
  12. webapi-parser (npm)
    https://www.npmjs.com/package/webapi-parser

Academic & Research Papers

  1. Meng, M., Steinhardt, S., & Schubert, A. (2019). "How Developers Use API Documentation: An Observation Study." Communication Design Quarterly, Vol 7, No. 2. ACM SIGDOC.
     https://dl.acm.org/doi/10.1145/3358931.3358937
  2. Henkel, M., & Keshishzadeh, S. (2020). "Optimizing API Documentation." ACM SIGDOC Annual International Conference on Design of Communication.
     https://dl.acm.org/doi/fullHtml/10.1145/3380851.3416759
  3. IEEE/ACM ICSE (2024). "Managing API Evolution in Microservice Architecture." 46th International Conference on Software Engineering.
     https://dl.acm.org/doi/10.1145/3639478.3639800
  4. Journal of Systems and Software (2024). "Microservice API Evolution in Practice: A Study on Strategies and Challenges." Elsevier.
     https://www.sciencedirect.com/science/article/pii/S0164121224001559
  5. Di Francesco, P., Malavolta, I., & Lago, P. (2021). "On Microservice Analysis and Architecture Evolution: A Systematic Mapping Study." MDPI Applied Sciences.
     https://www.mdpi.com/2076-3417/11/17/7856
  6. Heinrich, R., et al. (2017). "Performance Engineering for Microservices: Research Challenges and Directions." ACM/SPEC International Conference on Performance Engineering.
     https://dl.acm.org/doi/10.1145/3053600.3053653

Industry Reports & Best Practices

  1. SmartBear. "State of Software Quality - API Report 2023."
     https://smartbear.com/state-of-software-quality/api/
  2. Postman. "API Documentation: How to Write, Examples & Best Practices."
     https://www.postman.com/api-platform/api-documentation/
  3. Swagger/SmartBear. "API Documentation: The Secret to a Great API Developer Experience."
     https://swagger.io/resources/ebooks/api-documentation-the-secret-to-a-great-api-developer-experience/
  4. Pronovix. "Developer Experience Best Practices - API The Docs 2023."
     https://pronovix.com/articles/developer-experience-best-practices-api-docs-2023

Key Statistics

  • 64% of developers express frustration when faced with poor API documentation resources
  • Documentation with interactive components reduces support queries by up to 30%
  • Developers experienced a 40% decrease in onboarding time following documentation improvements
  • Support requests fell by an average of 35% with improved documentation
  • Satisfaction scores improved from 60% to over 85% after documentation enhancements
API AWS Documentation HTTPS JavaScript OpenAPI Specification Build (game engine) React (JavaScript library) Data Types developer experience

Opinions expressed by DZone contributors are their own.

Related

  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  • From Zero to Meme Hero: How I Built an AI-Powered Meme Generator in React
  • Migrating from React Router v5 to v6: A Comprehensive Guide
  • How to Build an OpenAI Custom GPT With a Third-Party API

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook