Back to "LLMApi: OpenAPI Dynamic Mock Generator: Charger n'importe quel spectreM SK2 Mock Any API"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

AI AI-Article API ASP.NET Core LLM LLMApi mockllmapi Nuget SignalR

LLMApi: OpenAPI Dynamic Mock Generator: Charger n'importe quel spectreM SK2 Mock Any API

Wednesday, 05 November 2025

NOTE: Cet article est principalement AI généré dans le cadre de mon paquet nuget en tant que documentation de diffusion.

Introduction

Avez-vous déjà besoin de tester contre une API qui n’était pas encore prête' ? Ou avez-vous voulu développer en mode offline sans dépasser les limites de vitesse? Le Generator Mock Dynamic OpenAPI vous permet de charger toute spécification OpenAPI et crée instantanément un API mock fonctionnel intégralement structuré avec des données réalistes?

Aucun fichier de configuration. Aucune création manuelle d'un point final. Just pointer sur une spécification OpenAPI et commencer à faire des demandesM SK2

NuGet NuGet

Vous pouvez trouver le GitHub ici pour le projet, tous les domaines publics, etc.

Gestionnaire OpenApi

Le problème: Développement des blocs de dépendance API

Les applications modernes dépendent de dizaines d’API externes. Au cours du développementM SK1 vous êtes confrontés à plusieurs défis :

graph TD
    A[Your App] --> B{External API}
    B -->|Not Ready| C[Development Blocked]
    B -->|Rate Limited| D[Can't Test Freely]
    B -->|Requires Auth| E[Complex Setup]
    B -->|Expensive| F[Cost Concerns]
    B -->|Unreliable| G[Flaky Tests]

Solutions traditionnelles impliquent:

  • Écrire manuellement les réponses de simulation (nuisible, devient outdatéM SK2
  • Enregistrement/replaying HTTP traffic (brittle, difficile à entretenir
  • Utilisation d'installations codées (non réalistes , ne couvre pas les cases de bordsM SK2

La solution: Mocking dynamique OpenAPI

Pointez le système à n’importe quel spec OpenAPI, et il apparaîtra automatiquement:

  1. Parse la spécification
  2. Discovers all endpoints
  3. génère des données de simulation réalistes à l'aide d'un LLM
  4. Serve l'API de modèle sur votre machine locale
sequenceDiagram
    participant Dev as Developer
    participant System as Mock System
    participant Spec as OpenAPI Spec
    participant LLM as Local LLM

    Dev->>System: Load spec from URL/file
    System->>Spec: Parse OpenAPI document
    Spec-->>System: Endpoints, schemas, descriptions
    System->>System: Register dynamic routes

    Dev->>System: GET /petstore/pet/123
    System->>LLM: Generate data for "Pet" schema
    LLM-->>System: Realistic pet data
    System-->>Dev: {"id": 123, "name": "Max", ...}

Comment cela fonctionne

Aperçu de l'architecture

Le système OpenAPI se compose de plusieurs composants coordonnés:

graph TB
    A[HTTP Request] --> B{Route Matches?}
    B -->|No| C[404 Not Found]
    B -->|Yes| D[DynamicOpenApiManager]
    D --> E[Find Matching Endpoint]
    E --> F[OpenApiRequestHandler]
    F --> G[Extract Schema from Spec]
    G --> H[Build LLM Prompt]
    H --> I[PromptBuilder]
    I --> J[Include Context?]
    J -->|Yes| K[OpenApiContextManager]
    J -->|No| L[LLM Client]
    K --> L
    L --> M[Get Response]
    M --> N[JsonExtractor]
    N --> O[Return Mock Data]

Components clés:

  1. DynamicOpenApiManager - Gestion des spécifications chargées et du parallèle de la route
  2. OpenApiSpecLoader - Extraire et analyser les documents OpenAPI
  3. OpenApiRequestHandler - Génie des réponses pour les points de fin correspondants
  4. PromptBuilder - Crée des prompts LLM à partir de schémas OpenAPI
  5. OpenApiContextManager - Maintien de la cohérence entre les appels optionnel

Chargement des spécifications

Les spécifications peuvent être chargées de trois sources:

1. URL à distance

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "petstore",
  "source": "https://petstore3.swagger.io/api/v3/openapi.json",
  "basePath": "/petstore"
}

2. fichier local

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "my-api",
  "source": "./specs/my-api.yaml",
  "basePath": "/api/v1"
}

3. URL de données

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "inline-api",
  "source": "data:application/json;base64,eyJvcGVuYXBpIjoiMy...",
  "basePath": "/api"
}

