Build and deploy a modular monolith
Introduction
In this guide we'll create a "modular monolith" Library application. It will be a Platformatic Runtime app which contains multiple Platformatic DB and Composer services. We'll learn how to:
- Create and configure a Platformatic Runtime app with multiple services
- Three Platformatic DB services, each with their own databases
- A Platformatic Composer service which aggregates multiple service's REST APIs into a composed API
- Customise the composed API that's automatically generated in a Composer service
- Generate a client for a service's REST API and use it in a Platformatic service to make API requests
- Add custom functionality to a Composer service's composed API by modifying its routes and responses
The architecture for our Library application will look like this:
The complete code for this tutorial is available on GitHub.
Prerequisites
To follow along with this tutorial, you'll need to have this software installed:
- Node.js >= v18.8.0
- npm v7 or later
- A code editor, for example Visual Studio Code.
Create a Platformatic Runtime app: Library app
We're going to start by creating our Library app. This will be a Platformatic Runtime app that contains all of our services.
First, let's run the Platformatic creator wizard in our terminal:
npm create platformatic@latest
And then let's enter the following settings:
- Which kind of project do you want to create?
Runtime
- Where would you like to create your project?
library-app
- Where would you like to load your services from?
services
- Do you want to run npm install?
yes
After the dependencies have been installed, the creator will prompt us to create a service:
Let's create a first service!
We're now going to create a Platformatic DB service named people-service
.
Let's enter the following settings for our new service:
- What is the name of the service?
people-service
- Which kind of project do you want to create?
DB
- What database do you want to use?
SQLite
- Do you want to use the connection string "sqlite://./db.sqlite"?
y
- Do you want to create default migrations?
yes
- Do you want to create a plugin?
no
- Do you want to use TypeScript?
no
- What port do you want to use?
3042
After answering these questions, the creator will create all the files for the people-service
.
Once the creator has finished, our library-app
directory should look like this:
library-app/
├── README.md
├── package.json
├── platformatic.runtime.json
└── services
└── people-service
├── README.md
├── migrations
│ ├── 001.do.sql
│ └── 001.undo.sql
├── package.json
└── platformatic.db.json
Start the Library app
Let's change into the directory that contains our Library app:
cd library-app
And then we can start the app with:
npm start
We'll see a warning message displayed like this in our terminal:
[17:56:00.807] WARN (people-service/8615): No tables found in the database. Are you connected to the right database? Did you forget to run your migrations? This guide can help with debugging Platformatic DB: https://docs.platformatic.dev/docs/guides/debug-platformatic-db
If we open up the API documentation for our People service at http://127.0.0.1:3042/documentation/, we'll also see that it says "No operations defined in spec!"
.
We're seeing these messages because we haven't yet defined a schema for our People database. To fix this, let's go ahead and configure our People service.
Configure the People service
To help us get our People service up and running, we're now going to do the following things:
- Create the People database schema — We'll create an SQL migration that adds the schema for our People database, and then apply it to our database using the Platformatic CLI. When we start our People service, Platformatic DB will automatically generate REST and GraphQL APIs based on our database schema (we'll only be working with the REST one in this tutorial).
- Populate the People database — We'll create a script that can add preset data into our database, and then use the Platformatic CLI to run it. This is commonly referred to as "seeding" the database.
- Test the People service — We'll explore the API documentation for our People service, and then make an HTTP request to one of the REST API routes. This will help us verify that our People database has the correct schema and contains the data that we seeded it with.
Create the People database schema
First, let's open up services/people-service/migrations/001.do.sql
and replace its contents with this SQL:
# services/people-service/migrations/001.do.sql
CREATE TABLE IF NOT EXISTS people (
id INTEGER PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Then let's open up services/people-service/migrations/001.undo.sql
and replace its contents with this SQL:
# services/people-service/migrations/001.undo.sql
DROP TABLE people;
Now in another terminal, let's change into the people-service
directory:
cd services/people-service
And apply our migration:
npx platformatic db migrations apply
Populate the People database
Let's create a new file, services/people-service/seed.js
, and add this code to it:
// services/people-service/seed.js
'use strict'
const people = [
'Stephen King',
'Miranda July',
'Lewis Carroll',
'Martha Schumacher',
'Mick Garris',
'Dede Gardner'
]
module.exports = async function ({ entities, logger }) {
for (const name of people) {
const newPerson = await entities.person.save({ input: { name } })
logger.info({ newPerson }, 'Created person')
}
}
Then let's add an npm run script which uses the Platformatic CLI to run the seed script to the package.json
for our People service:
npm pkg set scripts.seed="platformatic db seed seed.js"
And then let's run it:
npm run seed
We should see output like this from our seed script:
[18:06:05] INFO: seeding from seed.js
Created person: {
id: '1',
name: 'Stephen King',
createdAt: 1687827965773,
updatedAt: 1687827965773
}
Created person: {
id: '2',
name: 'Miranda July',
createdAt: 1687827965778,
updatedAt: 1687827965778
}
...
[18:06:05] INFO: seeding complete
You can learn more about seeding the database for a Platformatic DB app in this guide.
Test the People service
Let's refresh the API documentation page for our People service (http://127.0.0.1:3042/documentation/). We should now see all the /people
API routes that Platformatic DB has automatically generated based on our database schema.
Now we can test our People service API by making a request to it with cURL:
curl localhost:3042/people/
We should receive a response like this:
[{"id":1,"name":"Stephen King","createdAt":"1687827965773","updatedAt":"1687827965773"},{"id":2,"name":"Miranda July","createdAt":"1687827965778","updatedAt":"1687827965778"},{"id":3,"name":"Lewis Carroll","createdAt":"1687827965780","updatedAt":"1687827965780"},{"id":4,"name":"Martha Schumacher","createdAt":"1687827965782","updatedAt":"1687827965782"},{"id":5,"name":"Mick Garris","createdAt":"1687827965784","updatedAt":"1687827965784"},{"id":6,"name":"Dede Gardner","createdAt":"1687827965786","updatedAt":"1687827965786"}]
Create a Platformatic DB service: Books service
We're now going to create a Books service. We'll follow a similar process to the one that we just used to set up our People service.
In the root directory of our Runtime project (library-app
), let's run this command to create the new service:
npx create-platformatic
Then let's enter the following settings:
- What is the name of the service?
books-service
- Which kind of project do you want to create?
DB
- What database do you want to use?
SQLite
- Do you want to use the connection string "sqlite://./db.sqlite"?
y
- Do you want to create default migrations?
yes
- Do you want to create a plugin?
no
- Do you want to use TypeScript?
no
- What port do you want to use?
3043
- Do you want to apply migrations?
no
- Do you want to generate types?
yes
Once the command has finished running, we should see that a Platformatic DB service has been created for us in the services/books-service/
directory.
Create the Books database schema
Now we're going to create a migration that adds the schema for our Books database.
First, let's open up services/books-service/migrations/001.do.sql
and replace its contents with this SQL:
# services/books-service/migrations/001.do.sql
CREATE TABLE IF NOT EXISTS books (
id INTEGER PRIMARY KEY,
title VARCHAR(255) NOT NULL,
author_id INTEGER NOT NULL,
published_year INTEGER NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Then let's open up services/books-service/migrations/001.undo.sql
and replace its contents with this SQL:
# services/books-service/migrations/001.undo.sql
DROP TABLE books;
Now we'll change into the books-service
directory:
cd services/books-service
And apply our migration:
npx platformatic db migrations apply