Skip to content

OpenAPI Generator Server Generation: Build API Server Stubs from Specs

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about OpenAPI Generator Server Generation: Build API Server Stubs from Specs. We cover key concepts, practical examples, and best practices to help you master this topic.

Server generation creates stub code for implementing API backends from OpenAPI specs, providing controllers, models, serialization, validation, and routing for frameworks like Flask, Express, Spring, and Gin.

What You'll Learn

How to generate server stubs for multiple frameworks (Python Flask/FastAPI, Node.js Express, Java Spring Boot, Go Gin), implement handler logic, configure model serialization, add validation, and test the generated server.

Why It Matters

Server stub generation provides a spec-compliant starting point with routing, models, and validation pre-configured. DodaTech generates Spring Boot stubs from specs, reducing API implementation time by 60%.

Real-World Use

The DodaTech team adds a new Orders API. They write the OpenAPI spec, generate a Spring Boot server stub, implement the business logic in the generated controller interfaces, and deploy — all within 2 hours instead of 2 days.

flowchart LR
    A["OpenAPI\nSpec"] --> B["Server\nGenerator"]
    B --> C["Python\nFlask/FastAPI"]
    B --> D["Node.js\nExpress"]
    B --> E["Java\nSpring Boot"]
    B --> F["Go\nGin/Echo"]
    C --> G["Implement\nControllers"]
    D --> G
    E --> G
    F --> G
    G --> H["Working\nAPI Server"]
    style A fill:#6cb4ee,color:#fff
    style H fill:#bbf7d0,stroke:#16a34a

Generating Python Flask Server

# Generate Python Flask server
openapi-generator generate \
  -i openapi.yaml \
  -g python-flask \
  -o ./server-python \
  --additional-properties=packageName=dodatech_api

# Generated structure:
# ./server-python/
#   openapi_server/
#     controllers/     # Stub controllers to implement
#     models/          # Data models (auto-generated)
#     openapi/         # Original spec copy
#     encoder.py
#     util.py
#   requirements.txt
#   setup.py
# Generated controller stub (openapi_server/controllers/user_controller.py)
import connexion
from typing import Any
from openapi_server.models.user import User
from openapi_server.models.error import Error

def create_user(body: User) -> tuple[User, int]:
    """Create a new user.

    Implement this function with your business logic.
    """
    # TODO: Implement user creation
    # 1. Validate input
    # 2. Save to database
    # 3. Return created user
    return body, 201

def get_user(user_id: int) -> tuple[User | Error, int]:
    """Get user by ID."""
    # TODO: Implement
    # user = db.session.get(User, user_id)
    # if user:
    #     return user, 200
    return Error(message="User not found"), 404

def list_users(limit: int = 20, offset: int = 0) -> tuple[list[User], int]:
    """List users with pagination."""
    # TODO: Implement
    # users = db.session.query(User).limit(limit).offset(offset).all()
    # return users, 200
    return [], 200

Generating Node.js Express Server

# Generate Express server
openapi-generator generate \
  -i openapi.yaml \
  -g nodejs-express-server \
  -o ./server-node \
  --additional-properties=serverPort=8080
// Generated controller (controllers/UserController.js)
'use strict';

const utils = require('../utils/writer.js');
const User = require('../service/UserService');

module.exports.createUser = function createUser(req, res, next) {
    const body = req.swagger.params.body.value;
    User.createUser(body)
        .then((response) => {
            utils.writeJson(res, response, 201);
        })
        .catch((error) => {
            utils.writeJson(res, { message: error.message }, error.status || 500);
        });
};

module.exports.getUser = function getUser(req, res, next) {
    const userId = req.swagger.params.userId.value;
    User.getUser(userId)
        .then((response) => {
            if (response) {
                utils.writeJson(res, response);
            } else {
                utils.writeJson(res, { message: 'Not found' }, 404);
            }
        })
        .catch((error) => {
            utils.writeJson(res, { message: error.message }, 500);
        });
};

Generating Java Spring Boot Server

# Generate Spring Boot server
openapi-generator generate \
  -i openapi.yaml \
  -g spring \
  -o ./server-java \
  --additional-properties=\
useSpringBoot3=true,\
useJakartaEe=true,\
apiPackage=com.dodatech.api,\
modelPackage=com.dodatech.model
// Generated controller interface (com/dodatech/api/UsersApi.java)
package com.dodatech.api;

import com.dodatech.model.User;
import com.dodatech.model.Error;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1")
public interface UsersApi {