Préciser le processus de chargement

Voici ce qui se passe lorsque vous chargez un spec:

public async Task<SpecLoadResult> LoadSpecAsync(
    string name,
    string source,
    string? basePath = null,
    string? contextName = null)
{
    // 1. Use scoped service factory for OpenApiSpecLoader
    using var scope = _scopeFactory.CreateScope();
    var specLoader = scope.ServiceProvider
        .GetRequiredService<OpenApiSpecLoader>();

    // 2. Load the OpenAPI document
    var document = await specLoader.LoadSpecAsync(source);

    // 3. Determine base path from spec or parameter
    var effectiveBasePath = basePath
        ?? document.Servers?.FirstOrDefault()?.Url
        ?? "/api";

    // 4. Store spec with configuration
    var config = new OpenApiSpecConfig
    {
        Name = name,
        Source = source,
        Document = document,
        BasePath = effectiveBasePath,
        ContextName = contextName,
        LoadedAt = DateTimeOffset.UtcNow
    };

    _specs.AddOrUpdate(name, config, (_, __) => config);

    // 5. Notify listeners via SignalR
    await NotifySpecLoaded(name, effectiveBasePath);

    return new SpecLoadResult
    {
        Name = name,
        BasePath = effectiveBasePath,
        EndpointCount = CountEndpoints(document),
        Success = true
    };
}

Correspondance dynamique des routes

Lorsque une demande arrive,, le système la compare à toutes les spécifications chargées:

public OpenApiEndpointMatch? FindMatchingEndpoint(string path, string method)
{
    // Try each loaded spec
    foreach (var spec in _specs.Values)
    {
        // Remove base path prefix
        var relativePath = path;
        if (path.StartsWith(spec.BasePath))
        {
            relativePath = path.Substring(spec.BasePath.Length);
        }

        // Find matching path in OpenAPI document
        var (pathTemplate, operation) = FindOperation(
            spec.Document,
            relativePath,
            method);

        if (operation != null)
        {
            return new OpenApiEndpointMatch
            {
                Spec = spec,
                PathTemplate = pathTemplate,
                Operation = operation,
                Method = ParseMethod(method)
            };
        }
    }

    return null;
}

Traitement des demandes

Une fois qu'un point final correspondant est trouvé,, le gestionnaire génère une réponse:

public async Task<string> HandleRequestAsync(
    HttpContext context,
    OpenApiDocument document,
    string path,
    OperationType method,
    OpenApiOperation operation,
    string? contextName = null,
    CancellationToken cancellationToken = default)
{
    // 1. Extract request body
    var requestBody = await ReadRequestBodyAsync(context.Request);

    // 2. Get success response schema
    var shape = ExtractResponseSchema(operation);

    // 3. Get context history if using contexts
    var contextHistory = !string.IsNullOrWhiteSpace(contextName)
        ? _contextManager.GetContextForPrompt(contextName)
        : null;

    // 4. Build prompt from OpenAPI metadata
    var description = operation.Summary ?? operation.Description;
    var prompt = _promptBuilder.BuildPrompt(
        method.ToString(),
        path,
        requestBody,
        new ShapeInfo { Shape = shape },
        streaming: false,
        description: description,
        contextHistory: contextHistory);

    // 5. Get response from LLM
    var rawResponse = await _llmClient.GetCompletionAsync(
        prompt,
        cancellationToken);

    // 6. Extract clean JSON
    var jsonResponse = JsonExtractor.ExtractJson(rawResponse);

    // 7. Store in context if configured
    if (!string.IsNullOrWhiteSpace(contextName))
    {
        _contextManager.AddToContext(
            contextName,
            method.ToString(),
            path,
            requestBody,
            jsonResponse);
    }

    return jsonResponse;
}

Extraction du schéma

Le système extrait les schémas de réponse des définitions OpenAPI:

private string? ExtractResponseSchema(OpenApiOperation operation)
{
    // Look for successful response (2xx)
    var successResponse = operation.Responses
        .FirstOrDefault(r => r.Key.StartsWith("2"))
        .Value;

    if (successResponse == null)
        return null;

    // Get JSON content
    var jsonContent = successResponse.Content
        .FirstOrDefault(c => c.Key.Contains("json"))
        .Value;

    if (jsonContent?.Schema == null)
        return null;

    // Convert OpenAPI schema to JSON Schema
    return ConvertToJsonSchema(jsonContent.Schema);
}

