{ "version": "https://jsonfeed.org/version/1", "title": "Ilya Amelevich's Blog", "home_page_url": "https://iamelevich.dev", "feed_url": "https://iamelevich.dev/feed/json", "description": "My name is Ilya and I'm software engineer. I'm passionate about web development and open source. I'm also a big fan of Node.js, TypeScript, and Golang.", "icon": "https://iamelevich.dev/og?square=true", "author": { "name": "Ilya Amelevich", "url": "https://iamelevich.dev" }, "items": [ { "id": "asdf-version-management-tool", "content_html": "
I'm using asdf-vm for managing multiple runtime versions with a single CLI tool since 2022.
\nThis 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.
\nIt'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\n
\nThen you can install any version of node.js
you want:
asdf install nodejs 16.0.0\n
\nAnd then you can set this version as global:
\nasdf global nodejs 16.0.0\n
\nOr you can set it as local for your project:
\nasdf local nodejs 16.0.0\n
\nMore plugin can be found in asdf-community.
\nYou 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\npython 3.9.5\n
\nOr if you don't have such tool installed it will ask you to install it.
\nasdf
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.
", "url": "https://iamelevich.dev/blog/asdf-version-management-tool", "title": "asdf-vm - Manage multiple runtime versions with a single CLI tool", "summary": "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.", "image": "https://iamelevich.dev/og?square=true&title=asdf-vm%20-%20Manage%20multiple%20runtime%20versions%20with%20a%20single%20CLI%20tool", "date_modified": "2023-07-23T15:00:00.000Z", "author": { "name": "Ilya Amelevich", "url": "https://iamelevich.dev" } }, { "id": "nextjs-with-prometheus-metrics", "content_html": "Next.js is a popular React framework that allows for server-side rendering and static site generation. It is used to build production-grade web applications with ease. Prometheus, on the other hand, is an open-source monitoring system that collects metrics from various sources and stores them in a time-series database.
\nUsing 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:
\nTo 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.
\nBut 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.
\nThere list of tools that we will use in this article:
\nFirst, we need to create a new Next.js project. We can do this by running the following command:
\nnpx create-next-app@latest\n
\nI will use App Router and TypeScript for this project. So, I will select them in the setup wizard.
\n✔ What is your project named? … next-prometheus-example\n✔ Would you like to use TypeScript? … Yes\n✔ Would you like to use ESLint? … Yes\n✔ Would you like to use Tailwind CSS? … Yes\n✔ Would you like to use `src/` directory? … Yes\n✔ Would you like to use App Router? (recommended) … No\n✔ Would you like to customize the default import alias? … No\n
\nAfter the setup is complete, we can start the development server by running the following command:
\nnpm run dev\n
\nNow, we can open the application in our browser and see the default Next.js page.
\n\nNext.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:
\nnpm install --save-dev nodemon ts-node\n
\nNext 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\ntouch server/server.ts\n
\nNext, 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.
{\n \"watch\": [\"server/**/*.ts\"],\n \"exec\": \"ts-node --project tsconfig.server.json ./server/server.ts\",\n \"ext\": \"js ts\"\n}\n
\nNext, 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.
{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"module\": \"commonjs\",\n \"outDir\": \"dist\",\n \"lib\": [\"es2019\"],\n \"target\": \"es2019\",\n \"isolatedModules\": false,\n \"noEmit\": false\n },\n \"include\": [\"server/**/*.ts\"]\n}\n
\nAs last step there is a need to update package.json
file to add new scripts:
{\n \"scripts\": {\n \"dev\": \"TS_NODE_PROJECT=tsconfig.server.json nodemon --exec node --inspect -r ts-node/register ./server/server.ts\",\n \"build\": \"npm run build:server && npm run build:next\",\n \"build:next\": \"next build\",\n \"build:server\": \"tsc --project tsconfig.server.json\",\n \"start\": \"node dist/server/server.js\",\n \"lint\": \"next lint\"\n }\n}\n
\nAt 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:
\nnpm install --save fastify\n
\nNext, 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';\nimport next from 'next';\nimport { parse } from 'url';\n\nconst port = parseInt(process.env.PORT || '3000', 10);\nconst isDev = process.env.NODE_ENV !== 'production';\nconst app = next({ dev: isDev, hostname: 'localhost', port });\nconst handle = app.getRequestHandler();\nconst server = fastify({\n logger: {\n level: isDev ? 'debug' : 'info'\n }\n});\n\napp.prepare().then(async () => {\n server.all('*', async (request, response) => {\n return handle(request.raw, response.raw, parse(request.url, true));\n });\n server\n .listen({\n port,\n host: '0.0.0.0'\n })\n .then(() => {\n console.log('server started');\n });\n});\n
\nNow, we can start the development server by running the following command:
\nnpm run dev\n
\nAfter 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:
\nnpm install --save fastify-metrics\n
\nNext, 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';\nimport next from 'next';\nimport { parse } from 'url';\nimport metricsPlugin from 'fastify-metrics';\n\nconst port = parseInt(process.env.PORT || '3000', 10);\nconst isDev = process.env.NODE_ENV !== 'production';\nconst app = next({ dev: isDev, hostname: 'localhost', port });\nconst handle = app.getRequestHandler();\nconst server = fastify({\n logger: {\n level: isDev ? 'debug' : 'info'\n }\n});\n\napp.prepare().then(async () => {\n // Setup prometheus metrics plugin\n await server.register(metricsPlugin, {\n endpoint: '/api/metrics',\n defaultMetrics: {\n enabled: true,\n labels: {\n name: 'next-prometheus-example',\n version: '0.1.0'\n }\n },\n routeMetrics: {\n groupStatusCodes: true,\n routeBlacklist: ['/api/metrics'],\n customLabels: {\n name: 'next-prometheus-example',\n version: '0.1.0'\n },\n overrides: {\n labels: {\n // This is a custom label for the route name. It will try to use pathname or urls if not provided.\n getRouteLabel: (request: FastifyRequest) => {\n if (request.routeConfig.statsId) {\n return request.routeConfig.statsId;\n }\n const parsedUrl = parse(request.url, true);\n return parsedUrl.pathname ?? request.url;\n }\n }\n }\n }\n });\n\n server.all('*', async (request, response) => {\n return handle(request.raw, response.raw, parse(request.url, true));\n });\n server\n .listen({\n port,\n host: '0.0.0.0'\n })\n .then(() => {\n console.log('server started');\n });\n});\n
\nNow, 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/.
\nThis 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
\nYou can find the code for this article here
", "url": "https://iamelevich.dev/blog/nextjs-with-prometheus-metrics", "title": "Next.js with Prometheus", "summary": "How to use Prometheus with Next.js", "image": "https://iamelevich.dev/og?square=true&title=Next.js%20with%20Prometheus", "date_modified": "2023-07-23T22:00:00.000Z", "author": { "name": "Ilya Amelevich", "url": "https://iamelevich.dev" } }, { "id": "automate-release-process-with-release-please", "content_html": "In this article I will try to explain how to automate the release process of your projects with Release Please and some tools that I'm using with it.
\nFrom GitHub:
\n\n\nRelease Please automates CHANGELOG generation, the creation of GitHub releases, and version bumps for your projects.
\n
\n\nIt does so by parsing your git history, looking for Conventional Commit messages, and creating release PRs.
\n
\n\nIt does not handle publication to package managers or handle complex branch management.
\n
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.
\nFirst 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.
\nI'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.
\nInstall 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\n
\nIt will:
\nInstall commitlint and the conventional config.
\nnpm install --save-dev @commitlint/{cli,config-conventional}\n\n# Configure commitlint to use conventional config\necho \"module.exports = {extends: ['@commitlint/config-conventional']}\" > commitlint.config.js\n
\nAdd a commit-msg hook to run commitlint on every commit.
\nnpx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'\n
\nIf you are using pre-commit you can add this to your .pre-commit-config.yaml
file.
repos:\n - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook\n rev: v9.4.0\n hooks:\n - id: commitlint\n stages: [commit-msg]\n additional_dependencies:\n - '@commitlint/config-conventional'\n
\nFor GitHub Actions you can use this action.
\nExample of usage. File .github/workflows/commitlint.yml
.
name: Lint Commit Messages\non: [push, pull_request]\n\njobs:\n commitlint:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n with:\n fetch-depth: 0\n - uses: wagoid/commitlint-github-action@v5\n
\nAlso you can check documentation for more examples.
\nI 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.
\nExample file .github/workflows/release-please.yml
:
on:\n push:\n branches:\n - main\n\npermissions:\n contents: write\n pull-requests: write\n\nname: release-please\n\njobs:\n release-please:\n runs-on: ubuntu-latest\n steps:\n - uses: google-github-actions/release-please-action@v3\n with:\n release-type: node\n package-name: release-please-action\n
\nDo 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.
", "url": "https://iamelevich.dev/blog/automate-release-process-with-release-please", "title": "Automate release process with Release Please", "summary": "Release Please automates CHANGELOG generation, the creation of GitHub releases, and version bumps for your projects.", "image": "https://iamelevich.dev/og?square=true&title=Automate%20release%20process%20with%20Release%20Please", "date_modified": "2023-07-24T01:00:00.000Z", "author": { "name": "Ilya Amelevich", "url": "https://iamelevich.dev" } } ] }