springspring-bootgraphqlgraphiqlspring-graphql

GraphiQL over Springboot shows React errors and blank page


I work on a Kotlin Spring API. It is running inside a Docker container from eclipse-temurin:17-jdktrough image on WSL 2 Ubuntu 22.04 LTS (Host is Windows 11 Entreprise 24H2).

Project dependencies version :

The API works except for the /graphiql route. When I try accessing it, I get a blank page with the following console error and warnings :

Uncaught TypeError: e.useSyncExternalStore is not a function
    Lu https://unpkg.com/graphiql/graphiql.min.js:31
    Fu https://unpkg.com/graphiql/graphiql.min.js:31
    Ru https://unpkg.com/graphiql/graphiql.min.js:31
    React 16
    <anonymous> http://localhost:13311/graphiql?path=/graphql:39
graphiql.min.js:31:1963

The above error occurred in the <Ru> component:
    in Ru (created by nm)
    in nm
    in Unknown
react-dom.development.js:19662:15

Uncaught undefined
react-dom.development.js:22800:7

GET
http://localhost:13311/favicon.ico
[HTTP/1.1 403  31ms]

Source map error: NetworkError when attempting to fetch resource.
Resource URL: https://unpkg.com/graphiql/graphiql.min.js
Source Map URL: index.umd.js.map

On the network monitor side, everything succeed with a 200 status code except the favicon fetch that fail with a 403 status code.

graphiql.min.js version seems to be automatically chosen as loading https://unpkg.com/graphiql/graphiql.min.js automatically adds a version to the URL. The error varies based on the graphiql version.

application.properties content :

spring.graphql.graphiql.enabled=true
spring.graphql.graphiql.path=/graphiql
#allow all origins
spring.graphql.cors.allowed-origins=*
spring.graphql.cors.allowed-methods=*
spring.graphql.cors.allowed-headers=*
spring.datasource.url=jdbc:mariadb://${DB_HOST:1.1.1.1}:${DB_PORT:3333}/${DB_BASE:db}
spring.datasource.username=${DB_USER:user}
spring.datasource.password=${DB_PASS:password}
spring.main.allow-bean-definition-overriding=true
# Hibernate JPA DB initialization :: https://docs.spring.io/spring-boot/docs/1.1.0.M1/reference/html/howto-database-initialization.html
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
# https://www.baeldung.com/spring-data-jpa-generate-db-schema
spring.jpa.properties.jakarta.persistence.schema-generation.scripts.action=create
spring.jpa.properties.jakarta.persistence.schema-generation.scripts.create-target=create.sql
spring.jpa.properties.jakarta.persistence.schema-generation.scripts.create-source=metadata
# dump.sql and triggers.sql execution on load :: https://www.baeldung.com/spring-boot-data-sql-and-schema-sql
# ? Triggers auto-insertion doesn't work at the moment, see API ticket #79
#spring.sql.init.mode=always
#spring.jpa.defer-datasource-initialization=true
#spring.sql.init.schema-locations=classpath:triggers.sql
#spring.sql.init.data-locations=classpath:dump.sql
#
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=false
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
management.endpoint.health.show-components=always
management.prometheus.metrics.export.pushgateway.enabled=true
management.endpoints.web.cors.allowed-origins=*
management.endpoints.web.cors.allowed-methods=*
management.endpoints.web.cors.allowed-headers=*
management.endpoints.web.cors.max-age=3600
management.health.defaults.enabled=false
management.health.db.enabled=true
# JWT Token
security.jwt.token.secret-key=${JWT_SECRET_KEY:secret-key}
security.jwt.algorithm=${JWT_ALGORITHM:HS256}
#server.port=${SPRING_API_PORT:8080}

pom.xml content :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.company</groupId>
    <artifactId>SpringAPI</artifactId>
    <version>1.0.0</version>
    <name>Spring-API</name>
    <description>Spring API</description>
    <properties>
        <java.version>17</java.version>
        <kotlin.version>1.9.23</kotlin.version>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-json-jvm -->
        <dependency>
            <groupId>org.jetbrains.kotlinx</groupId>
            <artifactId>kotlinx-serialization-json-jvm</artifactId>
            <version>1.5.0</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-graphql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.tailrocks.graphql</groupId>
            <artifactId>graphql-datetime-spring-boot-starter</artifactId>
            <version>6.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-kotlin</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor.kotlin</groupId>
            <artifactId>reactor-kotlin-extensions</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlinx</groupId>
            <artifactId>kotlinx-coroutines-reactor</artifactId>
        </dependency>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java-extended-scalars</artifactId>
            <version>20.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.mariadb.jdbc</groupId>
            <artifactId>mariadb-java-client</artifactId>
            <version>3.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.ninja-squad</groupId>
            <artifactId>springmockk</artifactId>
            <version>4.0.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.graphql</groupId>
            <artifactId>spring-graphql-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>

        <!-- SPRING SECURITY -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2.12</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- JWT Dependency -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-serialization</artifactId>
            <version>1.9.23</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>8.0.1.Final</version>
        </dependency>
        <!-- ///////////////////////////////////////////////// -->

        <!--    Logging related dependency    -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.23.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.23.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j2-impl</artifactId>
            <version>2.23.1</version>
        </dependency>

    </dependencies>

    <build>
        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
        <plugins>

            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <configuration>
                    <args>
                        <arg>-Xjsr305=strict</arg>
                    </args>
                    <compilerPlugins>
                        <plugin>spring</plugin>
                        <plugin>jpa</plugin>
                    </compilerPlugins>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-allopen</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-noarg</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>


                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

I've tried the following without success :

I've red graphiql's GitHub changelog and migration guide.

Also, an update in spring-graphql repo seems to have been done 2 days ago, but as the office closed for the past two weeks, I can't tell if it's the origin of the issue.

There's a common point between the migration guide and an warning I encountered, refering the index.umd.js deprecated source map. I think of a unwanted automatic update as I have not updated nor added any dependency in pom.xml and I don't think I can control graphiql configuration from the Spring project, aside from its URL.

Thank you


Solution

  • I think this is due to version changes in the GraphiQL artifacts delivered by the CDN. When this feature was added to Spring for GraphQL, the team used the official CDN example from the project. I guess some of the resolved resources were not locked to a particular version.

    This will change with the GraphiQL 4.0.0 upgrade, scheduled for the 1.3.6 and 1.4.0 releases. In the meantime, you can copy this HTML file in the src/main/resources/graphiql folder of your project.