private string ConvertToJsonSchema(OpenApiSchema schema)
{
    // Recursively build JSON Schema representation
    var builder = new StringBuilder();
    builder.Append("{");

    if (schema.Type != null)
    {
        builder.Append($"\"type\":\"{schema.Type}\"");
    }

    if (schema.Properties?.Count > 0)
    {
        builder.Append(",\"properties\":{");
        var props = schema.Properties
            .Select(p => $"\"{p.Key}\":{ConvertToJsonSchema(p.Value)}");
        builder.Append(string.Join(",", props));
        builder.Append("}");
    }

    if (schema.Items != null)
    {
        builder.Append(",\"items\":");
        builder.Append(ConvertToJsonSchema(schema.Items));
    }

    builder.Append("}");
    return builder.ToString();
}

Utilisation mondiale

Exemple: Petstore API

Laissez's marcher en ridiculisant l’API classique de Petstore:

Étape 1: Charger le spec

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "petstore",
  "source": "https://petstore3.swagger.io/api/v3/openapi.json",
  "basePath": "/petstore"
}

Réponse:

{
  "name": "petstore",
  "basePath": "/petstore",
  "endpointCount": 19,
  "endpoints": [
    {"path": "/petstore/pet", "method": "POST"},
    {"path": "/petstore/pet/{petId}", "method": "GET"},
    {"path": "/petstore/pet/findByStatus", "method": "GET"},
    ...
  ],
  "success": true
}

Étape 2: Utiliser les points de fin Mock

Maintenant, tous les points 19 sont disponibles

### Get a pet by ID
GET /petstore/pet/123

### Response (auto-generated):
{
  "id": 123,
  "name": "Max",
  "category": {
    "id": 1,
    "name": "Dogs"
  },
  "photoUrls": [
    "https://example.com/max1.jpg"
  ],
  "tags": [
    {"id": 1, "name": "friendly"},
    {"id": 2, "name": "trained"}
  ],
  "status": "available"
}
### Find pets by status
GET /petstore/pet/findByStatus?status=available

### Response (auto-generated array):
[
  {
    "id": 42,
    "name": "Buddy",
    "status": "available",
    ...
  },
  {
    "id": 43,
    "name": "Luna",
    "status": "available",
    ...
  }
]

Étape 3: Inspecter le spec

GET /api/openapi/specs/petstore

### Shows full details:
### - All endpoints
### - Load time
### - Context configuration
### - Base path

Étape 4: Recharger si la spécification change

POST /api/openapi/specs/petstore/reload

Étape 5: Retirer lorsque cela est fait

DELETE /api/openapi/specs/petstore

Plusieurs espèces simultanément

Vous pouvez charger plusieurs spécifications à la fois, chacune avec son propre chemin de base:

### Load Petstore at /petstore
POST /api/openapi/specs
{"name": "petstore", "source": "...", "basePath": "/petstore"}

### Load GitHub API at /github
POST /api/openapi/specs
{"name": "github", "source": "...", "basePath": "/github"}

### Load Stripe API at /stripe
POST /api/openapi/specs
{"name": "stripe", "source": "...", "basePath": "/stripe"}

### All three APIs now available simultaneously:
GET /petstore/pet/123
GET /github/users/octocat
GET /stripe/customers/cus_123

Specs avec contexte

Pour plus de réalisme, assigner un contexte à une spécification.

POST /api/openapi/specs
Content-Type: application/json

{
  "name": "petstore",
  "source": "https://petstore3.swagger.io/api/v3/openapi.json",
  "basePath": "/petstore",
  "contextName": "petstore-session"
}

Maintenant, tous les points finals de petstore partagent le même contexte:

### Create a pet
POST /petstore/pet
{"name": "Max", "status": "available"}

### Response: {"id": 42, "name": "Max", "status": "available"}

### Get the pet (will reference same ID and name)
GET /petstore/pet/42

### Response: {"id": 42, "name": "Max", "status": "available"}
### Notice: Consistent ID and name from context

Point final d'essai

Le point final de l'essai vous permet d'essayer un point final sans faire une demande réelle:

POST /api/openapi/test
Content-Type: application/json

{
  "specName": "petstore",
  "path": "/pet/123",
  "method": "GET"
}

### Returns mock response without affecting routes

Ceci est utile pour:

  • Prévision des réponses avant l'intégration
  • Essai de points finals spécifiques en isolement
  • Débugging des problèmes liés au schéma

L'interface de gestion

Pour la gestion visuelle, visite http://localhost:5116/OpenApi:

graph TD
    A[OpenAPI Manager UI] --> B[Load Spec Section]
    A --> C[Spec List]
    A --> D[Context Viewer]

    B --> E[URL Input]
    B --> F[JSON Input]
    B --> G[Context Configuration]

    C --> H[Spec Card]
    H --> I[Reload Button]
    H --> J[Delete Button]
    H --> K[View Endpoints]

    D --> L[Active Contexts]
    L --> M[Context Details]
    L --> N[Clear Context]

Caractéristiques:

  • Drag-andM SK1drop téléchargement de fichiers spéciaux
  • La découverte d'un point final en direct - Voir tous les points de fin instantanément
  • Un-essai de cliquer - Tester tout point final avec un bouton
  • Notifications de temps réel- - SignalR fait des mises à jour lorsque les spécifications sont chargées
  • Éclaircissement de la syntaxe - Gracieuse présentation de la réponse JSON
  • Gestion du contexte - Voir et clarifier les contextes

Caractéristiques avancées

Paramètres du chemin

Les paramètres du chemin sont extraits automatiquement:

# OpenAPI Spec
/pet/{petId}:
  get:
    parameters:
      - name: petId
        in: path
        schema:
          type: integer
GET /petstore/pet/123
### LLM receives: "Generate data for Pet with petId=123"
### Response: {"id": 123, ...}

Paramètres de la demande

Les paramètres de la demande influent sur la réponse:

/pet/findByStatus:
  get:
    parameters:
      - name: status
        in: query
        schema:
          type: string
          enum: [available, pending, sold]
GET /petstore/pet/findByStatus?status=available
### LLM receives: "Generate array of Pets with status=available"
### Response: [{"status": "available", ...}, ...]

Organismes de demande

POST/Les organes PUT sont inclus dans le prompt:

/pet:
  post:
    requestBody:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Pet'
POST /petstore/pet
Content-Type: application/json

{"name": "Max", "status": "available"}

### LLM receives: "Generate response for creating Pet with name=Max, status=available"
### Response: {"id": 42, "name": "Max", "status": "available"}

Descriptions et sommaires

Les descriptions OpenAPI guident le LLM:

/pet/{petId}:
  get:
    summary: Find pet by ID
    description: Returns a single pet based on the ID provided

Elles sont incluses dans le prompt, qui aide le LLM à comprendre l’objectif du point final'

Codes d'état de la réponse

Le système utilise la première réponse réussie :

responses:
  '200':
    description: Successful operation
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/Pet'
  '404':
    description: Pet not found

Seule le schéma 200 est utilisé pour la génération de modèles.

SignalR Real-Mises à jour du temps

Lorsque les spécifications sont chargées, l’interface reçoit des notifications réelles de temps via SignalR:

public async Task NotifySpecLoaded(string name, string basePath)
{
    await _hubContext.Clients.All.SendAsync("SpecLoaded", new
    {
        name,
        basePath,
        timestamp = DateTimeOffset.UtcNow
    });
}

public async Task NotifySpecDeleted(string name)
{
    await _hubContext.Clients.All.SendAsync("SpecDeleted", new
    {
        name,
        timestamp = DateTimeOffset.UtcNow
    });
}

Code de l'interface JavaScript:

const connection = new signalR.HubConnectionBuilder()
    .withUrl('/hubs/openapi')
    .build();

connection.on('SpecLoaded', (data) => {
    showNotification(`Spec "${data.name}" loaded at ${data.basePath}`, 'success');
    refreshSpecList();
});

connection.on('SpecDeleted', (data) => {
    showNotification(`Spec "${data.name}" deleted`, 'info');
    refreshSpecList();
});

Soutien au format

Le système supporte:

  • OpenAPI 3.0.x
  • OpenAPI 3.1.x
  • Swagger 2.0
  • Format JSON
  • Format YAML

Les spécifications JSON et YAML sont automatiquement détectées et analysées.

Pratiques exemplaires

1. Utiliser des noms de spécifications descriptifs

 Bad:  {"name": "spec1", ...}
 Good: {"name": "github-v3", ...}

2. Déterminer les voies de base appropriées

