Skip to content

Microservice HTTP Middleware

Dealing with monolithic applications can be a challenging task, especially when it comes to upgrading specific code portions or databases. The microservices architecture pattern offers a solution by logically separating a traditional application into multiple services. However, the transition from a monolithic structure to a microservices architecture requires effective development practices and seamless migration strategies.

Metal provides specialized features to tackle database communications through its HTTP middleware.

In this use case, we demonstrate deploying the Metal server as an HTTP middleware, consolidating multiple Database Management Systems (DBMS) as if they were a unified database.

ℹ️ INFO

As of now, Metal Server exclusively supports CRUD operations.

Prerequisites

Before testing this use case, make sure that the sample project is deployed and ready. (Refer to: Sample project)

Presentation

Imagine an application communicating with the Metal server as an HTTP API bridge with a Postgres Database:

Here, clubdata is a Postgres Database containing tables: members, facilities, and bookings.

After separating your application into multiple services, one for each table, you are still essentially addressing the same DBMS at the backend:

At this point, even if the database is split into multiple Postgres servers, each one holding a table, the configuration becomes straightforward:

This configuration is easily manageable with Metal Server.

In the following, we assume these DBMS configurations:

HostPortUserPasswordDatabaseTables
pg-clubdata-members5432admin123456clubdatamembers
pg-clubdata-facilities5432admin123456clubdatafacilities
pg-clubdata-bookings5432admin123456clubdatabookings

The goal is to expose the tables members, facilities, and bookings through an HTTP API using Metal.

Configuration

Let's start with a minimal Metal configuration file, config.yml:

yaml
version: "0.2"
server:
  port: 3000
  authentication:
  request-limit: 100mb

In this configuration, we:

  • Set the Metal server port to 3000/TCP with port: 3000
  • Enable authentication with authentication:
  • Limit the maximum response size to 100 Mbytes with request-limit: 100mb

Add a users section with the user myapiuser:

yaml
users:
  myapiuser: myStr@ngpa$$w0rd

Include a sources section with the sources pg-clubdata-members, pg-clubdata-facilities, and pg-clubdata-bookings to connect to their databases clubdata:

yaml
sources:
  pg-clubdata-bookings:
    provider: postgres
    host: pg-clubdata-bookings
    port: 5432
    user: admin
    password: "123456"
    database: clubdata
  pg-clubdata-facilities:
    provider: postgres
    host: pg-clubdata-facilities
    port: 5432
    user: admin
    password: "123456"
    database: clubdata
  pg-clubdata-members:
    provider: postgres
    host: pg-clubdata-members
    port: 5432
    user: admin
    password: "123456"
    database: clubdata

To expose through an HTTP API, add a schema section with the schema clubdata that points to the tables members, facilities, and bookings from their sources:

yaml
schemas:
  clubdata:
    entities:
      members:
        sourceName: pg-clubdata-members
        entityName: members
      facilities:
        sourceName: pg-clubdata-facilities
        entityName: facilities
      bookings:
        sourceName: pg-clubdata-bookings
        entityName: bookings

With the entities command, we define tables to expose and their exposed names.

The final configuration will be:

yaml
version: "0.2"
server:
  port: 3000
  authentication:
  request-limit: 100mb
users:
  myapiuser: myStr@ngpa$$w0rd
sources:
  pg-clubdata-bookings:
    provider: postgres
    host: pg-clubdata-bookings
    port: 5432
    user: admin
    password: "123456"
    database: clubdata
  pg-clubdata-facilities:
    provider: postgres
    host: pg-clubdata-facilities
    port: 5432
    user: admin
    password: "123456"
    database: clubdata
  pg-clubdata-members:
    provider: postgres
    host: pg-clubdata-members
    port: 5432
    user: admin
    password: "123456"
    database: clubdata
schemas:
  clubdata:
    entities:
      members:
        sourceName: pg-clubdata-members
        entityName: members
      facilities:
        sourceName: pg-clubdata-facilities
        entityName: facilities
      bookings:
        sourceName: pg-clubdata-bookings
        entityName: bookings

With the configuration set, restart the Metal server:

bash
docker-compose restart metal

Playing with the HTTP API

To test the HTTP API, begin by logging in:

bash
curl --request POST \
  --url http://localhost:3000/user/login \
  --header 'content-type: application/json' \
  --data '{"username":"myapiuser","password": "myStr@ngpa$$w0rd"}'

You should receive a response with a token:

JSON
{
  "token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im15YXBpdXNlciIsImlhdCI6MTcwMDY0NzM4OCwiZXhwIjoxNzAwNjUwOTg4fQ.lcRcJBOWC6BiYL

pR2EiWNWwWSyyLIoMSnzVHAy3SWlE"
}

Then, select data from the "members" table using the provided token after the "Bearer" prefix:

