PHP gRPC-Server & NodeJS-Client
Tags: javascript, php, node, grpc
Published: 2019-07-15 18:42:55

Hello friends, hope you are doing well!

I'm gonna describe how easy to run PHP gRPC-server and code NodeJS gRPC- client. At this article, I will highlight only the more important parts of setting up the workflow. [Here](https://github.com/sergey-telpuk/php-grpc-server-nodejs-client), You can find a done repository. If you aren't acquainted with gRPC, look at the following [link](https://grpc.io/).  
There will be considered a fast solution [spiral/php-grpc](https://github.com/spiral/php-grpc) to gRPC-server. Let's start off.

Firstly, look at [docker-compose](https://github.com/sergey-telpuk/php-grpc-server-nodejs-client/blob/master/docker-compose.yml) file:
```yaml
version: '3.3'

services:

    grpc_php_server: # grpc server
        build:
            context: ./containers/php
            dockerfile: php.docker
        container_name: grpc_php_server
        working_dir: /app
        command: ./rr-grpc serve -v -d
        volumes:
            - ./src/php:/app

    grpc_php_protoc: # protoc generator 
        image:  grpc/php
        container_name: grpc_protoc
        working_dir: /app
        volumes:
            - ./src/php:/app

    client_nodejs: # grpc client
        image: node:latest
        container_name: client_nodejs
        command: node ./client.js
        working_dir: /app
        links:
          - grpc_php_server
        depends_on:
          - grpc_php_server
        volumes:
            - ./src/nodejs:/app
```
As you could see it's a very simple `docker-compose` file.
_Note: if you want to play around, don't forget to grab all dependencies._   
```bash
docker-compose run grpc_php_server composer install
docker-compose run client_nodejs npm install
```
The `proto` file was drawn up as very primitive way as I can :smirk:.
`contrived.proto` is for `server` and `client`: 
```proto
syntax = "proto3";

package service;

service ContrivedService {
    rpc ContrivedMethod (ContrivedMessageRequest) returns (ContrivedMessageResponse) {
    }
}

message ContrivedMessageRequest {
    string body = 1;
}

message ContrivedMessageResponse {
    string body = 1;
}
```
That one was put at `php/proto` and `nodejs/proto` directories.

For generating needed interfaces and initiation skeleton for gRPC-server the following command was used:
```bash
docker-compose run grpc_php_protoc protoc --plugin=./protoc-gen-php-grpc --php_out=/app --php-grpc_out=/app ./proto/contrived.proto
```

The setting of `grpc-server` was hosted at root directory `.rr.yaml`:
```yaml 
grpc:
  listen: "tcp://:3000"
  proto: "./proto/contrived.proto"
  workers:
    command: "php worker.php"
    pool:
      numWorkers: 1
      maxJobs:    1
```
The `worker.php` can look like:
```php
<?php
declare(strict_types=1);
/**
 * Sample GRPC PHP server.
 */
use Spiral\Goridge;
use Spiral\RoadRunner;
ini_set('display_errors', 'stderr');
require "vendor/autoload.php";
$server = new \Spiral\GRPC\Server();
$server->registerService(\Service\ContrivedServiceInterface::class, new \Service\ContrivedService());
$w = new RoadRunner\Worker(new Goridge\StreamRelay(STDIN, STDOUT));
$server->serve($w);
```
The `ContrivedService.php` can look like:
```php
<?php
namespace Service;
use Service\ContrivedMessageRequest;
use Service\ContrivedMessageResponse;
use Service\ContrivedServiceInterface;
use Spiral\GRPC;
class ContrivedService implements ContrivedServiceInterface
{
    /**
     * @param GRPC\ContextInterface $ctx
     * @param ContrivedMessageRequest $in
     * @return ContrivedMessageResponse
     *
     * @throws GRPC\Exception\InvokeException
     */
    public function ContrivedMethod(GRPC\ContextInterface $ctx, ContrivedMessageRequest $in): ContrivedMessageResponse
    {
        $response = new ContrivedMessageResponse();
        $response->setBody("Hello");
        return $response;
    }
}
```
So, let's move up to js-client.

The `client.js` can look like:
```js
const path = require('path');
const PROTO_PATH = path.resolve(__dirname, './proto/contrived.proto');
const GRPCClient = require('node-grpc-client');

const myClient = new GRPCClient(PROTO_PATH, 'service', 'ContrivedService', 'grpc_php_server:3000');

const dataToSend = {
    body: 'Nodejs client!'
};

myClient.runService('ContrivedMethod', dataToSend, (err, res) => {
    if (err) {
        console.error(err);
    }

    console.log('Service response\n', res);
});
```

For trying it out was used the following command:
```bash
 docker-compose up
```
The output result:
```bash
grpc_php_server    | DEBU[0000] [rpc]: started                               
grpc_php_server    | DEBU[0000] [grpc]: started                              
client_nodejs      | Service response
client_nodejs      |  { body: 'Hello' }
```
As you could see it's tremendous simple to start working with gRPC and start thinking of adapting that one to our workflow.

What kind of benefits of adopting GRPC:
1. Easy to understand.
2. Web infrastructure already built on top of HTTP.
3. Great tooling for testing, inspection, and modification.
4. Loose coupling between clients/server makes changes easy.
5. High-quality HTTP implementations in every language.