    @PostMapping("/users")
    ResponseEntity<User> createUser(@RequestBody User body);

    @GetMapping("/users/{userId}")
    ResponseEntity<User> getUser(@PathVariable Long userId);

    @GetMapping("/users")
    ResponseEntity<List<User>> listUsers(
        @RequestParam(defaultValue = "20") Integer limit,
        @RequestParam(defaultValue = "0") Integer offset
    );
}
// Implementation (com/dodatech/api/UsersApiController.java)
@RestController
public class UsersApiController implements UsersApi {

    @Autowired
    private UserRepository userRepository;

    @Override
    public ResponseEntity<User> createUser(User body) {
        User user = userRepository.save(body);
        return ResponseEntity.status(201).body(user);
    }

    @Override
    public ResponseEntity<User> getUser(Long userId) {
        return userRepository.findById(userId)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    @Override
    public ResponseEntity<List<User>> listUsers(Integer limit, Integer offset) {
        Pageable pageable = PageRequest.of(offset / limit, limit);
        Page<User> users = userRepository.findAll(pageable);
        return ResponseEntity.ok(users.getContent());
    }
}

Common Mistakes

1. Implementing Logic in Generated Controllers

Generated files are overwritten on regeneration. Never modify generated controller stubs directly. Use Dependency Injection (Spring), service layers, or separate implementation files (Express services).

2. Not Configuring Model Serialization

Default serialization may expose internal field names or include null values. Configure JSON property naming, null handling, and date formats in generator options.

3. Ignoring Validation Annotations

Generated models include validation annotations (@NotNull, @Size, @Pattern). Don't remove them — they provide spec-compliant input validation. Add custom validation in the service layer.

4. Using Generated Code Without Customization

Generated stubs return placeholder data. Every endpoint must be implemented with real business logic, database access, and error handling before the server is usable.

5. Forgetting to Handle Errors

Generated controllers often return 500 for unhandled exceptions. Implement proper error handling: 400 for validation, 404 for not found, 409 for conflicts, 500 for server errors.

Practice Questions

  1. How do you implement generated controller stubs without losing changes?
  2. What validation does generated code provide?
  3. How do you configure server port and package names?
  4. What should you do before running the generated server?

Answers:

  1. Use the service/Repository Patternory" >}} pattern. Generated controllers call service interfaces. Implement service classes separately (not regenerated). Spring's @Service, Express's service/ directory, Flask's blueprint separation.
  2. Generated models include field validation: @NotNull, @Size, @Pattern (Java), pydantic validators (Python), Joi schemas (Node.js). These validate input against the OpenAPI spec automatically.
  3. Use CLI additional-properties: --additional-properties=serverPort=8080,apiPackage=com.dodatech.api. Or set in the config YAML file. Each generator has its own options.
  4. Validate the spec, install dependencies (pip install, npm install, mvn install), configure database connection, implement at least one controller, and run the server to verify it starts.

Challenge: Write an OpenAPI spec for a Blog API (6 endpoints, 4 models), generate server stubs for 2 frameworks (Python FastAPI and Java Spring Boot), implement all endpoints in one framework with an in-memory database, test with curl, and verify the second framework compiles.

FAQ

Can I generate GraphQL servers?

Yes, use the graphql-schema generator to produce a GraphQL schema from OpenAPI. For full GraphQL server stubs, use the nodejs-express-server or spring generator and add GraphQL manually.

Does OpenAPI Generator support WebSocket endpoints?

OpenAPI 3.0 supports WebSocket callbacks. Some generators handle callbacks. For full WebSocket support, add WebSocket handlers manually after generation.

How do I add authentication to generated servers?

Generated stubs include security scheme definitions. Configure Spring Security, Flask middleware, or Express auth middleware to validate tokens based on the spec's securitySchemes.

Can I generate serverless functions?

Yes, use the aws-serverless-express or google-cloud-functions generator for serverless deployment. Generated handlers are compatible with AWS Lambda and Google Cloud Functions.

What is the best framework for Python server generation?

python-fastapi (recommended for new projects) with async support, automatic OpenAPI docs, and Pydantic validation. python-flask for legacy Flask projects.

Mini Project

Write an OpenAPI spec for a Task Management API (8 endpoints, 5 models), generate Python FastAPI server stubs and Java Spring Boot stubs, implement all Python endpoints with SQLite database, start the server, test every endpoint with curl, and verify the generated Spring Boot project compiles.

What's Next

Client Generation — generate client SDKs for various platforms.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro