NeuroAgent

Fix ID Mismatch Error: Spring Boot REST API & Next.js Client

Learn how to resolve JSON deserialization errors when calling Spring Boot REST API from Next.js client. Fix ID mismatch issues between UUID strings and long IDs in OpenAPI/Swagger-generated clients.

Question

How to resolve ID mismatch error when calling Spring Boot REST API from Next.js client generated with OpenAPI/Swagger?

I’m encountering a JSON deserialization error when posting a Product entity with nested ProductMaterials to my Spring Boot REST API. The error message is:

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `long` from String "52a92362-3c7b-40e6-adfe-85a1007c121f": not a valid `long` value

Here are my entity classes:

java
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Product {
    @Id
    private String sku;

    @OneToMany(cascade = CascadeType.ALL,
            fetch = FetchType.EAGER,
            targetEntity = ProductMaterial.class)
    private List<ProductMaterial> materials;
}

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ProductMaterial {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @OneToOne
    private Material material;

    private float unitsPerProduct;
}

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Material {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;

    private String description;
    private float costPerUnit;
    private int inStock;
}

And my repositories:

java
@RepositoryRestResource(collectionResourceRel = "product", path = "products")
public interface ProductRepository extends PagingAndSortingRepository<Product, Long>, CrudRepository<Product, Long> {
}

@RepositoryRestResource(collectionResourceRel = "product-material", path = "product-materials", exported = false)
public interface ProductMaterialRepository extends PagingAndSortingRepository<ProductMaterial, String>, CrudRepository<ProductMaterial, String> {
}

@RepositoryRestResource(collectionResourceRel = "material", path = "materials")
public interface MaterialRepository extends PagingAndSortingRepository<Material, String>, CrudRepository<Material, String> {
}

I’m using SpringDoc OpenAPI (springdoc-openapi-starter-webmvc-ui:2.8.13) and generated a TypeScript client with:

bash
npx @openapitools/openapi-generator-cli generate -i http://localhost:8080/v3/api-docs -g typescript-fetch -o ./services/generated/api --additional-properties=supportsES6=false,modelPropertyNaming=original,withInterfaces=true,prependFormOrBodyParameters=true

When I post a product with materials, the client sends this JSON:

json
{"sku":"PRD-001","name":"Test Produkt","materials":[{"id":-1,"material":{"id":"52a92362-3c7b-40e6-adfe-85a1007c121f","description":"Material 1","costPerUnit":1,"inStock":1},"unitsPerProduct":1},{"id":-1,"material":{"id":"8a0d57cc-d3bd-4653-b50f-d4a14d5183b3","description":"Material 4","costPerUnit":0.25,"inStock":4},"unitsPerProduct":1}],"sellPrice":"1.2"}

The issue seems to be that the material ID (UUID string) is being incorrectly mapped to the ProductMaterial’s ID field (long). How can I resolve this ID mismatch between the client and server?

NeuroAgent

The ID mismatch error occurs because your OpenAPI specification doesn’t properly distinguish between the auto-generated numeric ID of ProductMaterial and the UUID reference to Material. The TypeScript client is incorrectly mapping the Material’s UUID string to the ProductMaterial’s id field (which should be a long).


Contents

Understanding the Problem

The error Cannot deserialize value of type 'long' from String "52a92362-3c7b-40e6-adfe-85a1007c121f": not a valid 'long' value indicates a fundamental type mismatch between what your Spring Boot API expects and what the Next.js client is sending.

Looking at your entity classes:

  • ProductMaterial has @Id private long id (auto-generated numeric ID)
  • Material has @Id private String id (UUID string)
  • The JSON payload shows the client is sending "id":"52a92362-3c7b-40e6-adfe-85a1007c121f" where a numeric value is expected

This confusion happens because the OpenAPI specification doesn’t clearly distinguish between these different ID types, causing the client generator to incorrectly map fields.

Root Cause Analysis

The issue stems from how SpringDoc OpenAPI generates the specification and how the OpenAPI client interpreter handles it. According to the OpenAPI Specification v3.0.3, UUID fields should be properly declared with format: "uuid", but this often doesn’t happen automatically with SpringDoc.

From the research, we can see that this is a common problem:

Solution 1: Fix OpenAPI Specification

The most robust solution is to explicitly define your API contracts with proper UUID formatting. Add DTOs that clearly separate the different ID types:

java
// Create DTOs that clearly separate ID types
public class ProductCreateDTO {
    private String sku;
    private String name;
    private List<ProductMaterialCreateDTO> materials;
    private BigDecimal sellPrice;
}

public class ProductMaterialCreateDTO {
    // Don't include the auto-generated ID here
    private MaterialReferenceDTO material;
    private float unitsPerProduct;
}

public class MaterialReferenceDTO {
    private String id; // This is clearly a UUID
    private String description;
    private float costPerUnit;
    private int inStock;
}

