Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL together with gRPC #140

Open
ZirgVoice opened this issue Oct 23, 2023 · 4 comments
Open

GraphQL together with gRPC #140

ZirgVoice opened this issue Oct 23, 2023 · 4 comments
Labels
enhancement New feature or request

Comments

@ZirgVoice
Copy link
Contributor

Hi, I want to use gRPC to communicate microservices but leave GraphQL over http as api for the client. I added gRPC support in Vapor from this article, but then http stops working. Could you please tell me how to make GraphQL via http work together with gRPC? This article describes how to use rest together with GRPC, maybe it is possible to make GraphQL work too?

@ZirgVoice ZirgVoice added the enhancement New feature or request label Oct 23, 2023
@d-exclaimation
Copy link
Owner

d-exclaimation commented Oct 23, 2023

Hi, having a server serving both gRPC and GraphQL seemed possible. I haven't tried this personally but it would reckon it would not be too different from running gRPC with regular REST.

I have taken a look at the article but I am missing a lot context and information because the material (which I assumed is the starting point codebase) is not available unless I pay for it.

From my understanding, there is about 2 ways of having this setup:

  1. The easiest approach is to just have 2 servers with a different port running on 1 Swift application, with one for gRPC and HTTP/2 and the other is serving GraphQL which can be HTTP/1.1 or HTTP/2. Swift version 5 or higher have Task to help achieve this by running each server on their on Task. This is definitely not optimal but this is by far the easiest and simplest approach to it.
import Pioneer
import Vapor
import GRPCVapor
import NIOSSL

let gRPCTask = Task {
    let gApp = try Application(.detect())
    let certificates = try NIOSSLCertificate.fromPEMBytes(Array(sampleCert.utf8))
    let privateKey = try NIOSSLPrivateKey.init(bytes: Array(sampleKey.utf8), format: .pem)

    app.server.configuration.supportVersions = [.two]
    app.server.configuration.tlsConfiguration = .forServer(certificateChain: certificates.map { .certificate($0) }, privateKey: .privateKey(privateKey))

    app.middleware.use(GRPCMiddleware(services: [SomeGRPCService()]))
}

let gqlTask = Task {
    let server = try Pioneer(
        schema: schema(),
        resolver: Resolver(),
        httpStrategy: .csrfPrevention,
        introspection: true,
        playground: .sandbox
    )

    try server.standaloneServer(
         at: "graphql",
         context: { req, res in
             Context(req, res)
         }
    )
}
  1. The later is to run them on a single server. Taking a look at this example of setting up Vapor with gRPC and also serving REST endpoints, the only part that is of interest is that gRPC adapter is basically just a middleware. Pioneer is also just a Vapor middleware but it can also be apply on the route like a normal endpoint, so you can have the gRPC as a middleware and either also apply Pioneer as middleware (as far as I can tell, it won't cause conflict) or apply it on individual routes like a normal REST endpoint. This is more optimal because it have them running on 1 Vapor application but it will force your app to be on HTTP/2 because gRPC require HTTP/2. REST and GraphQL is usually using HTTP/1.1 for normal requests.
import Pioneer
import Vapor
import GRPCVapor
import NIOSSL

// Called before your application initializes.
public func configure(_ app: Application) throws {
    let server = try Pioneer(
        schema: schema(),
        resolver: Resolver(),
        httpStrategy: .csrfPrevention,
        introspection: true,
        playground: .sandbox
    )

    let certificates = try NIOSSLCertificate.fromPEMBytes(Array(sampleCert.utf8))
    let privateKey = try NIOSSLPrivateKey.init(bytes: Array(sampleKey.utf8), format: .pem)

    // Setting up HTTP/2 which is probably the cause of issues since normal REST and GraphQL is HTTP/1.1
    app.server.configuration.supportVersions = [.two]
    app.server.configuration.tlsConfiguration = .forServer(certificateChain: certificates.map { .certificate($0) }, privateKey: .privateKey(privateKey))

    app.middleware.use(GRPCMiddleware(services: [SomeGRPCService()]))
   
    app.group("graphql") { group in
        group.post { req in 
            try await server.httpHandler(req: req, context: { ... }) 
        }
    }
}

I haven't tried either approach but those 2 will be the approaches I would give a shot. I think the best issue in general is that gRPC is using HTTP/2 and making REST and GraphQL work with HTTP/2 is going to be difficult

@ZirgVoice
Copy link
Contributor Author

For Hummingbird there is a library that adds grpc support along with rest and rest seems to even work over HTTP/1.1. Is there a pioneer planned for the Hummingbird? I might be thinking about switching from Vapor to Hummingbird then 🙂

@d-exclaimation
Copy link
Owner

It was on my todo a while back. I stopped only due to that I ran into a roadblock with how I would be able to handle both HTTP and WebSocket requests using 1 "/graphql" endpoint, mainly on upgrade WebSocket requests manually.

I made a repo for the integration/adapter which should at some point hold the library itself while I am still experimenting with it, and before I added into this package a

https://github.com/d-exclaimation/pioneer-hummingbird

I can't make a promise that I would be able to get something within the next 1-2 weeks as I am still relatively occupied. After 2 weeks or so, I might be able to get a working adapter but it would pretty experimental with bugs and not so polish DX yet.

If you need this urgently, you can also go have a shot on creating a simple integration for it. I made a guide on this here, and if you do, feel free to ask me questions about it and I'll try my best helping.

@d-exclaimation
Copy link
Owner

Update on this, the package does technically work although it's very bare-bone at the moment and is missing GraphQL over WebSocket

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants