Skip to main content
Version: 1.53.4

Versionize a Platformatic application

Introduction

Platformatic applications support versioning of the API without the need to deploy a new application for each version. Every time you bump a new API version of your application platformatic calculates the diff between the previous and current versions and maps API routes to the current one. In cases when it's not possible to automatically map the routes, platformatic generates a mapper plugin that can be used to map the routes manually or generated automatically by OpenAI. As a result, you have a single instance of the application that handles all the requests.

Setup platformatic application versioning

Setup the application

Lets start with a basic platformatic service application that can be generated by running create-platformatic command.

npx create-platformatic@latest

Lets add a new route to the application that will be used to test the versioning. Add the following code to the routes/root.js file.

fastify.get('/users', {
schema: {
response: {
200: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'number' },
name: { type: 'string' }
}
}
}
}
}
}, async (request, reply) => {
return [
{ id: 1, name: 'Larry Lute' },
{ id: 2, name: 'Marcus Miller' },
{ id: 3, name: 'Devin Booker' }
]
})

It's important to add the schema property to the route definition. Platformatic uses the OpenAPI schema to find the differences between the versions.

You can start the application and test that the route is working as expected.

npx platformatic@latest start

Testing /users route:

curl http://localhost:3042/users

Response:

[
{
"id": 1,
"name": "Larry Lute"
},
{
"id": 2,
"name": "Marcus Miller"
},
{
"id": 3,
"name": "Devin Booker"
}
]

Bump the first API version

Now when we have a basic application we can bump the first API version. To do that we need to run the following command:

npx platformatic@latest service versions bump

It will create a first version of the application v1 and will map all the routes to the current version. If you start the application, you will see that now the route is available under the /v1/users path. If you want to create a different version name you can use the --version option.

Testing /v1/users route:

curl http://localhost:3042/v1/users
[
{
"id": 1,
"name": "Larry Lute"
},
{
"id": 2,
"name": "Marcus Miller"
},
{
"id": 3,
"name": "Devin Booker"
}
]

API "v1" swagger page Platformatic versions guide: "v1" swagger screenshot

Bump the second API version

Now when we have the first version of the application we can bump the second version. To do that we need to run the same command as before:

npx platformatic@latest service versions bump

It will create a second version of the application v2 and will map all the routes to the current version. If you start the application, you will see that now the same under /v1/users and /v2/users paths. These routes were mapped automatically because the OpenAPI schema was the same for both versions.

Testing /v2/users route:

curl http://localhost:3042/v2/users
[
{
"id": 1,
"name": "Larry Lute"
},
{
"id": 2,
"name": "Marcus Miller"
},
{
"id": 3,
"name": "Devin Booker"
}
]

API "v2" swagger page Platformatic versions guide: "v2" swagger screenshot

Update the second API version

Lets make some changes to the latest version of the application. Lets modify the /users route in a following way:

fastify.get('/users', {
schema: {
response: {
200: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'number' },
firstName: { type: 'string' },
lastName: { type: 'string' }
}
}
}
}
}
}, async (request, reply) => {
return [
{ id: 1, firstName: 'Larry', lastName: 'Lute' },
{ id: 2, firstName: 'Marcus', lastName: 'Miller' },
{ id: 3, firstName: 'Devin', lastName: 'Booker' }
]
})

Now the route returns the firstName and lastName instead of the name. Lets update the second version of the application to reflect the changes. To do that we need to run the following command:

npx platformatic@latest service versions update

Having that now the second version of the route can't be mapped automatically, platformatic will generate a mapper plugin that can be used to map the routes manually or generated automatically by OpenAI. The mapper plugin will be generated in the versions/v1/get-users.js file.

In the mappers plugin you can find a lot of useful information about the changes and code that proxies the "v1" route to the "v2" route. All you need to do is to update the mapOutputData200 function to map the response from the "v2" route to the "v1" route.

function mapOutputData200 (outputData) {
const mappedBody = outputData.responseBody.map((user) => {
return {
id: user.id,
name: `${user.firstName} ${user.lastName}`
}
})

return {
headers: outputData.headers,
responseBody: mappedBody
}
}

Now when you start the application and see that the /v1/users route returns response with the name property when the /v2/users route returns response with the firstName and lastName properties.

Testing /v1/users route:

curl http://localhost:3042/v1/users
[
{
"id": 1,
"name": "Larry Lute"
},
{
"id": 2,
"name": "Marcus Miller"
},
{
"id": 3,
"name": "Devin Booker"
}
]

Platformatic versions guide: "v1" updated swagger screenshot

Testing /v2/users route:

curl http://localhost:3042/v2/users
[
{
"id": 1,
"firstName": "Larry",
"lastName": "Lute"
},
{
"id": 2,
"firstName": "Marcus",
"lastName": "Miller"
},
{
"id": 3,
"firstName": "Devin",
"lastName": "Booker"
}
]

API "v2" swagger page Platformatic versions guide: "v2" updated swagger screenshot

Generating the mapper plugin with OpenAI

Platformatic supports generating the mapper plugin automatically using the OpenAI. Before doing so, make sure you have a platformatic user API key. If not, you can generate it by running the following command:

npx platformatic@latest login

Now when you have the API key you can generate the mapper plugin by running the following command:

npx platformatic@latest service versions update --openai

It will update the same mapper plugin as before but this time it will use OpenAI to generate the request and response mappers. You should always be careful and check the generated code. In my case OpenAI generated the following code for the same mapOutputData200 function:

// !!! This function was generated by OpenAI. Check before use !!!
function mapOutputData200 (output) {
const mappedOutput = {
headers: {},
responseBody: output.responseBody.map(item => ({
id: item.id,
name: `${item.firstName} ${item.lastName}`
}))
}
return mappedOutput
}

As you can see the code is a little bit different but the result is the same. You can use the code generated by OpenAI or write your own.

Shared routes

Sometimes you may want to have a route that is shared between all the versions. For example, you may want to have a /health route that is available under all the versions. To do that you need to set the plugin with this route in the platformatic plugins config section rather than in the versions config section.