Then update your controller to use these DTOs:

java
@PostMapping("/products")
public ResponseEntity<Product> createProduct(@RequestBody ProductCreateDTO productDTO) {
    // Convert DTO to entity and save
    Product product = convertToEntity(productDTO);
    Product savedProduct = productRepository.save(product);
    return ResponseEntity.ok(savedProduct);
}

According to the OpenAPI Specification, this approach ensures the specification properly defines UUID fields with the correct format.

Solution 2: Adjust Client Generation Configuration

Modify your client generation command to include properties that handle UUIDs correctly:

bash
npx @openapitools/openapi-generator-cli generate \
  -i http://localhost:8080/v3/api-docs \
  -g typescript-fetch \
  -o ./services/generated/api \
  --additional-properties=supportsES6=false,modelPropertyNaming=original,withInterfaces=true,prependFormOrBodyParameters=true,typescriptThreePlus=true,useSingleRequestParameter=true,enumNamingConvention=original

Add a custom configuration to handle UUIDs properly in the OpenAPI spec generation. Create a configuration class:

java
@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
            .info(new Info()
                .title("Product API")
                .version("1.0")
                .description("API for managing products and materials"))
            .components(new Components()
                .addSchemas("UUID", new Schema<>()
                    .type("string")
                    .format("uuid")
                    .pattern("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
                    .minLength(36)
                    .maxLength(36)));
    }
}

This ensures that UUID fields are properly defined in the OpenAPI specification, which helps the client generator create the correct TypeScript types.

Solution 3: Use DTOs with Proper ID Separation

Create separate DTOs that explicitly handle the ID mapping issue:

java
// Request DTO without auto-generated IDs
public class ProductRequest {
    private String sku;
    private String name;
    private List<MaterialReference> materials;
    private String sellPrice;
}

public class MaterialReference {
    private String materialId; // Clearly a UUID reference
    private float unitsPerProduct;
}

// Response DTOs that include auto-generated IDs
public class ProductResponse {
    private Long id;
    private String sku;
    private String name;
    private List<ProductMaterialResponse> materials;
    private String sellPrice;
}

public class ProductMaterialResponse {
    private Long id; // Auto-generated numeric ID
    private MaterialResponse material;
    private float unitsPerProduct;
}

This approach completely eliminates the confusion between different ID types.

Solution 4: Custom Serialization

If you need to keep the existing entity structure, implement custom serialization:

java
public class ProductMaterialSerializer extends JsonSerializer<ProductMaterial> {
    @Override
    public void serialize(ProductMaterial value, JsonGenerator gen, SerializerProvider provider) 
        throws IOException {
        gen.writeStartObject();
        // Don't serialize the auto-generated ID for creation requests
        // gen.writeNumberField("id", value.getId());
        gen.writeObjectField("material", value.getMaterial());
        gen.writeNumberField("unitsPerProduct", value.getUnitsPerProduct());
        gen.writeEndObject();
    }
}

// Use with @JsonSerialize on the list in your entity
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = ProductMaterial.class)
@JsonSerialize(contentUsing = ProductMaterialSerializer.class)
private List<ProductMaterial> materials;

Best Practices for UUID Handling

Based on the research findings from OpenAPI Specification and various GitHub issues, here are best practices:

  1. Always specify UUID format in your OpenAPI specification:

    yaml
    id:
      type: string
      format: uuid
      pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
      minLength: 36
      maxLength: 36
    
  2. Use separate DTOs for request and response to avoid ID confusion

  3. Configure your client generator with appropriate properties for UUID handling

  4. Consider using UUIDs consistently or clearly separating numeric and string IDs in your API design

  5. Test your API specification with tools like Swagger UI to ensure UUID fields are properly displayed and validated

Conclusion

The ID mismatch error between your Spring Boot API and Next.js client can be resolved through several approaches:

  1. Fix the OpenAPI specification by explicitly defining UUID formats and using proper DTOs
  2. Adjust client generation configuration to handle UUIDs correctly
  3. Use separate DTOs that clearly distinguish between different ID types
  4. Implement custom serialization when you need to maintain existing entity structures

The most sustainable solution is to implement proper DTOs with clear ID separation and ensure your OpenAPI specification correctly defines UUID fields with the appropriate format. This approach prevents similar issues in the future and makes your API more maintainable.

Remember to validate your OpenAPI specification using tools like Swagger UI to ensure all UUID fields are properly defined before generating client code.


Sources

  1. OpenAPI Specification v3.0.3 - UUID Format Definition
  2. How to define UUID property in JSON Schema and Open API (OAS)
  3. OpenAPI format uuid - PostgREST Issue #1031
  4. ClassCastException when casting UUIDs to strings - OpenAPI Generator Issue #16429
  5. UUID parameters don’t have format “uuid” - SpringFox Issue #2314