{"id":1980,"date":"2025-02-21T07:02:29","date_gmt":"2025-02-21T07:02:29","guid":{"rendered":"https:\/\/mailitics.com\/index.php\/2025\/02\/21\/how-to-use-an-llm-powered-boilerplate-for-building-your-own-node-js-api\/"},"modified":"2025-02-21T07:02:29","modified_gmt":"2025-02-21T07:02:29","slug":"how-to-use-an-llm-powered-boilerplate-for-building-your-own-node-js-api","status":"publish","type":"post","link":"https:\/\/mailitics.com\/index.php\/2025\/02\/21\/how-to-use-an-llm-powered-boilerplate-for-building-your-own-node-js-api\/","title":{"rendered":"How to Use an LLM-Powered Boilerplate for Building Your Own Node.js API"},"content":{"rendered":"<p>    How to Use an LLM-Powered Boilerplate for Building Your Own Node.js API<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<p class=\"wp-block-paragraph\" id=\"76cf\">For a long time, one of the common ways to start new Node.js projects was using boilerplate templates. These templates help developers reuse familiar code structures and implement standard features, such as access to cloud file storage. With the latest developments in LLM, project boilerplates appear to be more useful than ever.<\/p>\n<p class=\"wp-block-paragraph\" id=\"bd99\">Building on this progress, I\u2019ve extended my existing Node.js <a href=\"https:\/\/towardsdatascience.com\/tag\/api\/\" title=\"API\">API<\/a> boilerplate with a new tool\u00a0<a href=\"https:\/\/github.com\/vyancharuk\/nodejs-todo-api-boilerplate\" rel=\"noreferrer noopener\" target=\"_blank\">LLM Codegen<\/a>. This standalone feature enables the boilerplate to automatically generate module code for any purpose based on text descriptions. The generated module comes complete with E2E tests, database migrations, seed data, and necessary business logic.<\/p>\n<h3 class=\"wp-block-heading\" id=\"2bc6\">History<\/h3>\n<p class=\"wp-block-paragraph\" id=\"d587\">I initially created a\u00a0<a href=\"https:\/\/github.com\/vyancharuk\/nodejs-todo-api-boilerplate#nodejs-api-typescript-template-project\" rel=\"noreferrer noopener\" target=\"_blank\">GitHub repository<\/a>\u00a0for a Node.js API boilerplate to consolidate the best practices I\u2019ve developed over the years. Much of the implementation is based on code from a real Node.js API running in production on AWS.<\/p>\n<p class=\"wp-block-paragraph\" id=\"94fa\">I am passionate about vertical slicing architecture and Clean Code principles to keep the codebase maintainable and clean. With recent advancements in LLM, particularly its support for large contexts and its ability to generate high-quality code, I decided to experiment with generating clean TypeScript code based on my boilerplate. This boilerplate follows specific structures and patterns that I believe are of high quality. The key question was whether the generated code would follow the same patterns and structure. Based on my findings, it does.<\/p>\n<p class=\"wp-block-paragraph\" id=\"5302\">To recap, here\u2019s a quick highlight of the Node.js API boilerplate\u2019s key features:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">Vertical slicing architecture based on\u00a0<code>DDD<\/code>\u00a0&amp;\u00a0<code>MVC<\/code>\u00a0principles<\/li>\n<li class=\"wp-block-list-item\">Services input validation using\u00a0<code>ZOD<\/code>\n<\/li>\n<li class=\"wp-block-list-item\">Decoupling application components with dependency injection (<code>InversifyJS<\/code>)<\/li>\n<li class=\"wp-block-list-item\">Integration and\u00a0<code>E2E<\/code>\u00a0testing with Supertest<\/li>\n<li class=\"wp-block-list-item\">Multi-service setup using\u00a0<code>Docker<\/code>compose<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\" id=\"d58b\">Over the past month, I\u2019ve spent my weekends formalizing the solution and implementing the necessary code-generation logic. Below, I\u2019ll share the details.<\/p>\n<h3 class=\"wp-block-heading\" id=\"c3d7\">Implementation Overview<\/h3>\n<p class=\"wp-block-paragraph\" id=\"dfbe\">Let\u2019s explore the specifics of the implementation. All <a href=\"https:\/\/towardsdatascience.com\/tag\/code-generation\/\" title=\"Code Generation\">Code Generation<\/a> logic is organized at the project root level, inside the\u00a0<code>llm-codegen<\/code>\u00a0folder, ensuring easy navigation. The Node.js boilerplate code has no dependency on\u00a0<code>llm-codegen<\/code>, so it can be used as a regular template without modification.<\/p>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" data-dominant-color=\"1f1f1f\" data-has-transparency=\"false\" style=\"--dominant-color: #1f1f1f;\" loading=\"lazy\" decoding=\"async\" width=\"876\" height=\"882\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_ohPuqfZMXGgwAiGIzNLyBg-1.webp?resize=876%2C882&#038;ssl=1\" alt=\"\" class=\"wp-image-598251 not-transparent\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_ohPuqfZMXGgwAiGIzNLyBg-1.webp 876w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_ohPuqfZMXGgwAiGIzNLyBg-1-298x300.webp 298w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_ohPuqfZMXGgwAiGIzNLyBg-1-150x150.webp 150w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_ohPuqfZMXGgwAiGIzNLyBg-1-768x773.webp 768w\" sizes=\"auto, (max-width: 876px) 100vw, 876px\"><figcaption class=\"wp-element-caption\">LLM-Codegen folder structure<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\" id=\"4700\">It covers the following use cases:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">Generating clean, well-structured code for new module based on input description. The generated module becomes part of the Node.js REST API application.<\/li>\n<li class=\"wp-block-list-item\">Creating database migrations and extending seed scripts with basic data for the new module.<\/li>\n<li class=\"wp-block-list-item\">Generating and fixing E2E tests for the new code and ensuring all tests pass.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\" id=\"f51e\">The generated code after the first stage is clean and adheres to vertical slicing architecture principles. It includes only the necessary business logic for CRUD operations. Compared to other code generation approaches, it produces clean, maintainable, and compilable code with valid E2E tests.<\/p>\n<p class=\"wp-block-paragraph\" id=\"2677\">The second use case involves generating DB migration with the appropriate schema and updating the seed script with the necessary data. This task is particularly well-suited for LLM, which handles it exceptionally well.<\/p>\n<p class=\"wp-block-paragraph\" id=\"79b3\">The final use case is generating E2E tests, which help confirm that the generated code works correctly. During the running of E2E tests, an SQLite3 database is used for migrations and seeds.<\/p>\n<p class=\"wp-block-paragraph\" id=\"9ac6\">Mainly supported LLM clients are OpenAI and Claude.<\/p>\n<h3 class=\"wp-block-heading\" id=\"a5d8\"><strong>How to Use It<\/strong><\/h3>\n<p class=\"wp-block-paragraph\" id=\"bf34\">To get started, navigate to the root folder\u00a0<code>llm-codegen<\/code>\u00a0and install all dependencies by running:<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\" id=\"0edd\">npm i<\/p>\n<\/blockquote>\n<p class=\"wp-block-paragraph\" id=\"9a19\"><code>llm-codegen<\/code>\u00a0does not rely on Docker or any other heavy third-party dependencies, making setup and execution easy and straightforward. Before running the tool, ensure that you set at least one\u00a0<code>*_API_KEY<\/code>\u00a0environment variable in the\u00a0<code>.env<\/code>\u00a0file with the appropriate API key for your chosen LLM provider. All supported environment variables are listed in the\u00a0<code>.env.sample<\/code>\u00a0file (<code>OPENAI_API_KEY, CLAUDE_API_KEY<\/code>\u00a0etc.) You can use\u00a0<code>OpenAI<\/code>,\u00a0<code>Anthropic Claude<\/code>, or\u00a0<code>OpenRouter LLaMA<\/code>. As of mid-December,\u00a0<code>OpenRouter LLaMA<\/code>\u00a0is surprisingly free to use. It\u2019s possible to register\u00a0<a href=\"https:\/\/openrouter.ai\/nousresearch\/hermes-3-llama-3.1-405b:free\/api\" rel=\"noreferrer noopener\" target=\"_blank\">here<\/a>\u00a0and obtain a token for free usage. However, the output quality of this free LLaMA model could be improved, as most of the generated code fails to pass the compilation stage.<\/p>\n<p class=\"wp-block-paragraph\" id=\"c588\">To start\u00a0<code>llm-codegen<\/code>, run the following command:<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\" id=\"188e\">npm run start<\/p>\n<\/blockquote>\n<p class=\"wp-block-paragraph\" id=\"7c36\">Next, you\u2019ll be asked to input the module description and name. In the module description, you can specify all necessary requirements, such as entity attributes and required operations. The core remaining work is performed by micro-agents:\u00a0<code>Developer<\/code>,\u00a0<code>Troubleshooter<\/code>, and\u00a0<code>TestsFixer<\/code>.<\/p>\n<p class=\"wp-block-paragraph\" id=\"c57d\">Here is an example of a successful code generation:<\/p>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" data-dominant-color=\"17191e\" data-has-transparency=\"false\" style=\"--dominant-color: #17191e;\" loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"541\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_JUVXzdX-VUqZ-yybWGKH4g-1.gif?resize=960%2C541&#038;ssl=1\" alt=\"\" class=\"wp-image-598252 not-transparent\"><figcaption class=\"wp-element-caption\">Successful code generation<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">Below is another example demonstrating how a compilation error was fixed:<\/p>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" data-dominant-color=\"17191e\" data-has-transparency=\"false\" style=\"--dominant-color: #17191e;\" loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"541\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_P47LS1S6XuHC_gC1CigmzQ-1.gif?resize=960%2C541&#038;ssl=1\" alt=\"\" class=\"wp-image-598253 not-transparent\"><\/figure>\n<p class=\"wp-block-paragraph\">The following is an example of a generated\u00a0<code>orders<\/code>\u00a0module code:<\/p>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" data-dominant-color=\"1c1e1d\" data-has-transparency=\"false\" style=\"--dominant-color: #1c1e1d;\" loading=\"lazy\" decoding=\"async\" width=\"864\" height=\"850\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_QS1mvxG6Mbh2bA4zorf1oA-1.webp?resize=864%2C850&#038;ssl=1\" alt=\"\" class=\"wp-image-598254 not-transparent\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_QS1mvxG6Mbh2bA4zorf1oA-1.webp 864w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_QS1mvxG6Mbh2bA4zorf1oA-1-300x295.webp 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_QS1mvxG6Mbh2bA4zorf1oA-1-768x756.webp 768w\" sizes=\"auto, (max-width: 864px) 100vw, 864px\"><\/figure>\n<p class=\"wp-block-paragraph\" id=\"efda\">A key detail is that you can generate code step by step, starting with one module and adding others until all required APIs are complete. This approach allows you to generate code for all required modules in just a few command runs.<\/p>\n<h3 class=\"wp-block-heading\" id=\"dc78\"><strong>How It Works<\/strong><\/h3>\n<p class=\"wp-block-paragraph\" id=\"ced9\">As mentioned earlier, all work is performed by those micro-agents:\u00a0<code>Developer<\/code>,\u00a0<code>Troubleshooter<\/code>\u00a0and\u00a0<code>TestsFixer<\/code>, controlled by the\u00a0<code>Orchestrator<\/code>. They run in the listed order, with the\u00a0<code>Developer<\/code>\u00a0generating most of the codebase. After each code generation step, a check is performed for missing files based on their roles (e.g., routes, controllers, services). If any files are missing, a new code generation attempt is made, including instructions in the prompt about the missing files and examples for each role. Once the\u00a0<code>Developer<\/code>\u00a0completes its work, TypeScript compilation begins. If any errors are found, the\u00a0<code>Troubleshooter<\/code>\u00a0takes over, passing the errors to the prompt and waiting for the corrected code. Finally, when the compilation succeeds, E2E tests are run. Whenever a test fails, the\u00a0<code>TestsFixer<\/code>\u00a0steps in with specific prompt instructions, ensuring all tests pass and the code stays clean.<\/p>\n<p class=\"wp-block-paragraph\" id=\"48f9\">All micro-agents are derived from the\u00a0<code>BaseAgent<\/code>\u00a0class and actively reuse its base method implementations. Here is the\u00a0<code>Developer<\/code>\u00a0implementation for reference:<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"2e3337\" data-has-transparency=\"false\" style=\"--dominant-color: #2e3337;\" loading=\"lazy\" decoding=\"async\" width=\"578\" height=\"1024\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_XPDMHj0TnJvyYAx9Ub_o4Q-1-578x1024.webp?resize=578%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-598255 not-transparent\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_XPDMHj0TnJvyYAx9Ub_o4Q-1-578x1024.webp 578w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_XPDMHj0TnJvyYAx9Ub_o4Q-1-169x300.webp 169w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_XPDMHj0TnJvyYAx9Ub_o4Q-1-768x1360.webp 768w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_XPDMHj0TnJvyYAx9Ub_o4Q-1.webp 799w\" sizes=\"auto, (max-width: 578px) 100vw, 578px\"><\/figure>\n<p class=\"wp-block-paragraph\" id=\"c96a\">Each agent utilizes its specific prompt. Check out this GitHub\u00a0<a href=\"https:\/\/github.com\/vyancharuk\/nodejs-todo-api-boilerplate\/blob\/master\/llm-codegen\/core\/prompts\/developer.main.prompt\" rel=\"noreferrer noopener\" target=\"_blank\">link<\/a>\u00a0for the prompt used by the\u00a0<code>Developer<\/code>.<\/p>\n<p class=\"wp-block-paragraph\" id=\"ae82\">After dedicating significant effort to research and testing, I refined the prompts for all micro-agents, resulting in clean, well-structured code with very few issues.<\/p>\n<p class=\"wp-block-paragraph\" id=\"9ca3\">During the development and testing, it was used with various module descriptions, ranging from simple to highly detailed. Here are a few examples:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">- The module responsible for library book management must handle endpoints for CRUD operations on books.\n- The module responsible for the orders management. It must provide CRUD operations for handling customer orders. Users can create new orders, read order details, update order statuses or information, and delete orders that are canceled or completed. Order must have next attributes: name, status, placed source, description, image url\n- Asset Management System with an \"Assets\" module offering CRUD operations for company assets. Users can add new assets to the inventory, read asset details, update information such as maintenance schedules or asset locations, and delete records of disposed or sold assets.<\/code><\/pre>\n<p class=\"wp-block-paragraph\" id=\"8304\">Testing with\u00a0<code>gpt-4o-mini<\/code>\u00a0and\u00a0<code>claude-3-5-sonnet-20241022<\/code>\u00a0showed comparable output code quality, although Sonnet is more expensive. Claude Haiku (<code>claude-3\u20135-haiku-20241022<\/code>), while cheaper and similar in price to\u00a0<code>gpt-4o-mini<\/code>, often produces non-compilable code. Overall, with\u00a0<code>gpt-4o-mini<\/code>, a single code generation session consumes an average of around 11k input tokens and 15k output tokens. This amounts to a cost of approximately 2 cents per session, based on token pricing of 15 cents per 1M input tokens and 60 cents per 1M output tokens (as of December 2024).<\/p>\n<p class=\"wp-block-paragraph\" id=\"2fae\">Below are Anthropic usage logs showing token consumption:<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" data-dominant-color=\"292929\" data-has-transparency=\"false\" style=\"--dominant-color: #292929;\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"532\" src=\"https:\/\/i0.wp.com\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_rbQAv40mxTmyLHYCQDaurA-1-1024x532.webp?resize=1024%2C532&#038;ssl=1\" alt=\"\" class=\"wp-image-598256 not-transparent\" srcset=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_rbQAv40mxTmyLHYCQDaurA-1-1024x532.webp 1024w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_rbQAv40mxTmyLHYCQDaurA-1-300x156.webp 300w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_rbQAv40mxTmyLHYCQDaurA-1-768x399.webp 768w, https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/1_rbQAv40mxTmyLHYCQDaurA-1.webp 1400w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\"><\/figure>\n<p class=\"wp-block-paragraph\" id=\"b3e1\">Based on my experimentation over the past few weeks, I conclude that while there may still be some issues with passing generated tests, 95% of the time generated code is compilable and runnable.<\/p>\n<p class=\"wp-block-paragraph\" id=\"e2b4\">I hope you found some inspiration here and that it serves as a starting point for your next Node.js API or an upgrade to your current project. Should you have suggestions for improvements, feel free to contribute by submitting PR for code or prompt updates.<\/p>\n<p class=\"wp-block-paragraph\" id=\"34c5\">If you enjoyed this article, feel free to clap or share your thoughts in the comments, whether ideas or questions. Thank you for reading, and happy experimenting!<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\" id=\"bfb0\"><strong>UPDATE<\/strong>\u00a0[February 9, 2025]: The LLM-Codegen GitHub repository was updated with\u00a0<a href=\"https:\/\/github.com\/vyancharuk\/nodejs-todo-api-boilerplate\/blob\/master\/llm-codegen\/core\/llmClients\/deepSeekLLMClient.ts\" rel=\"noreferrer noopener\" target=\"_blank\">DeepSeek API<\/a>\u00a0support. It\u2019s cheaper than\u00a0<code>gpt-4o-mini<\/code>\u00a0and offers nearly the same output quality, but it has a longer response time and sometimes struggles with API request errors.<\/p>\n<\/blockquote>\n<p class=\"wp-block-paragraph\" id=\"4d78\"><em>Unless otherwise noted, all images are by the author<\/em><a href=\"https:\/\/medium.com\/tag\/nodejs?source=post_page-----59e9fc11ce95---------------------------------------\"><\/a><\/p>\n<p>The post <a href=\"https:\/\/towardsdatascience.com\/how-to-use-an-llm-powered-boilerplate-for-building-your-own-node-js-api\/\">How to Use an LLM-Powered Boilerplate for Building Your Own Node.js API<\/a> appeared first on <a href=\"https:\/\/towardsdatascience.com\/\">Towards Data Science<\/a>.<\/p>\n<\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><br \/>\n    Uladzimir Yancharuk<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/towardsdatascience.com\/how-to-use-an-llm-powered-boilerplate-for-building-your-own-node-js-api\/\">Go to original source<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>How to Use an LLM-Powered Boilerplate for Building Your Own Node.js API For a long time, one of the common ways to start new Node.js projects was using boilerplate templates. These templates help developers reuse familiar code structures and implement standard features, such as access to cloud file storage. With the latest developments in LLM, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[62,1115,1813,71,1814,160],"tags":[1815,368,1816],"class_list":["post-1980","post","type-post","status-publish","format-standard","hentry","category-aimldsaimlds","category-api","category-code-generation","category-large-language-models","category-nodejs","category-programming","tag-boilerplate","tag-code","tag-node"],"_links":{"self":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1980"}],"collection":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/comments?post=1980"}],"version-history":[{"count":0,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1980\/revisions"}],"wp:attachment":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/media?parent=1980"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/categories?post=1980"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/tags?post=1980"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}