bash
curl --request GET \
  --url http://localhost:3000/schema/clubdata/members \
  --header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im15YXBpdXNlciIsImlhdCI6MTcwMDY0NzM4OCwiZXhwIjoxNzAwNjUwOTg4fQ.lcRcJBOWC6BiYLpR2EiWNWwWSyyLIoMSnzVHAy3SWlE' \
  --header 'content-type: application/json'

You should receive the following response:

JSON
{
  "schemaName": "clubdata",
  "entityName": "members",
  "transaction": "select",
  "result": "OK",
  "status": 200,
  "metadata": {},
  "fields": {
    "memid": "number",
    "surname": "string",
    "firstname": "string",
    "address": "string",
    "zipcode": "number",
    "telephone": "string",
    "recommendedby": "object",
    "joindate": "object"
  },
  "rows": [
    {
      "memid": 0,
      "surname": "GUEST",
      "firstname": "GUEST",
      "address": "GUEST",
      "zipcode": 0,
      "telephone": "(000) 000-0000",
      "recommendedby": null,
      "joindate": "2012-07-01T00:00:00.000Z"
    },
    {
      "memid": 1,
      "surname": "Smith",
      "firstname": "Darren",
      "address": "8 Bloomsbury Close, Boston",
      "zipcode": 4321,
      "telephone": "555-555-5555",
      "recommendedby": null,
      "joindate": "2012-07-02T12:02:05.000Z"
    },
    {
      "memid": 2,
      "surname": "Smith",
      "firstname": "Tracy",
      "address": "8 Bloomsbury Close, New York",
      "zipcode": 4321,
      "telephone": "555-555-5555",
      "recommendedby": null,
      "joindate": "2012-07-02T12:08:23.000Z"
    },
    ...
  ]
}

Then, select data from the "facilities" table using the provided token after the "Bearer" prefix:

bash
curl --request GET \
  --url http://localhost:3000/schema/clubdata/facilities \
  --header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im15YXBpdXNlciIsImlhdCI6MTcwMDY0NzM4OCwiZXhwIjoxNzAwNjUwOTg4fQ.lcRcJBOWC6BiYLpR2EiWNWwWSyyLIoMSnzVHAy3SWlE' \
  --header 'content-type: application/json'

You should receive the following response:

JSON
{
  "schemaName": "clubdata",
  "entityName": "facilities",
  "transaction": "select",
  "result": "OK",
  "status": 200,
  "metadata": {},
  "fields": {
    "facid": "number",
    "name": "string",
    "membercost": "string",
    "guestcost": "string",
    "initialoutlay": "string",
    "monthlymaintenance": "string"
  },
  "rows": [
    {
      "facid": 0,
      "name": "Tennis Court 1",
      "membercost": "5",
      "guestcost": "25",
      "initialoutlay": "10000",
      "monthlymaintenance": "200"
    },
    {
      "facid": 1,
      "name": "Tennis Court 2",
      "membercost": "5",
      "guestcost": "25",
      "initialoutlay": "8000",
      "monthlymaintenance": "200"
    },
    {
      "facid": 2,
      "name": "Badminton Court",
      "membercost": "0",
      "guestcost": "15.5",
      "initialoutlay": "4000",
      "monthlymaintenance": "50"
    },
    ...
  ]
}

Then, select data from the "bookings" table using the provided token after the "Bearer" prefix:

bash
curl --request GET \
  --url http://localhost:3000/schema/clubdata/bookings \
  --header 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im15YXBpdXNlciIsImlhdCI6MTcwMDY0NzM4OCwiZXhwIjoxNzAwNjUwOTg4fQ.lcRcJBOWC6BiYLpR2EiWNWwWSyyLIoMSnzVHAy3SWlE' \
  --header 'content-type: application/json'

You should receive the following response:

JSON
{
  "schemaName": "clubdata",
  "entityName": "bookings",
  "transaction": "select",
  "result": "OK",
  "status": 200,
  "metadata": {},
  "fields": {
    "bookid": "number",
    "facid": "number",
    "memid": "number",
    "starttime": "object",
    "slots": "number"
  },
  "rows": [
    {
      "bookid": 0,
      "facid": 3,
      "memid": 1,
      "starttime": "2012-07-03T11:00:00.000Z",
      "slots": 2
    },
    {
      "bookid": 1,
      "facid": 4,
      "memid": 1,
      "starttime": "2012-07-03T08:00:00.000Z",
      "slots": 2
    },
    {
      "bookid": 2,
      "facid": 6,
      "memid": 0,
      "starttime": "2012-07-03T18:00:00.000Z",
      "slots": 2
    },
    ...
  ]
}

Feel free to explore and interact with the HTTP API using the provided example.

For additional details and comprehensive information, please consult the REST API documentation.

Released under the GNU v3 License.