This is a great tool for managing multiple versions of different runtimes like Node.js, Python, Ruby, Java, Elixir, Erlang, etc. It let you define a .tool-versions
file in your project root directory and it will automatically switch to that version when you enter the directory. It also allows you to install multiple versions of the same runtime and switch between them on the fly.
This tool is available for Linux, macOS, and Windows. You can find the installation instructions here. It supports also a lot of shells like bash, fish, zsh, and powershell.
It's pretty simple to use. You should install plugin that add support of your favourite tool. For example for node.js
it looks like this:
asdf plugin add nodejs
Then you can install any version of node.js
you want:
asdf install nodejs 16.0.0
And then you can set this version as global:
asdf global nodejs 16.0.0
Or you can set it as local for your project:
asdf local nodejs 16.0.0
More plugin can be found in asdf-community.
You can also define .tool-versions
file in your project root directory and it will automatically switch to that version when you enter the directory. It's pretty useful when you have multiple projects with different versions of the same runtime.
nodejs 16.0.0
python 3.9.5
Or if you don't have such tool installed it will ask you to install it.
asdf
and let you manage your environment variables in .envrc
file.go.mod
file.I'm using this tool for a long time and I'm pretty happy with it. It's a great tool for managing multiple versions of different runtimes. It's also pretty easy to use and it's well documented. I recommend you to try it out. You can find more information on the official website.
]]>Using Prometheus with Next.js can provide valuable insights into the performance and health of your web application. Here are some benefits of integrating Prometheus with Next.js:
To integrate Prometheus with Next.js, you can use the prom-client library. This library provides an easy-to-use API that allows you to instrument your code and collect metrics.
But unfortunately, there is no way to track all HTTP requests without using the Custom Server feature of Next.js. In this article I will use fastify with fastify-metrics library for that.
There list of tools that we will use in this article:
First, we need to create a new Next.js project. We can do this by running the following command:
npx create-next-app@latest
I will use App Router and TypeScript for this project. So, I will select them in the setup wizard.
✔ What is your project named? … next-prometheus-example
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … No
✔ Would you like to customize the default import alias? … No
After the setup is complete, we can start the development server by running the following command:
npm run dev
Now, we can open the application in our browser and see the default Next.js page.
Next.js allows us to create a custom server using the server.js
file. We can use this file to configure our server and add custom routes. In this article, we will use Fastify as our server framework. Unfortunatelly Next.js doesn't support Typescript for custom server, but with some hacks we can make it work. If you want to use plain JavaScript, you can skip this step.
First, let's install the required dependencies:
npm install --save-dev nodemon ts-node
Next let's create server
folder with server.ts
file inside it. We do it to separate our server code from the rest of the application, but it's not required.
mkdir server
touch server/server.ts
Next, we need to create a nodemon.json
file in the root directory of our project. This file will be used by nodemon to run our server.
{
"watch": ["server/**/*.ts"],
"exec": "ts-node --project tsconfig.server.json ./server/server.ts",
"ext": "js ts"
}
Next, we need to create a tsconfig.server.json
file in the root directory of our project. This file will be used by ts-node
to compile our server code.
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "dist",
"lib": ["es2019"],
"target": "es2019",
"isolatedModules": false,
"noEmit": false
},
"include": ["server/**/*.ts"]
}
As last step there is a need to update package.json
file to add new scripts:
{
"scripts": {
"dev": "TS_NODE_PROJECT=tsconfig.server.json nodemon --exec node --inspect -r ts-node/register ./server/server.ts",
"build": "npm run build:server && npm run build:next",
"build:next": "next build",
"build:server": "tsc --project tsconfig.server.json",
"start": "node dist/server/server.js",
"lint": "next lint"
}
}
At the end of this step our project will not be able to start, because we don't have server.ts
file yet. Let's create it.
First, we need to install the required dependencies:
npm install --save fastify
Next, we need to create a server.ts
file in the server
folder. This file will be used to configure our server and add custom routes.
import fastify from 'fastify';
import next from 'next';
import { parse } from 'url';
const port = parseInt(process.env.PORT || '3000', 10);
const isDev = process.env.NODE_ENV !== 'production';
const app = next({ dev: isDev, hostname: 'localhost', port });
const handle = app.getRequestHandler();
const server = fastify({
logger: {
level: isDev ? 'debug' : 'info'
}
});
app.prepare().then(async () => {
server.all('*', async (request, response) => {
return handle(request.raw, response.raw, parse(request.url, true));
});
server
.listen({
port,
host: '0.0.0.0'
})
.then(() => {
console.log('server started');
});
});
Now, we can start the development server by running the following command:
npm run dev
After the server is started, we can open http://localhost:3000
in our browser and see the default Next.js page.
First, we need to install the required dependencies:
npm install --save fastify-metrics
Next, we need to update server.ts
file in the server
folder. This file will be used to configure our server and add custom routes.
import fastify, { FastifyRequest } from 'fastify';
import next from 'next';
import { parse } from 'url';
import metricsPlugin from 'fastify-metrics';
const port = parseInt(process.env.PORT || '3000', 10);
const isDev = process.env.NODE_ENV !== 'production';
const app = next({ dev: isDev, hostname: 'localhost', port });
const handle = app.getRequestHandler();
const server = fastify({
logger: {
level: isDev ? 'debug' : 'info'
}
});
app.prepare().then(async () => {
// Setup prometheus metrics plugin
await server.register(metricsPlugin, {
endpoint: '/api/metrics',
defaultMetrics: {
enabled: true,
labels: {
name: 'next-prometheus-example',
version: '0.1.0'
}
},
routeMetrics: {
groupStatusCodes: true,
routeBlacklist: ['/api/metrics'],
customLabels: {
name: 'next-prometheus-example',
version: '0.1.0'
},
overrides: {
labels: {
// This is a custom label for the route name. It will try to use pathname or urls if not provided.
getRouteLabel: (request: FastifyRequest) => {
if (request.routeConfig.statsId) {
return request.routeConfig.statsId;
}
const parsedUrl = parse(request.url, true);
return parsedUrl.pathname ?? request.url;
}
}
}
}
});
server.all('*', async (request, response) => {
return handle(request.raw, response.raw, parse(request.url, true));
});
server
.listen({
port,
host: '0.0.0.0'
})
.then(() => {
console.log('server started');
});
});
Now, we can start the development server and check the metrics endpoint /api/metrics
. Result should be similar to this:
pino-pretty
to format logs in development moderoute
label by using statsId
property in request. For example, it can be useful to track dynamic routes.routeBlacklist
to exclude some routes from metrics. For example, you can exclude /api/metrics
endpoint.groupStatusCodes
to group status codes. For example, you can group 200
and 201
status codes to 2xx
code.customLabels
to add custom labels to all metrics.In this article, we have learned how to integrate Prometheus with Next.js. We have also seen how to use Prometheus to monitor the performance of our Next.js application. If you want to learn more about Prometheus, check out the official documentation at https://prometheus.io/docs/introduction/overview/.
This is not the best way how to monitor Next.js application, but in some cases it can be useful. Also check the official documentation for Monitoring with OpenTelemetry: https://nextjs.org/docs/app/building-your-application/optimizing/open-telemetry
You can find the code for this article here
]]>From GitHub:
Release Please automates CHANGELOG generation, the creation of GitHub releases, and version bumps for your projects.
It does so by parsing your git history, looking for Conventional Commit messages, and creating release PRs.
It does not handle publication to package managers or handle complex branch management.
In other words it will create a PR with the changes that you made in your project, and it will bump the version of your project based on the changes that you made.
First of all you need to start using Conventional Commit messages, this is a standard for commit messages. It let Release Please know what to add to CHANGELOG and how to bump the version of your project.
I'm using commitlint to enforce this standard in my projects. You can use it with husky or pre-commit to run it before every commit. And also it's good to use it in your CI.
Install husky. There I will show how to do it with npm
, for other package managers you can check the documentation.
npx husky-init && npm install
It will:
Install commitlint and the conventional config.
npm install --save-dev @commitlint/{cli,config-conventional}
# Configure commitlint to use conventional config
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
Add a commit-msg hook to run commitlint on every commit.
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
If you are using pre-commit you can add this to your .pre-commit-config.yaml
file.
repos:
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.4.0
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies:
- '@commitlint/config-conventional'
For GitHub Actions you can use this action.
Example of usage. File .github/workflows/commitlint.yml
.
name: Lint Commit Messages
on: [push, pull_request]
jobs:
commitlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: wagoid/commitlint-github-action@v5
Also you can check documentation for more examples.
I propose to use release-please-action to automate the release process. It's a GitHub Action that will run Release Please on every push to the main branch.
Example file .github/workflows/release-please.yml
:
on:
push:
branches:
- main
permissions:
contents: write
pull-requests: write
name: release-please
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v3
with:
release-type: node
package-name: release-please-action
Do not forget to update the package-name
with the name of your project and the release-type
with the type of your project. You can check the documentation to find supported release types.
fix
or feat
types for your commits to make release happen.Release Please is a great tool to automate the release process of your projects. It's easy to setup and use. I hope this article will help you to start using it in your projects.
]]>