Éviter les conflits en utilisant des voies de base uniques:

### Good separation
/petstore/...
/github/...
/stripe/...

### Bad (conflicts!)
/api/... (multiple specs)

3. Recharger lorsque les spécifications changent

Si votre spécification OpenAPI est mise à jour, la chargez de nouveau:

POST /api/openapi/specs/my-api/reload

4. Utiliser des contextes pour les appels connexes

POST /api/openapi/specs
{
  "name": "petstore",
  "source": "...",
  "contextName": "test-session"
}

### Now all petstore calls maintain consistency

5. Nettoyage après essai

Supprimer les spécifications que vous n’utilisez plus'

DELETE /api/openapi/specs/old-spec

Limitations

  1. Codes d'état de la réponse - Seules les réponses réussies 2xxM SK2 sont reprochées
  2. Authentification - Les en-têtes d'Auth sont acceptés mais non validés
  3. Validation - La demande de validation contre les schémas n'est pas appliquée
  4. État - Aucune base de données réelle
  5. Rendement - La génération de LLM ajoute la latence ~100-500ms par demande

Intégration avec les points finals de Mock régulier

Les spécifications OpenAPI fonctionnent en même temps que les ordinaires /api/mock Points finals:

### OpenAPI-based (from spec)
GET /petstore/pet/123
### Uses Pet schema from OpenAPI spec

### Regular mock (shape-based)
GET /api/mock/custom?shape={"id":0,"name":"string"}
### Uses explicit shape parameter

Les deux utilisent le même LLM sous-jacent mais diffèrent dans la façon dont le schéma est fourni.

Optimisation du rendement

Cachement

Les spécifications chargées sont cachées dans la mémoire:

private readonly ConcurrentDictionary<string, OpenApiSpecConfig> _specs = new();

Durée de vie du service

DynamicOpenApiManager est un tonne unique, de sorte que les spécifications demeurent chargées pour la durée de vie de l'applicationM SK1

Chargement de spécifications parallèles

Plusieurs spécifications peuvent être chargées en parallèle:

### Send these simultaneously
POST /api/openapi/specs {"name": "spec1", ...}
POST /api/openapi/specs {"name": "spec2", ...}
POST /api/openapi/specs {"name": "spec3", ...}

Tous les trois seront chargés en parallèle, non séquentiellementM SK1

Troubleshooting

Spec Won't Charge

Problème: Défaut de chargement des spécifications

Solutions:

  • Vérifier si l'URL est accessible
  • Vérifier si le fichier existe (pour les chemins locaux)
  • Assurez-vous que JSON/YAML est valide
  • Trouver des problèmes de CORS (pour les URLs éloignés)

Point final n'est pas trouvé

Problème: 404 sur le point final prévu

Solutions:

  • Vérifier le chemin de base: /petstore + /pet/123 = /petstore/pet/123
  • Vérifiez que la spécification définit effectivement ce chemin
  • Assurez-vous que la méthode HTTP correspond (GET vs POST)

Réponse ne correspond pas à l’échéancier

Problème: Les données générées ne correspondent pas au schéma escompté

Solutions:

  • Vérifier si le schéma dans la spécification est correct
  • Vérifiez si vous regardez la réponse appropriée (200 vs 201)
  • Rappelez-vous que la génération de LLM est probabiliste: non déterministe

Conclusion

L'OpenAPI Dynamic Mock Generator transforme toute spécification OpenAPI en une API à mock fonctionnel intégralement-dans quelques secondes

Il suffit de charger une spécification et de commencer à faire des demandes. Le LLM génère des données réalistes, variées qui correspondent aux schémas de votre entrepriseM SK2 ce qui la rend parfaite pour les besoins de l’entreprise :

  • Développement de frontend alors que le backend est en cours de construction
  • Essais d'intégration sans toucher des API réels
  • Développement hors ligne lorsque la connectivité est limitée
  • Essais de chargement sans préoccupations en matière de quota
  • Prototypage de nouvelles fonctionnalités rapidement

En combinaison avec les contextes d’API, vous obtiendrez des API-moquettes de qualité qui assurent la cohérence entre les appels, offrant une simulation encore plus réaliste des systèmes de productionM SK2

L’avenir des API-moquettes est dynamique, intelligent, et appuyé par les mêmes LLM qui alimentent les outils de développement modernes . Bienvenue à la nouvelle ère du moquettement d’APIM SK3

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.