Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

Hi! In 7 version can i use different schema for authorized/unauthorized requests? In previous version i use it over:

suspend fun handle(applicationCall: ApplicationCall) {
        val result = when {
            applicationCall.containHeader("ADMIN_HEADER") -> {
                if (applicationCall.request.hasCorrectAdminHeader()) {
                    fullGraphQLServer.execute(applicationCall.request)
                } else {
                    delay(2_000)
                    applicationCall.response.call.respond(HttpStatusCode.Unauthorized, "No access")
                    return
                }
            }

            applicationCall.containHeader("AUTH_HEADER") -> {
                fullGraphQLServer.execute(applicationCall.request)
            }

            else -> {
                guestGraphqlServer.execute(applicationCall.request)
            }
        }
        if (result != null) {
            val json = mapper.writeValueAsString(result)
            applicationCall.response.call.respondText(contentType = ContentType.Application.Json, text = json)
        } else {
            applicationCall.response.call.respond(HttpStatusCode.BadRequest, "Invalid request")
        }
    }

How can now i do this?

You must be logged in to vote

In the end I managed to solve the problem:

  1. Create custom route for post request:
private fun Route.graphQLPostRoute(): Route {
    val ktorServer = get<KtorServer>()
    return route("/graphql", HttpMethod.Post) {
        handle {
            ktorServer.handle(this.call)
        }
        install(ContentNegotiation) {
            jackson(streamRequestBody = true) {}
        }
    }
}
  1. Create instance for Guest graphql server:
internal class GuestGraphql(
    private val contextFactory: GuestGraphQLContextFactory,
    private val dataLoaderRegistryFactory: KotlinDataLoaderRegistryFactory,
) {
    private val graphQl: GraphQL
    val server: KtorGraphQLServer
        get() = graphQl.ser…

Replies: 4 comments

Comment options

Hello 👋
I assume the question is how to serve 2 schemas using new GraphQL Kotlin Ktor Plugin?

I am unsure whats the use case for serving two different GraphQL schemas vs using some authorization mechanism to limit access (directives) on the schema directly. Plugin currently doesn't support generating multiple schemas so you would either have to transform existing one or manually generate it. I believe you should (haven't tried it) be able to do it by providing some custom routing (see default routing, i.e.

fun Application.graphQLModule() {
    install(GraphQL) {
        schema {
            packages = listOf("com.example")
            queries = listOf(
                HelloWorldQuery()
            )
        }
    }
    routing {
      route("/graphql", HttpMethod.Post) {
        // plugin holds auto generated FULL schema + corresponding server config
        val graphQLPlugin = this.application.plugin(GraphQL)
        // you could transform the schema and generate the corresponding server but thats custom logic
        handle {
          // your logic from above goes here
        }
      }
    }
}
You must be logged in to vote
0 replies
Comment options

Are you planning to open class KtorGraphQLRequestParser to replace it over ServerConfiguration (Field var requestParser)? Otherwise, why is it marked as var?

My case:
I use first schema for authorization and refresh token, and second for authorized requests. May be can you advice better use case for it?

You must be logged in to vote
0 replies
Comment options

Are you planning to open class KtorGraphQLRequestParser to replace it over ServerConfiguration (Field var requestParser)? Otherwise, why is it marked as var?

Yeah the request parser should be open, looks like it was missed on the Ktor side (Spring request parser is already open) -> please open up a PR :)

My case:
I use first schema for authorization and refresh token, and second for authorized requests. May be can you advice better use case for it?

Personally I wouldn't use GraphQL for doing auth as IMHO that's already a solved problem with OAuth2. Instead I'd call my GraphQL server AFTER obtaining proper JWT tokens so you would only need to validate them (i.e. whether users have valid permissions/scopes) inside your server.

You must be logged in to vote
0 replies
Comment options

In the end I managed to solve the problem:

  1. Create custom route for post request:
private fun Route.graphQLPostRoute(): Route {
    val ktorServer = get<KtorServer>()
    return route("/graphql", HttpMethod.Post) {
        handle {
            ktorServer.handle(this.call)
        }
        install(ContentNegotiation) {
            jackson(streamRequestBody = true) {}
        }
    }
}
  1. Create instance for Guest graphql server:
internal class GuestGraphql(
    private val contextFactory: GuestGraphQLContextFactory,
    private val dataLoaderRegistryFactory: KotlinDataLoaderRegistryFactory,
) {
    private val graphQl: GraphQL
    val server: KtorGraphQLServer
        get() = graphQl.server
    val schema: GraphQLSchema
        get() = graphQl.schema

    init {
        val schema = GuestGraphqlSchema.schema
        val config = GraphQLConfiguration(config = ApplicationConfig(null)).apply {
            this.schema.override(schema)
            engine {
                this.dataLoaderRegistryFactory = this@GuestGraphql.dataLoaderRegistryFactory
            }
            server {
                this.contextFactory = this@GuestGraphql.contextFactory
            }
        }
        this.graphQl = GraphQL(config = config)
    }
}
  1. Create authorized graphql server:
internal class FullGraphql(
    private val contextFactory: FullGraphQLContextFactory,
    private val dataLoaderRegistryFactory: KotlinDataLoaderRegistryFactory,
) {
    private val graphQl: GraphQL
    val schema: GraphQLSchema
        get() = graphQl.schema
    val server: KtorGraphQLServer
        get() = graphQl.server
    val subscriptionServer: KtorGraphQLWebSocketServer
        get() = graphQl.subscriptionServer

    init {
        val schema = FullGraphqlSchema.schema
        val config = GraphQLConfiguration(config = ApplicationConfig(null)).apply {
            this.schema.override(schema)
            engine {
                this.dataLoaderRegistryFactory = this@FullGraphql.dataLoaderRegistryFactory
            }
            server {
                this.contextFactory = this@FullGraphql.contextFactory
            }
        }
        this.graphQl = GraphQL(config = config)
    }
}
  1. Parse ktor reques, check correct headers, execute request:
internal class KtorServer(
    private val fullGraphql: FullGraphql,
    private val guestGraphqlServer: GuestGraphql,
    private val tokenService: TokenService,
    private val mapper: ObjectMapper,
) : KoinComponent {
    /**
     * Handle incoming Ktor Http requests and send them back to the response methods.
     */
    suspend fun handle(applicationCall: ApplicationCall) {
        val executeResult = applicationCall.executeByCallType(
            authorizedRequest = {
                fullGraphql.server.execute(applicationCall.request)
            },
            guestRequest = {
                guestGraphqlServer.server.execute(applicationCall.request)
            },
            brokenRequest = {
                applicationCall.response.call.respond(HttpStatusCode.Unauthorized, "No access")
                return
            },
        )

        if (executeResult == null) {
            applicationCall.response.call.respond(HttpStatusCode.BadRequest, "Invalid request")
            return
        }
        val json = mapper.writeValueAsString(executeResult)
        applicationCall.response.call.respondText(contentType = ContentType.Application.Json, text = json)
    }
}
You must be logged in to vote
0 replies
Answer selected by dariuszkuc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement New feature or request
2 participants
Converted from issue

This discussion was converted from issue #1850 on September 21, 2023 18:25.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.