outmoded / discuss

The "mailing list"
99 stars 9 forks source link

Miroservices using Hapi.js ? #331

Closed sathishsoundharajan closed 5 years ago

sathishsoundharajan commented 8 years ago

Hi Folks, Does someone have an idea of how to rewrite my monolithic Hapi.js Application into Microservice ??

My App uses 2 databases,

  1. SQL -- A Relational module API (Custom Reviews and Ratings, Comments) & Transaction related
  2. Redis -- Caching.

I googled a lot about Microservice and its pattern, seen examples using Express & Docker. Incase of express, every API is an express application with Associated docker container.

My Questions are

  1. Do anyone have example / recommended way of building a Microservice using Hapi.js ?
  2. How can I make use of Hapi's plugin mechanism in Microservice pattern ?
  3. About database connections, say that I have 10 API's 5 needs access to SQL and 3 needs access to Redis and SQL, 2 needs only Redis access, Say all the API's are written as separate Hapi Application like they do for express, so in that case In every App I need to open a database connection and close that connection, that would cause overhead right ? Or Is there way to share database connection across the Application ?
  4. May be Sample Project structure? Can be Helpful.
devinivy commented 8 years ago

I can elaborate on this later, but here are some initial thoughts.

hapi plugins cater to microservices because if they're written well they can trivially be deployed together or separately. Often it's desirable to deploy them together until they need to scale independently. Each plugin can represent a service. You compose your server by attaching those services/plugins to hapi connections. In order to achieve this, each plugin should rely almost exclusively on plugin options for configuration.

Sharing DB connections and other resources is a very important thing. The general approach I take is for each service/plugin to declare the resources it needs (likely using a server decoration). Then during server initialization (the onPreStart server extension) those resources are doled-out. At that point it's possible that multiple plugins/services share the same resource. A reasonable example of this can be seen in a plugin I wrote named dogwater.

A note on caching. I suggest using catbox for caching. Write cacheable routines as hapi server methods. Server methods can be configured to cache results in various different ways. The caches themselves will be defined in your server configuration, and each will have its own name. If you want your services (each of which defining its own server methods) to use different caches, pass the names of those caches into the plugin as plugin options. Keep your plugin's code and your server's configuration decoupled in this way so that the service cares how it's configured but not how it's deployed.

Here's a boilerplate (somewhat unfinished) I often use that has a good separation between server and plugin: https://github.com/devinivy/boilerplate-api. The server only exists to help with development or for a reasonable standalone deployment.

lloydbenson commented 8 years ago

Not sure if this is useful but @geek and I just put together a workshop on microservices which we recently gave at midwestjs. Here is the workshop if you are interested. Some of the examples are hapi but this may be what you are looking for. https://github.com/lloydbenson/microservices-workshop.

devinivy commented 8 years ago

Yeah, not sure how I failed to mention seneca and the chairo plugin!

sathishsoundharajan commented 8 years ago

@devinivy Thanks for the breif explanation. @lloydbenson Thanks for the workshop.

  1. Even though i write an API as plugin, it should be deployed as separate Hapi App ?
  2. About senecajs, I am not able to understand what actually it is doing.
    • It Provides high level API's.
    • How the App should be deployed finally.
    • How it shares the db connection.

There are some new patterns I've been playing with that make building large apps easier. The problem is to find the time to write about it. For example, I use a process runner to create the hapi server and load plugins into it, where each plugin is created as a connectionless plugin that creates its own internal connection. By @hueniverse

I got this info from him on via Sideway Hapijs Q/A session.

I was more keen on how he was able to separate and share those plugins.

P.S: I have lot question about senecajs. Need to look on it more.

Stradivario commented 6 years ago

Hello guys please take a look at this project it is using Hapi internally and you can add Hapi plugins to it

It is based on GraphQL and it can help you to work with microservices and merge GraphQL Schema from all microservicess added.

@gapi namespace: https://github.com/Stradivario/gapi

@gapi microservices starter project https://github.com/Stradivario/gapi-starter-microservices

example microservice https://github.com/Stradivario/gapi-starter-microservices/blob/master/src/app/microservices/findUser.microservice.ts

import {
  GraphQLInt,
  GraphQLNonNull,
  GapiController,
  Type,
  Query,
  GapiModule,
  Bootstrap,
  ConfigService,
  GapiServerModule,
  Container
} from '@gapi/core';
import { UserType } from '../types/user.type';
import { UserService } from '../core/services/user/user.service';
import { CoreModule } from '../core/core.module';

@GapiController()
export class UserQueriesController {

  constructor(
    private userService: UserService
  ) {}

  @Type(UserType)
  @Query({
    id: {
      type: new GraphQLNonNull(GraphQLInt)
    }
  })
  findUser(root, { id }, context): UserType {
    return this.userService.findUser(id);
  }
}

@GapiModule({
  imports: [
    CoreModule,
    GapiServerModule.forRoot({
      ...Container.get(ConfigService).APP_CONFIG,
      port: 10000
    })
  ],
  controllers: [UserQueriesController]
})
export class AppModule {}

example Hapi Plugin can be found here https://github.com/Stradivario/gapi-voyager/blob/master/development/gapi-voyager-plugin.ts

import { GapiVoyagerConfig } from "./gapi-voyager-config";
import { GapiModule, GapiHapiPlugin, GapiHapiPluginInterface } from '@gapi/core';
import renderVoyagerPage from 'graphql-voyager/middleware/render-voyager-page';

@GapiHapiPlugin()
export class VoyagerGapiHapiPlugin implements GapiHapiPluginInterface {
    name = 'VoyagerGapiHapiPlugin';
    version = '1.0.0';

    constructor(
        private config: GapiVoyagerConfig
    ) {}

    async register(server, options) {
        server.route({
            method: 'GET',
            path: this.config.path,
            handler: this.handler.bind(this)
        });
    }

    async handler(_request, reply) {
        return await renderVoyagerPage({ endpointUrl: this.config.endpointUrl });
    }

}

Regards, Kristiyan Tachev