<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: koyopro</title>
    <description>The latest articles on DEV Community by koyopro (@koyopro).</description>
    <link>https://dev.to/koyopro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1575930%2F847cddbf-312e-4792-b7f2-8b6006e7fa0d.png</url>
      <title>DEV Community: koyopro</title>
      <link>https://dev.to/koyopro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/koyopro"/>
    <language>en</language>
    <item>
      <title>After a Year of Wanting a Typed Rails and Starting Development, I Released a Framework for TypeScript</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Fri, 28 Mar 2025 16:25:15 +0000</pubDate>
      <link>https://dev.to/koyopro/after-a-year-of-wanting-a-typed-rails-and-starting-development-i-released-a-framework-for-519o</link>
      <guid>https://dev.to/koyopro/after-a-year-of-wanting-a-typed-rails-and-starting-development-i-released-a-framework-for-519o</guid>
      <description>&lt;p&gt;I released version 1.0 of Accella, a TypeScript-oriented web framework, in February 2025. In this article, I’ll provide a brief introduction to Accella, followed by a chronological overview of its development process and the design intentions behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Accella?
&lt;/h2&gt;

&lt;p&gt;Accella is a server-side web application framework designed for TypeScript. Below is an excerpt of its features from the opening section of the official website (&lt;a href="https://accella.dev):" rel="noopener noreferrer"&gt;https://accella.dev):&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server-First&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Accella follows the tradition of full-stack MVC frameworks. Based on Astro, it returns pre-rendered HTML to the client from the server. By keeping the architecture simple, it enhances both development efficiency and user experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ORM Integration&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Accella utilizes Accel Record, an ORM implemented with the Active Record pattern. This integration enhances development efficiency, particularly for database CRUD operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Accella provides a type-safe development environment with TypeScript, covering everything from table operations to template rendering.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more details about the framework, check out the &lt;a href="https://accella.dev/" rel="noopener noreferrer"&gt;official website&lt;/a&gt;. It includes sample code for application development and comparisons with other frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Intentions and Development Journey
&lt;/h2&gt;

&lt;p&gt;I’ll outline the journey from the start of Accella’s development to its release, focusing particularly on aspects related to its design intentions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Starting with an ORM Library
&lt;/h3&gt;

&lt;p&gt;Development of Accella began in January 2024. At the time, I wanted to contribute to open-source software (OSS) while also thinking, “I wish TypeScript had a framework as efficient as Ruby on Rails.”&lt;/p&gt;

&lt;p&gt;The elements I sought in this “Rails-like development efficiency” included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Development based on SSR (Server-Side Rendering) for MPA (Multi-Page Applications), not SPA (Single-Page Applications).&lt;/li&gt;
&lt;li&gt;The ability to implement RDB (Relational Database) CRUD functionality with minimal code.&lt;/li&gt;
&lt;li&gt;A framework covering the MVC (Model-View-Controller) domains.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After researching existing frameworks, I couldn’t find one that sufficiently met these criteria. Further analysis led me to conclude that a powerful ORM like Rails’ Active Record was necessary, so I started the project by developing an ORM for TypeScript.&lt;/p&gt;

&lt;p&gt;I wrote about the motivations behind this in more detail in the following article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/koyopro/seeking-a-type-safe-ruby-on-rails-in-typescript-i-started-developing-an-orm-1of5"&gt;Seeking a Type-Safe Ruby on Rails in TypeScript, I Started Developing an ORM&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Prisma for Table Definitions and Migrations
&lt;/h3&gt;

&lt;p&gt;While developing the ORM, I considered how to handle table definitions and migrations.&lt;/p&gt;

&lt;p&gt;I discovered that Prisma’s &lt;a href="https://www.prisma.io/docs/orm/prisma-schema/overview/generators" rel="noopener noreferrer"&gt;generator&lt;/a&gt; feature allows arbitrary code generation from Prisma’s table definition files. Even query builder libraries like &lt;a href="https://kysely.dev/" rel="noopener noreferrer"&gt;Kysely&lt;/a&gt; can use Prisma generators to output necessary type definition files. For this ORM, I decided to use Prisma for table definitions and migrations, automatically generating required files with Prisma’s generator. While Rails’ migrations involve creating differential files, I personally find Prisma’s approach—automatically generating migration files from schema files—more convenient, which was one reason for this choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adopting a Synchronous API for the ORM
&lt;/h3&gt;

&lt;p&gt;Typically, JavaScript/TypeScript libraries use asynchronous APIs for external access like database operations. However, as development progressed, I began to think that designing the ORM’s interface as a synchronous API would be better.&lt;/p&gt;

&lt;p&gt;With asynchronous APIs that return Promises, the common approach is to use &lt;code&gt;await&lt;/code&gt; to wait for completion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Fetch the first User from the database&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I found that asynchronous APIs limited the library’s interface, making it difficult to achieve the usability of Rails’ Active Record. So, I adopted a synchronous API that allows calling operations without &lt;code&gt;await&lt;/code&gt;. This was the most challenging part of the ORM’s design and implementation, but it’s also where its uniqueness shines.&lt;/p&gt;

&lt;p&gt;I’ve written more about synchronous APIs in the following articles, so feel free to check them out if you’re interested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/koyopro/why-we-adopted-a-synchronous-api-for-the-new-typescript-orm-1jm"&gt;Why We Adopted a Synchronous API for the New TypeScript ORM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/koyopro/even-server-side-typescript-needs-the-option-to-avoid-asynchronous-processing-1opm"&gt;Even Server-Side TypeScript Needs the Option to Avoid Asynchronous Processing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/koyopro/techniques-for-synchronous-db-access-in-typescript-2976"&gt;Techniques for Synchronous DB Access in TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/koyopro/released-a-library-for-synchronous-execution-of-asynchronous-processes-in-jsts-2k7j"&gt;Released a Library for Synchronous Execution of Asynchronous Processes in JS/TS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Releasing the ORM as Accel Record
&lt;/h3&gt;

&lt;p&gt;In April 2024, I released version 1.0 of the ORM library “Accel Record.” It implements core CRUD operations for tables, featuring a synchronous API and type safety, such as changing model types before and after persistence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/koyopro/introduction-to-accel-record-a-typescript-orm-using-the-active-record-pattern-2oeh"&gt;Introduction to "Accel Record": A TypeScript ORM Using the Active Record Pattern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Afterward, I added more features to the ORM, heavily inspired by Rails’ Active Record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Factories&lt;/li&gt;
&lt;li&gt;Bulk Inserts&lt;/li&gt;
&lt;li&gt;Serialization&lt;/li&gt;
&lt;li&gt;Transactions&lt;/li&gt;
&lt;li&gt;Internationalization (I18n)&lt;/li&gt;
&lt;li&gt;Callbacks&lt;/li&gt;
&lt;li&gt;PostgreSQL Support&lt;/li&gt;
&lt;li&gt;Password Authentication&lt;/li&gt;
&lt;li&gt;Scopes&lt;/li&gt;
&lt;li&gt;Advanced Search&lt;/li&gt;
&lt;li&gt;Locking&lt;/li&gt;
&lt;li&gt;Composite Key Support&lt;/li&gt;
&lt;li&gt;Form Objects&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using Astro Components for Rendering
&lt;/h3&gt;

&lt;p&gt;In Rails, pagination and forms can be easily created based on Active Record data. I wanted to achieve similar functionality in Accella. I also wanted the View templates (like erb in Rails) to be type-safe. Traditional server-side JS template engines (EJS, Pug, etc.) didn’t seem capable of this. That’s when Astro came up as a candidate. I found that &lt;a href="https://docs.astro.build/en/basics/astro-components/" rel="noopener noreferrer"&gt;Astro components&lt;/a&gt; allow type-safe server-side template rendering, similar to React or Vue.&lt;/p&gt;

&lt;p&gt;By August 2024, the ORM’s features were fairly robust, so I began implementing functionalities such as form building integrated with the ORM using Astro components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pagination&lt;/li&gt;
&lt;li&gt;Forms&lt;/li&gt;
&lt;li&gt;Request Parameter Parsing&lt;/li&gt;
&lt;li&gt;Session Management&lt;/li&gt;
&lt;li&gt;CSRF Protection&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Contributing to Astro
&lt;/h3&gt;

&lt;p&gt;To use Astro components as a template engine, Astro needed to serve as Accella’s base. In MVC terms, the Model is handled by Accella’s custom ORM, while the View and Controller are managed by Astro’s features.&lt;/p&gt;

&lt;p&gt;While developing features, I frequently worked with Astro and contributed to its community by addressing issues I encountered.&lt;/p&gt;

&lt;p&gt;In Rails, session data is stored in cookies by default. I created a library for Astro to store sessions in cookies and shared it on Reddit, receiving a lot of positive feedback:&lt;br&gt;
&lt;a href="https://www.reddit.com/r/astrojs/comments/1fxl8od/i_just_published_astro_cookie_session_middleware/" rel="noopener noreferrer"&gt;I just published "Astro Cookie Session", middleware for managing sessions using cookies on Astro. : r/astrojs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also submitted two bug-fix pull requests to Astro’s main repository, which were merged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Polishing it as a Framework
&lt;/h3&gt;

&lt;p&gt;Around November 2024, I worked on refining the features I’d built into a usable framework. Like Rails’ &lt;code&gt;rails new&lt;/code&gt;, I aimed for an environment where application development could start immediately with &lt;code&gt;npm create&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I ensured that SQLite could be used for development right after setup and that tests involving table read/write operations could run, focusing on completing database-related setup. Accella uses Vitest as its testing framework, but since Vitest runs tests in multiple processes (or threads), I had to devise ways to properly prepare multiple test databases.&lt;/p&gt;

&lt;p&gt;Ultimately, running &lt;code&gt;npm create accella&lt;/code&gt; sets up an environment where database-driven development can begin immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation and Version 1.0 Release
&lt;/h3&gt;

&lt;p&gt;Starting in January 2025, I prepared Accella’s documentation site, creating a starter guide while fixing usability issues and bugs. I built the documentation with Astro Starlight and hosted it on Netlify, which made the process quite smooth.&lt;/p&gt;

&lt;p&gt;On February 26, 2025, I released version 1.0 of Accella.&lt;/p&gt;

&lt;p&gt;I posted about it on Reddit and received a lot of responses:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.reddit.com/r/astrojs/comments/1j48lmd/introducing_accella_a_fullstack_framework_built/" rel="noopener noreferrer"&gt;Introducing Accella: A Full-Stack Framework Built on Astro : r/astrojs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F62874%2F2415e648-996e-4023-90ad-59bfb1cb7248.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F62874%2F2415e648-996e-4023-90ad-59bfb1cb7248.png" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Development
&lt;/h2&gt;

&lt;p&gt;With version 1.0 released and documentation prepared, Accella is now in a state where others can try it out. I believe I’ve created one direction for my initial motivation: “I want a TypeScript framework as efficient as Ruby on Rails.” In particular, the ORM featuring the “Active Record pattern” and “synchronous API” could offer a new option for server-side JavaScript.&lt;/p&gt;

&lt;p&gt;Currently, I’m developing a CarrierWave-like library that integrates with ORM models to easily upload files to S3.&lt;/p&gt;

&lt;p&gt;In the future, I’d like to work on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A library for efficiently managing initial (seed) database data (like Seed Fu).&lt;/li&gt;
&lt;li&gt;A library for easily creating internal admin panels (like Active Admin).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re interested, I’d be delighted if you’d star the repository:&lt;br&gt;
&lt;a href="https://github.com/koyopro/accella" rel="noopener noreferrer"&gt;https://github.com/koyopro/accella&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading to the end!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>orm</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Released a Library for Synchronous Execution of Asynchronous Processes in JS/TS</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Mon, 16 Dec 2024 18:57:26 +0000</pubDate>
      <link>https://dev.to/koyopro/released-a-library-for-synchronous-execution-of-asynchronous-processes-in-jsts-2k7j</link>
      <guid>https://dev.to/koyopro/released-a-library-for-synchronous-execution-of-asynchronous-processes-in-jsts-2k7j</guid>
      <description>&lt;p&gt;I have released a library called &lt;a href="https://www.npmjs.com/package/sync-actions" rel="noopener noreferrer"&gt;sync-actions&lt;/a&gt; that allows asynchronous processes to be executed synchronously in JavaScript/TypeScript. Especially in TypeScript, you can call defined functions in a type-safe manner. It is intended for use in cases where you want to execute asynchronous processes within functions that you do not want (or cannot) mark as async.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Utilizes Node.js worker_threads

&lt;ul&gt;
&lt;li&gt;Asynchronous processes are executed in a sub-thread, and the main thread waits synchronously for their completion.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Type-safe function calls

&lt;ul&gt;
&lt;li&gt;In TypeScript, you can utilize the type information of defined functions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Published as Native ESM

&lt;ul&gt;
&lt;li&gt;It is made simple by not supporting CommonJS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/koyopro/sync-actions" rel="noopener noreferrer"&gt;https://github.com/koyopro/sync-actions&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;It is published as an npm package, so please install it using npm install or similar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;sync-actions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Basic Usage
&lt;/h3&gt;

&lt;p&gt;By passing an asynchronous function that returns a Promise object to &lt;code&gt;defineSyncWorker()&lt;/code&gt;, you can define the interface and start the worker thread with &lt;code&gt;launch()&lt;/code&gt;. It is assumed that the file defining the worker is created separately from other processing files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// worker.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineSyncWorker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync-actions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineSyncWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Execute asynchronous process,&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// Return the result as a return value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pong&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// You can execute asynchronous functions synchronously&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; "pong" is output after 1 second&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Type-safe Function Calls
&lt;/h3&gt;

&lt;p&gt;In TypeScript, you can call functions defined with &lt;code&gt;defineSyncWorker&lt;/code&gt; in a type-safe manner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// worker.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineSyncWorker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sync-actions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineSyncWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// By specifying the types of arguments and return values, type-safe calls are possible&lt;/span&gt;
  &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Type-safe call&lt;/span&gt;
&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; 3 (number)&lt;/span&gt;

&lt;span class="c1"&gt;// @ts-expect-error&lt;/span&gt;
&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; Argument of type 'string' is not assignable to parameter of type 'number'&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;The content so far is the same as the README, so I will describe the background of its creation.&lt;/p&gt;

&lt;p&gt;I am developing an ORM called Accel Record.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; Unlike general ORMs, Accel Record is designed to perform DB access with a synchronous interface.&lt;sup id="fnref2"&gt;2&lt;/sup&gt; The part that executes DB access synchronously was realized by executing asynchronous processes in a subprocess started with the child_process module.&lt;sup id="fnref3"&gt;3&lt;/sup&gt; I thought that by using worker_threads instead of child_process, I could reduce the overhead at runtime.&lt;/p&gt;

&lt;p&gt;Accel Record is also designed to be similar to Ruby on Rails' Active Record in terms of usability, and one of the things I want to achieve in the future is to create a library like &lt;a href="https://github.com/carrierwaveuploader/carrierwave" rel="noopener noreferrer"&gt;CarrierWave&lt;/a&gt;. CarrierWave allows you to save images to external storage services (such as AWS S3) when saving records, and to achieve this with Accel Record, it is necessary to execute asynchronous processes such as image uploads synchronously. I expect that this process can be executed faster by using worker_threads instead of subprocesses.&lt;/p&gt;

&lt;p&gt;So I once looked for a library that synchronously executes asynchronous processes using worker_threads. I found several libraries such as synckit and deasync, but none of them worked as expected on my end, so I decided to create my own. Since I was at it, I thought I would make an interface that can be used in a type-safe manner with TypeScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Internal Details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Until the asynchronous process in the sub-thread started with worker_threads is completed, the main thread is blocked using Atomic.wait().&lt;/li&gt;
&lt;li&gt;MessageChannel is used for communication between threads. The source code of synckit was very helpful for this part of the implementation.&lt;/li&gt;
&lt;li&gt;When starting a Worker with worker_threads, it is necessary to transpile the .ts file to .js. For that part, I am using esbuild.&lt;/li&gt;
&lt;li&gt;When starting a Worker, I wanted to pass the transpiled source code as a string to the Worker for execution, but it did not work properly in my environment. This is the part where I struggled the most. In the end, I wrote the file under node_modules and passed its path to the Worker. This method turned out to be the most stable.&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://dev.to/koyopro/introduction-to-accel-record-a-typescript-orm-using-the-active-record-pattern-2oeh"&gt;Introduction to "Accel Record": A TypeScript ORM Using the Active Record Pattern&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://dev.to/koyopro/why-we-adopted-a-synchronous-api-for-the-new-typescript-orm-1jm"&gt;Why We Adopted a Synchronous API for the New TypeScript ORM&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://dev.to/koyopro/techniques-for-synchronous-db-access-in-typescript-2976"&gt;Techniques for Synchronous DB Access in TypeScript&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>typescript</category>
      <category>synchronous</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Password Authentication with Auth.js in Astro and Customizing Session Information (auth-astro)</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Tue, 01 Oct 2024 21:51:57 +0000</pubDate>
      <link>https://dev.to/koyopro/password-authentication-with-authjs-in-astro-and-customizing-session-information-auth-astro-1n4n</link>
      <guid>https://dev.to/koyopro/password-authentication-with-authjs-in-astro-and-customizing-session-information-auth-astro-1n4n</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I wanted to implement a feature in Astro that allows authentication with an email and password and stores session information in cookies.&lt;/li&gt;
&lt;li&gt;It seemed possible with &lt;a href="https://github.com/nowaythatworked/auth-astro" rel="noopener noreferrer"&gt;auth-astro&lt;/a&gt;, which is &lt;a href="https://docs.astro.build/en/guides/authentication/#authjs" rel="noopener noreferrer"&gt;officially introduced in Astro&lt;/a&gt;.

&lt;ul&gt;
&lt;li&gt;auth-astro is a wrapper library that makes Auth.js (formerly NextAuth.js) easier to use in Astro.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;I couldn't find consolidated information for the following two points, so I organized them:

&lt;ul&gt;
&lt;li&gt;How to set up password authentication.&lt;/li&gt;
&lt;li&gt;How to customize the authentication information saved in cookies.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After trying this, I found Auth.js to be somewhat difficult to use, so it's worth considering carefully before deciding to use it. Refer to the Impressions section for details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js: 20.11&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;astro&lt;/code&gt;: 4.15.2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;auth-astro&lt;/code&gt;: &lt;strong&gt;4.1.2&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@auth/core&lt;/code&gt;: &lt;strong&gt;0.35.3&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;※ In auth-astro 4.1.2, &lt;code&gt;"@auth/core": "^0.32.0"&lt;/code&gt; is specified in the dependencies, but version &lt;code&gt;0.32.x&lt;/code&gt; had an issue where a 500 error occurred during error handling for &lt;code&gt;CredentialsSignin&lt;/code&gt;, so I specified the latest version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Code
&lt;/h2&gt;

&lt;p&gt;Assuming that auth-astro has already been integrated into a project created with Astro, you can set up password authentication and customize session information by configuring &lt;code&gt;auth.config.mjs&lt;/code&gt; as follows. The example below saves the logged-in user's ID as a session token in cookies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// auth.config.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CredentialsSignin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@auth/core/errors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Credentials&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@auth/core/providers/credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auth-astro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Credentials&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// your logic here&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CredentialsSignin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  UI
&lt;/h2&gt;

&lt;p&gt;Using the login screen provided by Auth.js, the following behavior can be seen:&lt;/p&gt;

&lt;h3&gt;
  
  
  Login Screen (/api/auth/signin)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://gyazo.com/fad9b60f4cc4af064bffd75845e8aef0" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2zl8jh0nhvo0suq5k6xk.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When Authentication Fails (/api/auth/signin?error=CredentialsSignin&amp;amp;code=credentials)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://gyazo.com/7d623e37f4c6db5a45c3ca3c2588f158" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F598jej2zo39ztjsn298v.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When Authentication Succeeds
&lt;/h3&gt;

&lt;p&gt;After successful authentication, you are redirected to another page, and you can retrieve session information using &lt;code&gt;getSession&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/example.astro&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getSession&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth-astro/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   user: { id: 1 },&lt;/span&gt;
&lt;span class="c1"&gt;//   expireds: '2024-10-01T00:00:00.000Z'&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Logged&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Not&lt;/span&gt; &lt;span class="nx"&gt;logged&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When authentication is successful, a cookie named &lt;code&gt;authjs.session-token&lt;/code&gt; is saved, with its contents encrypted in JWT format by Auth.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of a Session Token&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwia2lkIjoiTjEzYW5aS3hzMmpXT0pDUmNYaTRCYjZCMkdUNmxZdmxaa3lUVEdfbHRYZG14UEJVYkVkNkJxaVlRRnhNdTFuZjRBa3BGRmxhTlI1dW11ZENRc3JaQ0EifQ..IAkEwYkG1M9Y-J5rTju63g.haLgQeSxUEqctr2Gjn23sMaNEAaGb9y1ott-dZnws-oo7Sdcz1fnPRRIvpLpBe6LkJN2JMELAHbElbAl-41BrgAzUDTeWnGlNTeFVa3Em6iWObNtfwX_H7nOtnddB3OZ.aOZQ_jzdVkNvdepksoAFc9I8Qz7fuFlcAtJcN-Tp-ng
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Customizing the Session Token
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Default Behavior
&lt;/h3&gt;

&lt;p&gt;When using Credentials, the default behavior is to save &lt;code&gt;email&lt;/code&gt; as the unique key in the cookie. If you don't need to change the format, customization of the callbacks is unnecessary. The following settings would suffice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// auth.config.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CredentialsSignin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@auth/core/errors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Credentials&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@auth/core/providers/credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auth-astro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Credentials&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// your logic here&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CredentialsSignin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Contents of the session&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   user: { email: 'test@example.com', name: undefined, image: undefined },&lt;/span&gt;
&lt;span class="c1"&gt;//   expireds: '2024-10-01T00:00:00.000Z'&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Customization Details
&lt;/h3&gt;

&lt;p&gt;If you want to save information other than the email in the session token, you will need to specify the callbacks. The example below replaces the email with the user ID. This is reflected in the code shown in the "Final Code" section of this article.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Change the return format&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Change the token format saved in the cookie&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// Change the format of the information read from the token&lt;/span&gt;
  &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, you can retrieve the session in the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   user: { id: 1 },&lt;/span&gt;
&lt;span class="c1"&gt;//   expireds: '2024-10-01T00:00:00.000Z'&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Impressions
&lt;/h2&gt;

&lt;p&gt;The feature I wanted to implement, "password authentication with email and saving session information in cookies with Astro," was achieved with the above configuration.&lt;/p&gt;

&lt;p&gt;However, after working with Auth.js, I felt that customizing its behavior might be challenging.&lt;/p&gt;

&lt;p&gt;For example, in the following cases, there is no official support, so you would likely need to prepare a custom page and implement it yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you want to change error messages (for multilingual support, etc.)&lt;/li&gt;
&lt;li&gt;If you want to modify the appearance of the login page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, keeping the email address entered in the field after an authentication failure seems quite difficult with the current Auth.js system. Since there is a redirect during the authentication process, the information entered in the form is lost. &lt;/p&gt;

&lt;p&gt;In my research, I also came across the following opinions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.reddit.com/r/nextjs/comments/19a86sa/youre_not_a_bad_engineer_nextauth_is_a_bad_library/" rel="noopener noreferrer"&gt;You're not a bad engineer, NextAuth is a bad library. - Reddit&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Conversely, it feels like NextAuth was written with the express goal of avoiding the complexity of auth, and it does it badly. It lets you get something working quickly, but later causes immense pain trying to customise it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.reddit.com/r/nextjs/comments/1crn50l/why_is_nextauth_or_authjs_so_popular/" rel="noopener noreferrer"&gt;Why is next-auth (or Auth.js) so popular? - Reddit&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have tried to build a new website with auth, and my experience with Auth.js (v5) was nothing short of a disaster. The docs was horrible, it offers little customizability, and the configuration just doesn't work. If I were the project lead, I wouldn't promote this piece of shit until it gets stable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're using the default behavior of Auth.js, there might not be any issues, but if you're considering any customizations, it might be worth reviewing beforehand.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Authentication | Astro&lt;br&gt;
&lt;a href="https://docs.astro.build/en/guides/authentication/#authjs" rel="noopener noreferrer"&gt;https://docs.astro.build/en/guides/authentication/#authjs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;nowaythatworked/auth-astro: Community maintained Astro integration of &lt;a class="mentioned-user" href="https://dev.to/auth"&gt;@auth&lt;/a&gt;/core&lt;br&gt;
&lt;a href="https://github.com/nowaythatworked/auth-astro" rel="noopener noreferrer"&gt;https://github.com/nowaythatworked/auth-astro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Credentials | Auth.js&lt;br&gt;
&lt;a href="https://authjs.dev/getting-started/authentication/credentials" rel="noopener noreferrer"&gt;https://authjs.dev/getting-started/authentication/credentials&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Callbacks | NextAuth.js&lt;br&gt;
&lt;a href="https://next-auth.js.org/configuration/callbacks" rel="noopener noreferrer"&gt;https://next-auth.js.org/configuration/callbacks&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>astro</category>
      <category>authjs</category>
      <category>cookie</category>
      <category>nextauth</category>
    </item>
    <item>
      <title>Two Reasons Why I Often Use Python for Creating Personal Tools (Plus One Complaint)</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Sat, 21 Sep 2024 16:39:40 +0000</pubDate>
      <link>https://dev.to/koyopro/two-reasons-why-i-often-use-python-for-creating-personal-tools-plus-one-complaint-1n9d</link>
      <guid>https://dev.to/koyopro/two-reasons-why-i-often-use-python-for-creating-personal-tools-plus-one-complaint-1n9d</guid>
      <description>&lt;p&gt;I often use Python when creating tools for personal use. The tools I create are generally for automating day-to-day tasks or for fun application projects.&lt;/p&gt;

&lt;p&gt;These are usually small projects that I complete in a few days and don’t update much afterward. The considerations are different for larger, publicly released services, but here are two reasons why I frequently choose Python for small tool development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 1: Python Can Do Almost Anything
&lt;/h2&gt;

&lt;p&gt;When I want to accomplish something, Python often already has a library for it. Here are some examples of Python libraries I've used for personal projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Machine Learning&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python is likely the most well-equipped language for machine learning libraries.&lt;/li&gt;
&lt;li&gt;Although I don’t personally train deep learning models often, I sometimes use scikit-learn or XGBoost to build and apply models.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Image Processing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I’ve written scripts for managing personal photos.&lt;/li&gt;
&lt;li&gt;Libraries like PIL (Python Imaging Library) and Pillow help me retrieve Exif data or resize images.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scraping&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I’ve created tools to periodically check information on certain websites.&lt;/li&gt;
&lt;li&gt;You can use simple libraries like Requests, or more comprehensive ones like Scrapy to make scraping even easier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cryptocurrency Trading&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I once wanted to use a cryptocurrency exchange API.&lt;/li&gt;
&lt;li&gt;Thanks to the library &lt;code&gt;ccxt&lt;/code&gt;, which allows you to use the APIs of over 100 exchanges with a unified interface, I could achieve what I wanted.&lt;/li&gt;
&lt;li&gt;It was very helpful not to have to investigate the API specifications of each exchange and to be able to trade with a consistent interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Web Applications&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sometimes I want to control the above functionalities through a GUI.&lt;/li&gt;
&lt;li&gt;In such cases, I often use Django to run it as a web application.&lt;/li&gt;
&lt;li&gt;I particularly like Django because it provides an admin panel by default, making it easy to manage settings and check data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reason 2: It’s Cheap to Run in the Cloud
&lt;/h2&gt;

&lt;p&gt;Since personal tools aren’t used frequently, I want to keep the costs low when running them on a server. Python has long been supported by free cloud platforms, which is another reason I choose it for personal tool development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google App Engine (GAE)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GAE offers a free tier in its standard environment.&lt;/li&gt;
&lt;li&gt;Since it has supported Python since its release in 2008, I’ve often used it for running personal tools.&lt;/li&gt;
&lt;li&gt;It’s also handy that you can set up cron jobs for scheduled execution through the management console.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AWS Lambda&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Lambda was released in 2014, and Python has been supported since October 2015.&lt;/li&gt;
&lt;li&gt;It also offers a free tier, so I sometimes run tools on it nowadays.&lt;/li&gt;
&lt;li&gt;Using the Serverless Framework provides a smooth experience from local development to deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Depending on the situation, I also run tools on EC2 or Heroku.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Complaint About Developing with Python
&lt;/h2&gt;

&lt;p&gt;There are some aspects of Python that I find unsatisfactory. In particular, the management of virtual environments and packages tends to be unstable. When I check back after some time, I often find a new method has been introduced or an old method has been deprecated. I’ve used the following tools, but it’s easy to get confused if you don’t understand how to use each one properly. (I’m not sure what the current best practices are.)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;virtualenv&lt;/li&gt;
&lt;li&gt;venv&lt;/li&gt;
&lt;li&gt;pipenv&lt;/li&gt;
&lt;li&gt;pip-tools&lt;/li&gt;
&lt;li&gt;poetry&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I’ve listed two reasons why I often use Python for creating personal tools and added one complaint for good measure. I hope this has been helpful.&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Git Feature Flow – A More Flexible Branch Model for Incremental Releases Than Git-Flow</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Sun, 08 Sep 2024 17:33:50 +0000</pubDate>
      <link>https://dev.to/koyopro/git-feature-flow-a-more-flexible-branch-model-for-incremental-releases-than-git-flow-1cl3</link>
      <guid>https://dev.to/koyopro/git-feature-flow-a-more-flexible-branch-model-for-incremental-releases-than-git-flow-1cl3</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;In a server-side development project, we were using Git-Flow (or something similar), but we faced difficulties during production releases. This article is about how we changed the Git workflow to solve these issues. &lt;/p&gt;

&lt;p&gt;First, I'll explain the problem in order, so if you're only interested in the conclusion (the new workflow), you can skip to this section.&lt;/p&gt;

&lt;p&gt;There are several well-known Git workflows, such as Git-Flow, GitHub Flow, and GitLab Flow, but our new workflow felt a bit different from any of these, so I decided to summarize it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview of the Project Where It Was Introduced
&lt;/h3&gt;

&lt;p&gt;The best workflow depends on the project, so I'll briefly describe the project where we introduced this new workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are fewer than 10 developers (engineers).

&lt;ul&gt;
&lt;li&gt;It's a relatively small team, but larger than just one or two people.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The contents of the develop branch are always reflected on the testing server.

&lt;ul&gt;
&lt;li&gt;Updates to the develop branch are automatically uploaded via CI.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The contents of the master branch are reflected in the production environment.

&lt;ul&gt;
&lt;li&gt;The master branch must always be in a releasable state.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Feature branches are developed locally.

&lt;ul&gt;
&lt;li&gt;Most people develop on their Mac locally.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;There is only one testing server.

&lt;ul&gt;
&lt;li&gt;We have considered setting up a server for each branch, but the current operation has determined that one server is more suitable.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Production releases occur about once every few days to two weeks.

&lt;ul&gt;
&lt;li&gt;It's not the kind of environment where we release to production multiple times a day.&lt;/li&gt;
&lt;li&gt;The release process itself is simple, just one command if the updates are in the master branch, so the cost of the process is low.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Original Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Branch Model
&lt;/h3&gt;

&lt;p&gt;Although we called it Git-Flow, it wasn’t the full Git-Flow but rather a workflow with the release branch omitted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h8ae36r18y4a4jc6nal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h8ae36r18y4a4jc6nal.png" alt="The Original Workflow" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;develop

&lt;ul&gt;
&lt;li&gt;The branch for internal testing.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;feature/xxx

&lt;ul&gt;
&lt;li&gt;A local branch for developing features.&lt;/li&gt;
&lt;li&gt;Branched off from develop.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;master

&lt;ul&gt;
&lt;li&gt;The branch for production.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In the diagram above, two feature branches are created and reflected on the testing server when merged into develop. After passing the tests for each branch, merge develop into master and release.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unable to release without other feature tests passing
&lt;/h3&gt;

&lt;p&gt;Here’s a scenario where this flow causes problems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ssn5c0oei8gmgw5jwpx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ssn5c0oei8gmgw5jwpx.png" alt="Problem of The Original Workflow" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same flow as before, but it becomes problematic when you want to release feature/2 immediately after it has passed the tests. This is because feature/1, which requires fixes and testing, gets released together.&lt;/p&gt;

&lt;p&gt;You might think that you can simply fix and test feature/1 right away, but there are various reasons in the project that make it not easy to do so. (For example, there may be a need for specification changes after testing, or the person in charge may not be available for the next 3 business days. Even if the fixes can be done quickly, there are important people who need to check it during the internal testing stage and they are not easily available.)&lt;/p&gt;

&lt;p&gt;The feature/2 branch has passed the tests, but it cannot be released immediately in the usual flow. We need to wait for the fixes and tests on the feature/1 branch to be completed, or we need to take measures such as cherry-picking only certain commits. This is relatively easy if there are only a few branches, but it becomes quite difficult to coordinate the timing of releases when the number of developers increases and they are working on separate feature developments.&lt;/p&gt;

&lt;p&gt;We only have one server for internal testing, and as long as we follow the procedure of releasing to production after passing the tests, I think the same problem will occur with GitHub Flow or GitLab Flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Branch Model
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsze57x2oty74jjw70d0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsze57x2oty74jjw70d0.png" alt="The New Workflow" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new workflow still uses three types of branches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;develop

&lt;ul&gt;
&lt;li&gt;The branch for internal testing. Reflected on the testing server.&lt;/li&gt;
&lt;li&gt;No direct commits/pushes.&lt;/li&gt;
&lt;li&gt;Updated by merging feature branches.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;master

&lt;ul&gt;
&lt;li&gt;The branch for production. Reflected on the production server.&lt;/li&gt;
&lt;li&gt;No direct commits/pushes.&lt;/li&gt;
&lt;li&gt;Updated by merging feature branches.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;feature/xxx

&lt;ul&gt;
&lt;li&gt;A local branch for developing features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Branched off from master.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Created for each feature that is to be released (multiple feature branches can exist at the same time).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The biggest difference from Git-Flow is that the &lt;strong&gt;feature branches are always branched from master&lt;/strong&gt;. It's similar to a hotfix branch in Git-Flow. This allows features to be released individually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You can release each feature individually.

&lt;ul&gt;
&lt;li&gt;There’s no need to coordinate release timing with other people or features.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;No need to create hotfix branches.

&lt;ul&gt;
&lt;li&gt;Since feature branches always branch off from master, they handle hotfixes the same way.&lt;/li&gt;
&lt;li&gt;In Git-Flow, post-release fixes involve irregular branch operations, but this method is much simpler.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need to manage each feature until it’s released.

&lt;ul&gt;
&lt;li&gt;Merging into develop doesn’t automatically mean the feature will be reflected; if a feature branch is forgotten, it may never be released.&lt;/li&gt;
&lt;li&gt;However, if you link tasks in your task management tool with feature branches, this shouldn’t be much of a problem.&lt;/li&gt;
&lt;li&gt;A tip: when creating a pull request from a feature branch to develop, simultaneously create a pull request to master to avoid forgetting.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The possibility of conflicts between features increases.

&lt;ul&gt;
&lt;li&gt;If a feature is developed over a long period without being merged into master, it may conflict with other features during the develop merge.&lt;/li&gt;
&lt;li&gt;In such cases, you’ll need to coordinate with the feature branch causing the conflict.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thoughts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In the project I’m working on, multiple features are developed in parallel. Not having to consider others’ timelines when releasing has made the process much easier.&lt;/li&gt;
&lt;li&gt;As a result, whereas we previously released once every two weeks due to the hassle of coordinating everything, we’re now able to release small feature additions and fixes every two days.&lt;/li&gt;
&lt;li&gt;I used Gitgraph.js to create the flow diagrams. It was my first time using it, but I found it really easy!&lt;/li&gt;
&lt;li&gt;I wanted a name for this new flow, and I found an article referring to a similar method as "GitFeatureFlow." This name felt right to me, so I’ll be using this term from now on. &lt;sup id="fnref1"&gt;1&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="http://developers.gnavi.co.jp/entry/GitFeatureFlow/koyama" rel="noopener noreferrer"&gt;GitFlowは使わない！シンプルな「GitFeatureFlow」を紹介します - ぐるなびをちょっと良くするエンジニアブログ&lt;/a&gt; (in Japanese) ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>git</category>
      <category>gitflow</category>
      <category>githubflow</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Perform Error Handling Across Pages in Astro</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Fri, 30 Aug 2024 16:58:54 +0000</pubDate>
      <link>https://dev.to/koyopro/how-to-perform-error-handling-across-pages-in-astro-3dk8</link>
      <guid>https://dev.to/koyopro/how-to-perform-error-handling-across-pages-in-astro-3dk8</guid>
      <description>&lt;p&gt;To handle errors across pages in Astro's server mode, you can use Middleware.&lt;/p&gt;

&lt;p&gt;By using &lt;code&gt;await next()&lt;/code&gt;, you can execute the subsequent process and handle errors with try-catch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/middleware.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro/middleware&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle errors here&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sending error logs
&lt;/h3&gt;

&lt;p&gt;If you want to send error logs when an error occurs, you can simply throw the caught error.&lt;br&gt;
This can be useful when notifying error collection services like &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;br&gt;
(Assuming there is a function called &lt;code&gt;notifyError&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/middleware.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro/middleware&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;notifyError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./notifyError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Send error logs&lt;/span&gt;
    &lt;span class="nf"&gt;notifyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Displaying a 404 page for specific errors
&lt;/h3&gt;

&lt;p&gt;If you want to display a 404 page for specific errors, you can check the error content and return a 404 page.&lt;br&gt;
For example, when the data specified by a path parameter does not exist in the database, instead of returning a 500 error, you can display a 404 page.&lt;br&gt;
(Assuming an error called &lt;code&gt;NotFoundError&lt;/code&gt; is thrown in such cases)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/middleware.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro/middleware&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NotFoundError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./errors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Display a 404 page for NotFoundError&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;NotFoundError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rewrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/404&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative Approach
&lt;/h2&gt;

&lt;p&gt;Another approach is to create a custom 500 page and handle errors.&lt;br&gt;
&lt;a href="https://docs.astro.build/en/basics/astro-pages/#custom-500-error-page" rel="noopener noreferrer"&gt;https://docs.astro.build/en/basics/astro-pages/#custom-500-error-page&lt;/a&gt;&lt;br&gt;
However, using Middleware is more versatile in my opinion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/pages/500.astro&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;notifyError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../notifyError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;NotFoundError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rewrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/404&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;notifyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="nx"&gt;Internal&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Thoughts
&lt;/h2&gt;

&lt;p&gt;According to the Middleware documentation, it is possible to &lt;a href="https://docs.astro.build/en/guides/middleware/#example-redacting-sensitive-information" rel="noopener noreferrer"&gt;rewrite the response content&lt;/a&gt;, so it feels very flexible.&lt;/p&gt;

</description>
      <category>astro</category>
    </item>
    <item>
      <title>Techniques for Synchronous DB Access in TypeScript</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Fri, 05 Jul 2024 17:46:07 +0000</pubDate>
      <link>https://dev.to/koyopro/techniques-for-synchronous-db-access-in-typescript-2976</link>
      <guid>https://dev.to/koyopro/techniques-for-synchronous-db-access-in-typescript-2976</guid>
      <description>&lt;p&gt;I am developing an ORM library for TypeScript called &lt;a href="https://www.npmjs.com/package/accel-record" rel="noopener noreferrer"&gt;Accel Record&lt;/a&gt;. Unlike other TypeScript/JavaScript ORM libraries, Accel Record adopts a synchronous API instead of an asynchronous one.&lt;/p&gt;

&lt;p&gt;However, to execute DB access synchronously, it was necessary to conduct thorough technical research.&lt;/p&gt;

&lt;p&gt;In this article, I will introduce the techniques Accel Record employs to achieve synchronous DB access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supported Databases
&lt;/h2&gt;

&lt;p&gt;Accel Record supports the following databases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQLite&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the early stages of development, priority was given to supporting SQLite and MySQL. Therefore, this article focuses on SQLite and MySQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQLite
&lt;/h2&gt;

&lt;p&gt;When using SQLite with Node.js, the &lt;a href="https://www.npmjs.com/package/better-sqlite3" rel="noopener noreferrer"&gt;better-sqlite3&lt;/a&gt; library is commonly used. Other ORM libraries also frequently use &lt;code&gt;better-sqlite3&lt;/code&gt; to access SQLite.&lt;/p&gt;

&lt;p&gt;Upon investigation, we found that &lt;code&gt;better-sqlite3&lt;/code&gt; inherently provides a synchronous API. Thus, executing queries to SQLite synchronously was easily achievable using &lt;code&gt;better-sqlite3&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  MySQL
&lt;/h2&gt;

&lt;p&gt;The challenge was with MySQL.&lt;/p&gt;

&lt;p&gt;When using MySQL with Node.js, the &lt;a href="https://www.npmjs.com/package/mysql2" rel="noopener noreferrer"&gt;mysql2&lt;/a&gt; library is commonly used. However, &lt;code&gt;mysql2&lt;/code&gt; only offers an asynchronous API, making it impossible to use a synchronous API. We searched for other MySQL libraries that offered a synchronous API but could not find any that were well-maintained recently.&lt;/p&gt;

&lt;p&gt;Next, we investigated whether there was a way to execute asynchronous APIs synchronously.&lt;/p&gt;

&lt;p&gt;We found several older libraries claiming to execute MySQL queries synchronously, and we examined how they achieved synchronous processing.&lt;/p&gt;

&lt;p&gt;The first method involved using &lt;a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait" rel="noopener noreferrer"&gt;Atomics.wait()&lt;/a&gt;. This method employs two threads: one for performing asynchronous operations and one for synchronously waiting for the result. Libraries such as &lt;a href="https://www.npmjs.com/package/synckit" rel="noopener noreferrer"&gt;synckit&lt;/a&gt; wrap this functionality to make it more user-friendly. However, &lt;code&gt;synckit&lt;/code&gt; cannot be used outside the main thread and is not easily usable in a multi-threaded environment. In the Accel Record project, we use Vitest for testing. Vitest performs parallel testing using Node.js &lt;code&gt;worker_threads&lt;/code&gt;, making this constraint a barrier to adoption.&lt;/p&gt;

&lt;p&gt;The second method led us to a library called &lt;a href="https://www.npmjs.com/package/sync-rpc" rel="noopener noreferrer"&gt;sync-rpc&lt;/a&gt;. This library uses &lt;a href="https://nodejs.org/api/child_process.html" rel="noopener noreferrer"&gt;Node.js's child_process module&lt;/a&gt; to create a separate process for executing asynchronous operations and waits synchronously for the result. Upon testing, we found that we could use &lt;code&gt;sync-rpc&lt;/code&gt; to leverage &lt;code&gt;mysql2&lt;/code&gt;'s asynchronous API synchronously. However, since &lt;code&gt;sync-rpc&lt;/code&gt; itself is an older library and did not always perform as expected, we incorporated its source code and made necessary modifications to achieve the desired functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  How sync-rpc Works
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sync-rpc&lt;/code&gt; operates as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The main process specifies the entry point file and starts a child process.&lt;/li&gt;
&lt;li&gt;The child process reads the entry point file and starts as a server.&lt;/li&gt;
&lt;li&gt;The main process requests function execution from the child process and waits synchronously for the result.&lt;/li&gt;
&lt;li&gt;The child process executes the asynchronous function and returns the result to the main process.&lt;/li&gt;
&lt;li&gt;The main process receives the result from the child process and continues processing synchronously.&lt;/li&gt;
&lt;li&gt;When the main process exits, the child process also terminates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using &lt;code&gt;sync-rpc&lt;/code&gt;, we realized that any asynchronous process could be used synchronously from the perspective of the main process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Implementation of Accel Record
&lt;/h2&gt;

&lt;p&gt;Currently, by using &lt;code&gt;sync-rpc&lt;/code&gt;, we can execute asynchronous processes synchronously. Therefore, regardless of the database engine, queries are executed through &lt;code&gt;sync-rpc&lt;/code&gt; for SQLite and PostgreSQL as well.&lt;/p&gt;

&lt;p&gt;Specifically, SQL construction is performed in the main process, and only the query execution is handled by the child process using &lt;code&gt;sync-rpc&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Improvements
&lt;/h2&gt;

&lt;p&gt;While the current implementation uses &lt;code&gt;sync-rpc&lt;/code&gt; to execute asynchronous processes synchronously, it relies on launching a child process.&lt;/p&gt;

&lt;p&gt;However, using child processes has its drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overhead of inter-process communication

&lt;ul&gt;
&lt;li&gt;There is overhead due to data exchange between the main process and the child process.&lt;/li&gt;
&lt;li&gt;Generally, this overhead is not significantly large compared to DB access latency, so it may not be a major issue in this case.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Operational complexity

&lt;ul&gt;
&lt;li&gt;Launching child processes can complicate operations.&lt;/li&gt;
&lt;li&gt;Currently, we depend on Node.js's &lt;code&gt;child_process&lt;/code&gt; for launching child processes, which might make it difficult to operate in environments other than Node.js.&lt;/li&gt;
&lt;li&gt;It is expected to work properly in typical Node.js environments and serverless environments where Node.js runs (e.g., AWS Lambda, Vercel Functions).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;If we find a method that can overcome these drawbacks, we would consider adopting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We introduced the techniques Accel Record considered and adopted to achieve synchronous DB access. During the research phase, we explored methods to execute asynchronous processes synchronously using multi-threading and inter-process communication. Ultimately, we adopted &lt;code&gt;sync-rpc&lt;/code&gt;, which spawns a separate process, to execute queries synchronously.&lt;/p&gt;

&lt;p&gt;Please check out '&lt;a href="https://dev.to/koyopro/introduction-to-accel-record-a-typescript-orm-using-the-active-record-pattern-2oeh"&gt;Introduction to "Accel Record": A TypeScript ORM Using the Active Record Pattern&lt;/a&gt;' and the &lt;a href="https://github.com/koyopro/accella/blob/main/packages/accel-record/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt; to see what kind of interface Accel Record can achieve by adopting a synchronous API.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>node</category>
      <category>orm</category>
      <category>database</category>
    </item>
    <item>
      <title>Even Server-Side TypeScript Needs the Option to Avoid Asynchronous Processing</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Wed, 26 Jun 2024 17:15:11 +0000</pubDate>
      <link>https://dev.to/koyopro/even-server-side-typescript-needs-the-option-to-avoid-asynchronous-processing-1opm</link>
      <guid>https://dev.to/koyopro/even-server-side-typescript-needs-the-option-to-avoid-asynchronous-processing-1opm</guid>
      <description>&lt;p&gt;I am developing a TypeScript ORM library called &lt;a href="https://www.npmjs.com/package/accel-record" rel="noopener noreferrer"&gt;Accel Record&lt;/a&gt;.&lt;br&gt;
Unlike other TypeScript/JavaScript ORM libraries, Accel Record adopts a synchronous API instead of an asynchronous API.&lt;/p&gt;

&lt;p&gt;In the process of examining the ORM interface, we compared the advantages and disadvantages of asynchronous and synchronous APIs. In this article, I would like to organize those thoughts and discuss the idea that we might need an option to develop in synchronous processing even in server-side TypeScript.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Is Asynchronous Processing in TypeScript/JavaScript?
&lt;/h2&gt;

&lt;p&gt;When running JavaScript on the server side, Node.js is often used.&lt;br&gt;
Node.js uses a single-threaded asynchronous I/O model, and it is common to implement applications that use asynchronous processing.&lt;/p&gt;

&lt;p&gt;The way to write asynchronous processing has historically changed, but currently, writing with &lt;code&gt;async/await&lt;/code&gt; is mainstream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Functions that perform asynchronous processing are marked with `async` and return a Promise&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Process to retrieve users from the DB&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Use await to wait for the result of the asynchronous process&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As shown above, functions that perform asynchronous processing are implemented by marking them with &lt;code&gt;async&lt;/code&gt; and returning a Promise. On the caller side, the result of the asynchronous process is awaited using &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the past, JavaScript achieved asynchronous processing using callback functions, but recently, using &lt;code&gt;async/await&lt;/code&gt; has made writing asynchronous processing more intuitive. While the development experience has greatly improved, the necessity to be aware of whether the called function performs asynchronous processing and write &lt;code&gt;await&lt;/code&gt; accordingly remains unchanged.&lt;/p&gt;

&lt;p&gt;Calling an asynchronous function without &lt;code&gt;await&lt;/code&gt; can lead to unintended behavior. Therefore, it is necessary to consider whether the called function is synchronous or asynchronous and decide whether to write &lt;code&gt;await&lt;/code&gt;, a task that needs to be done every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous Processing Used on the Server Side
&lt;/h2&gt;

&lt;p&gt;If there are hardly any asynchronous calls, the above task may not be much of an issue. However, in server-side development for web applications, asynchronous processing is often used. This is because DB access in JavaScript libraries is basically implemented as asynchronous processing.&lt;/p&gt;

&lt;p&gt;In server-side processing for web applications, the main flow is to receive an HTTP request, perform DB access such as data retrieval or writing, and return the final result as an HTTP response. The more complex the application, the more places where DB access is needed, and asynchronous processing is often used.&lt;/p&gt;

&lt;p&gt;When there are many places to write asynchronous processing, the necessity to be aware of whether to write &lt;code&gt;await&lt;/code&gt; increases. Compared to cases where asynchronous processing is not used at all, the cost of caring for such detailed parts increases, reducing development efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Using Asynchronous Processing
&lt;/h2&gt;

&lt;p&gt;So, what are the benefits of asynchronous processing, and why is it used?&lt;/p&gt;

&lt;p&gt;The most significant advantage is the improvement of system performance.&lt;/p&gt;

&lt;p&gt;In execution environments like Node.js, using asynchronous processing allows the time spent waiting for I/O to be utilized for other processes. As a result, it is expected that the overall system performance will be better than if asynchronous processing is not used. For example, when there is a process to access the DB upon receiving an HTTP request, it becomes possible to accept other requests while waiting for the response from the DB.&lt;/p&gt;

&lt;p&gt;Node.js has the concept of an event loop, and it is considered important for performance to not block the event loop by properly using asynchronous processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Option to Avoid Asynchronous Processing
&lt;/h2&gt;

&lt;p&gt;However, during the process of examining the ORM interface, I started to question whether it is necessary to always use asynchronous processing just because it is a JavaScript execution environment. I realized that by assuming asynchronous processing, it becomes difficult to abstract the library and that every method that may involve DB access requires the use of &lt;code&gt;await&lt;/code&gt;. I felt that if DB access can be done synchronously, it would allow for the realization of an ideal interface and create a more user-friendly library.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; While implementing the application with a synchronous approach may result in some decrease in system performance compared to using asynchronous processing, there is also the benefit of reducing the effort of determining whether a function is asynchronous and adding &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Taking these factors into consideration, I believe that by implementing a &lt;strong&gt;synchronous-centric approach instead of relying on asynchronous processing, we can improve the development efficiency of the application&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Depending on the nature of the product being developed, prioritizing development efficiency over system performance may be desirable in some cases. And I believe this is a fairly common scenario.&lt;/p&gt;

&lt;p&gt;In other languages commonly used for server-side development, it is common to not use asynchronous processing. It is not always necessary to pursue performance by using asynchronous processing, and if there are other advantages, it is often adopted. (In fact, I still feel that choosing TypeScript/JavaScript for server-side development is not yet mainstream.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages of Choosing TypeScript for Server-Side Development
&lt;/h2&gt;

&lt;p&gt;So, if we prioritize development efficiency over performance, are there any reasons to choose TypeScript for server-side development? I believe there are two major advantages:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Reducing Context Switching by Unifying Development Languages with the Frontend
&lt;/h3&gt;

&lt;p&gt;Currently, TypeScript is widely used for frontend development in web applications. By adopting TypeScript for server-side development as well, the commonality with the frontend can be improved, and the burden on developers can be reduced by lowering the cost of switching languages.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Improved Development Experience Through Type Safety and Type Support
&lt;/h3&gt;

&lt;p&gt;TypeScript is a statically typed language, which makes it easier to receive editor support such as autocompletion and allows for early bug detection through type checking. This type safety is a very powerful factor, especially in the development of large-scale applications. Compared to other languages chosen for server-side development, which are not always highly type-safe, the advantages of choosing TypeScript are significant.&lt;/p&gt;

&lt;p&gt;These two advantages make it worthwhile to choose TypeScript for server-side development, even when prioritizing development efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing Development Efficiency in Server-Side TypeScript
&lt;/h2&gt;

&lt;p&gt;From the above two points, development in TypeScript has the characteristic of maintaining high development efficiency. However, the current TypeScript server-side development environment, which requires an asynchronous processing-centric implementation, seems to be a factor that lowers that development efficiency. Moreover, it seems to affect various libraries. Due to the assumption of asynchronous processing, ideal interfaces may not always be adopted, resulting in compromised usability.&lt;/p&gt;

&lt;p&gt;As a result, the advantage of choosing TypeScript for server-side development might be halved.&lt;/p&gt;

&lt;p&gt;For server-side TypeScript development to become more widespread, I believe it is necessary to have the option to develop without using asynchronous processing. It is necessary to be able to choose a development experience where there is no need to care for asynchronous processing every time there is DB access.&lt;/p&gt;

&lt;p&gt;When developing a new TypeScript ORM library, I decided to adopt a synchronous API. The reason is that by using a synchronous API, the interface can be more abstracted, and I believe the experience of library users will improve more than with an asynchronous API.&lt;/p&gt;

&lt;p&gt;Until now, server-side JavaScript might have been chosen for performance.&lt;/p&gt;

&lt;p&gt;However, I think server-side TypeScript might be chosen for development efficiency. And to support that, I believe the option to avoid asynchronous processing is necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I discussed the idea that to improve development efficiency in server-side development using TypeScript, it might be necessary to have the option to develop without using asynchronous processing.&lt;/p&gt;

&lt;p&gt;Please check out '&lt;a href="https://dev.to/koyopro/introduction-to-accel-record-a-typescript-orm-using-the-active-record-pattern-2oeh"&gt;Introduction to "Accel Record": A TypeScript ORM Using the Active Record Pattern&lt;/a&gt;' and the &lt;a href="https://github.com/koyopro/accella/blob/main/packages/accel-record/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt; to see what kind of interface Accel Record can achieve by adopting a synchronous API.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Please refer to '&lt;a href="https://dev.to/koyopro/why-we-adopted-a-synchronous-api-for-the-new-typescript-orm-1jm"&gt;Why We Adopted a Synchronous API for the New TypeScript ORM&lt;/a&gt;' which I wrote previously to see the impact of asynchronous processing on the ORM interface. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Why We Adopted a Synchronous API for the New TypeScript ORM</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Thu, 20 Jun 2024 16:10:09 +0000</pubDate>
      <link>https://dev.to/koyopro/why-we-adopted-a-synchronous-api-for-the-new-typescript-orm-1jm</link>
      <guid>https://dev.to/koyopro/why-we-adopted-a-synchronous-api-for-the-new-typescript-orm-1jm</guid>
      <description>&lt;p&gt;I am developing a TypeScript ORM library called &lt;a href="https://www.npmjs.com/package/accel-record"&gt;Accel Record&lt;/a&gt;. Unlike other TypeScript/JavaScript ORM libraries, Accel Record has adopted a synchronous API instead of an asynchronous one.&lt;/p&gt;

&lt;p&gt;In this article, I will explain the background and reasons for adopting a synchronous API in Accel Record.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ORM We Wanted to Create
&lt;/h2&gt;

&lt;p&gt;In the article "&lt;a href="https://dev.to/koyopro/seeking-a-type-safe-ruby-on-rails-in-typescript-i-started-developing-an-orm-1of5"&gt;Seeking a Type-Safe Ruby on Rails in TypeScript, I Started Developing an ORM&lt;/a&gt;," I introduced the start of my work on a TypeScript ORM library.&lt;/p&gt;

&lt;p&gt;My goal was to have a framework for TypeScript that is as efficient as Ruby on Rails. To achieve this, I decided to try creating an ORM in TypeScript with functionalities similar to Rails' Active Record. Hence, the first step in creating this new ORM was to imitate the API of Active Record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems with Asynchronous APIs
&lt;/h2&gt;

&lt;p&gt;In JavaScript/TypeScript, database access is typically done using asynchronous APIs with Promises or callbacks. Since libraries for database access also return Promises for each operation, the new ORM naturally implemented each API asynchronously.&lt;/p&gt;

&lt;p&gt;When executing asynchronous APIs, it is common to use &lt;code&gt;await&lt;/code&gt; to handle them in sequence. For example, when performing CRUD operations on the User model, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Create&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Read&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Update&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although it’s somewhat tedious to write &lt;code&gt;await&lt;/code&gt; each time, it wasn't a major issue initially. However, the problem became more significant when handling associations. Consider a case where the User model has a hasOne association with the Setting model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem Example 1: Updating Associations
&lt;/h3&gt;

&lt;p&gt;I wanted to write the update process like this, following the Active Record interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Setting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// This is not possible&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason for adding &lt;code&gt;await&lt;/code&gt; here is that this operation &lt;strong&gt;might&lt;/strong&gt; involve database access. In Rails' Active Record, if this setter causes a change in either the user or setting, a database access (save operation) occurs.&lt;/p&gt;

&lt;p&gt;However, since TypeScript setters cannot be asynchronous, this kind of syntax is not possible. We needed to consider an alternative interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem Example 2: Loading Associations
&lt;/h3&gt;

&lt;p&gt;When fetching associations, &lt;code&gt;await&lt;/code&gt; needs to be written each time because there is a &lt;strong&gt;possibility&lt;/strong&gt; of database access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Active Record, associations are lazy-loaded, so database access may occur when fetching associations. If the user instance already has the setting cached, no database access occurs; otherwise, it does. This also necessitated reconsidering the interface due to usability issues.&lt;/p&gt;

&lt;p&gt;Each of these issues could be somewhat resolved by designing the interface carefully. However, I felt that repeatedly making such adjustments was gradually leading the library’s usability away from the ideal. The asynchronous API was restricting the library's interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conceptual Shift to Synchronous APIs
&lt;/h2&gt;

&lt;p&gt;Rails' Active Record abstracts database access processes by associating tables with model classes. However, making these APIs asynchronous means constantly being aware of the timing of database access, which hinders abstraction and reduces development efficiency.&lt;/p&gt;

&lt;p&gt;Continuing with an asynchronous API to implement the ORM made me realize that achieving the same level of abstraction as Active Record would be difficult, and so would achieving high development efficiency, which was the initial goal.&lt;/p&gt;

&lt;p&gt;So, I reconsidered the API design and thought there might be another approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I decided to question the assumption that "database access APIs in JavaScript/TypeScript must be asynchronous."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Synchronous API calls without Promises do not require &lt;code&gt;await&lt;/code&gt;. If we could implement each API as a synchronous API rather than an asynchronous one, the aforementioned issues would not arise. There would be no need to worry about whether to add &lt;code&gt;await&lt;/code&gt; to each method call, allowing a development experience closer to Active Record.&lt;/p&gt;

&lt;p&gt;Thus, the next step was to answer the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why do JS/TS database access libraries adopt asynchronous APIs?&lt;/li&gt;
&lt;li&gt;Is it absolutely necessary to use asynchronous APIs?&lt;/li&gt;
&lt;li&gt;Can an ORM be created using synchronous APIs?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Node.js Event Loop and Synchronous Processing
&lt;/h2&gt;

&lt;p&gt;The goal of this ORM library is to provide a development experience similar to Ruby on Rails’ Active Record. Therefore, it is intended for server-side use, not for frontend use. The most common execution environment for server-side TypeScript (JavaScript) is Node.js. So, I investigated the challenges of using synchronous APIs in Node.js.&lt;/p&gt;

&lt;p&gt;The most relevant information from Node.js official documentation includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick"&gt;Node.js — The Node.js Event Loop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/learn/asynchronous-work/dont-block-the-event-loop"&gt;Node.js — Don't Block the Event Loop (or the Worker Pool)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Node.js uses a single-threaded, asynchronous I/O model, which achieves high performance by utilizing time waiting for I/O to perform other tasks. JavaScript and Node.js have the concept of an event loop, but synchronous processing can block the event loop, preventing other tasks from being processed during that time. This could potentially degrade system performance compared to using asynchronous processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Downsides of Synchronous Processing and Mitigation Strategies
&lt;/h2&gt;

&lt;p&gt;For example, if a web application handles HTTP requests only with synchronous APIs, it cannot accept other requests until the current one is completed. However, this behavior is common in other languages. In Ruby on Rails, for instance, a process typically cannot handle other requests until one request is completed.&lt;/p&gt;

&lt;p&gt;Thus, while using synchronous APIs may lower performance compared to asynchronous Node.js applications, it is not necessarily inferior in performance compared to applications in other languages.&lt;/p&gt;

&lt;p&gt;Moreover, this refers to the performance per thread. By running multiple processes or threads in parallel, overall system performance may not significantly decrease. Parallelizing server-side processing with multiple processes is very common in web applications. For instance, in Ruby, it's common to use application servers like Unicorn to run multiple processes.&lt;/p&gt;

&lt;p&gt;Even in Node.js, processes can be run in parallel, and there are mechanisms to run certain tasks on separate threads without blocking the event loop . &lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;br&gt;
While using synchronous processing in Node.js may reduce performance per thread, system-level performance degradation can potentially be avoided through system architecture.&lt;/p&gt;

&lt;p&gt;Additionally, in serverless environments like AWS Lambda, where handling multiple requests concurrently in a single process (container) is uncommon, synchronous processing may not impact performance significantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prioritizing Development Efficiency Over System Performance
&lt;/h2&gt;

&lt;p&gt;Ultimately, my goal is to have a framework for TypeScript with development efficiency comparable to Rails. What I prioritize is development efficiency, not system performance.&lt;/p&gt;

&lt;p&gt;Many development environments prioritize development efficiency over system (per-thread) performance. If performance was the only concern, faster languages like C would always be chosen for server-side development. However, languages like PHP and Ruby, which are relatively slower, are also popular. This is because these languages and frameworks are considered to provide a more efficient development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adopting a Synchronous API in the New ORM
&lt;/h2&gt;

&lt;p&gt;Based on the investigation, I have organized answers to the initial questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why do JS/TS database access libraries adopt asynchronous APIs?

&lt;ul&gt;
&lt;li&gt;To avoid blocking the JavaScript event loop. Blocking the event loop can lead to degraded system performance.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Is it absolutely necessary to use asynchronous APIs?

&lt;ul&gt;
&lt;li&gt;Not necessarily. If the degradation in performance per thread is acceptable, synchronous APIs can be used. (And, as mentioned, there are ways to mitigate this downside through system architecture.)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Can an ORM be created using synchronous APIs?

&lt;ul&gt;
&lt;li&gt;There seems to be no inherent restriction in JavaScript or Node.js preventing this.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the ORM is designed with synchronous APIs, the main downside would be a degradation in (per-thread) performance. However, as discussed, this can be mitigated through system architecture. Therefore, weighing this downside against the benefit of improved development efficiency for library users, I concluded that the benefits of adopting synchronous APIs outweigh the downsides.&lt;/p&gt;

&lt;p&gt;Thus, the new ORM, Accel Record, has adopted synchronous APIs despite being a TypeScript library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, I explained the background and reasons for adopting synchronous APIs in Accel Record.&lt;/p&gt;

&lt;p&gt;First, it was challenging to achieve the ideal ORM interface with asynchronous APIs. Asynchronous APIs restricted the library's interface. This led to investigating whether a synchronous API could be adopted. Although there were concerns about system performance, considering the project's goals, we determined that the benefits (improved development efficiency) of realizing the ideal interface were significant.&lt;/p&gt;

&lt;p&gt;To see how Accel Record achieves an ideal interface with synchronous APIs, please check out the &lt;a href="https://dev.to/koyopro/introduction-to-accel-record-a-typescript-orm-using-the-active-record-pattern-2oeh"&gt;Introduction to "Accel Record": A TypeScript ORM Using the Active Record Pattern&lt;/a&gt; or the &lt;a href="https://github.com/koyopro/accella/blob/main/packages/accel-record/README.md"&gt;README&lt;/a&gt;.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://nodejs.org/api/cluster.html#cluster"&gt;Cluster | Node.js v22.2.0 Documentation&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>typescript</category>
      <category>orm</category>
      <category>opensource</category>
      <category>activerecord</category>
    </item>
    <item>
      <title>Seeking a Type-Safe Ruby on Rails in TypeScript, I Started Developing an ORM</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Tue, 11 Jun 2024 16:49:48 +0000</pubDate>
      <link>https://dev.to/koyopro/seeking-a-type-safe-ruby-on-rails-in-typescript-i-started-developing-an-orm-1of5</link>
      <guid>https://dev.to/koyopro/seeking-a-type-safe-ruby-on-rails-in-typescript-i-started-developing-an-orm-1of5</guid>
      <description>&lt;p&gt;I am developing an ORM library for TypeScript called Accel Record. Recently, I published an article introducing this library, titled &lt;a href="https://dev.to/koyopro/introduction-to-accel-record-a-typescript-orm-using-the-active-record-pattern-2oeh"&gt;Introduction to "Accel Record": A TypeScript ORM Using the Active Record Pattern&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, I would like to explain why I decided to develop a new ORM for TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update (April 14, 2025)
&lt;/h3&gt;

&lt;p&gt;For updates on the development progress, please check out the following article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/koyopro/after-a-year-of-wanting-a-typed-rails-and-starting-development-i-released-a-framework-for-519o"&gt;After a Year of Wanting a Typed Rails and Starting Development, I Released a Framework for TypeScript&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Motivation
&lt;/h2&gt;

&lt;p&gt;As someone who uses Ruby on Rails for server-side development and TypeScript for front-end development, I often find myself thinking the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;I want a framework for TypeScript with development efficiency comparable to Ruby on Rails.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This desire stems from wanting to use TypeScript for server-side development as well. The reasons are mainly as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I want to develop the server-side with type safety.&lt;/li&gt;
&lt;li&gt;I want to develop both the front-end and back-end in the same language to reduce context switching and learning costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This thought process seems common, and in fact, there are cases where TypeScript is adopted for server-side development for these reasons.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Kind of Framework Do I Want?
&lt;/h2&gt;

&lt;p&gt;So, what does a framework with development efficiency comparable to Ruby on Rails look like?&lt;/p&gt;

&lt;p&gt;The requirements may vary from person to person, but in my case, I thought as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It should be able to implement functionalities for common use cases in web application development with minimal code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More specifically, I am looking for the following elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Based on developing MPA with SSR (not SPA)&lt;/li&gt;
&lt;li&gt;Able to implement CRUD functions for RDB with minimal code&lt;/li&gt;
&lt;li&gt;A framework that covers the MVC domain on the server-side&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If such a framework existed, I believe server-side development with TypeScript could be as efficient as, if not more efficient than, using Ruby on Rails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Existing Frameworks Were Not Satisfactory
&lt;/h2&gt;

&lt;p&gt;I investigated whether a framework meeting the above requirements already existed.&lt;br&gt;
The conclusion I reached was that there might not be a TypeScript framework that offers functionalities for server-side processes as efficiently and with as little code as Rails.&lt;br&gt;
In general terms, existing TypeScript frameworks seem to focus more on routing and enhancing front-end/back-end integration. While they are rich in features related to View and Controller (in MVC terms), the Model part seems weaker compared to Rails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do We Need an ORM Like Active Record?
&lt;/h2&gt;

&lt;p&gt;In Rails, the ORM handling the Model role is Active Record.&lt;br&gt;
Of course, TypeScript also has ORMs, but Rails’ Active Record is more than just an ORM. It provides various functionalities related to the corresponding domain model, not just operations on DB records.&lt;/p&gt;

&lt;p&gt;Rails is characterized by its ability to implement common functionalities with minimal code. This is possible because the model classes provided by Active Record have many features. They are tightly integrated with other parts like routing and controllers, resulting in high development efficiency.&lt;/p&gt;

&lt;p&gt;From this perspective, I thought that to achieve the goal, TypeScript needs a highly functional ORM similar to Rails’ Active Record.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Elements Should the ORM Have?
&lt;/h2&gt;

&lt;p&gt;To provide an efficient development experience in TypeScript similar to Rails, I thought the ORM should have the following elements:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Active Record Pattern
&lt;/h3&gt;

&lt;p&gt;It should adopt the Active Record pattern. This is because I want one class to handle not only operations on DB records but also features related to the corresponding domain model.&lt;br&gt;
For instance, with an ORM adopting the Table Gateway pattern, the retrieved records are plain objects, making it difficult to attach methods related to the model.&lt;br&gt;
Additionally, an Active Record pattern ORM would make it easier to integrate with features like routing and View as done in Rails.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Type Safety
&lt;/h3&gt;

&lt;p&gt;Leveraging the advantages of TypeScript, the operations and query interface of the ORM should be type-safe.&lt;br&gt;
The initial motivation was to develop the server-side with type safety, so sufficient type support is expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  No Existing ORM Met the Requirements
&lt;/h2&gt;

&lt;p&gt;Given the above considerations, I investigated existing ORMs but could not find one that was type-safe and adopted the Active Record pattern.&lt;/p&gt;

&lt;p&gt;For example, the increasingly popular &lt;a href="https://www.prisma.io" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt; has high type safety but adopts the Table Gateway pattern.&lt;br&gt;
The closest fit was &lt;a href="https://typeorm.io" rel="noopener noreferrer"&gt;TypeORM&lt;/a&gt;, which uses the Active Record pattern, but its type support is weaker compared to recent ORMs, and its release frequency has been low recently.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Decided to Try Developing an ORM
&lt;/h2&gt;

&lt;p&gt;Based on the above, I decided to start developing a type-safe ORM in TypeScript that adopts the Active Record pattern.&lt;br&gt;
I wanted to try developing it to see if it was feasible and identify any potential challenges.&lt;/p&gt;

&lt;p&gt;The subsequent development process will be detailed in another article, but ultimately, I published the new ORM as &lt;a href="https://www.npmjs.com/package/accel-record" rel="noopener noreferrer"&gt;Accel Record&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, I outlined why I decided to develop a new ORM for TypeScript.&lt;/p&gt;

&lt;p&gt;The initial motivation was the desire for a framework in TypeScript with development efficiency comparable to Ruby on Rails. As I continued researching existing libraries, I realized the need for a type-safe ORM in TypeScript adopting the Active Record pattern.&lt;/p&gt;

&lt;p&gt;Thus, I started development, and eventually released the library. To see what kind of ORM it has become, please check out &lt;a href="https://dev.to/koyopro/introduction-to-accel-record-a-typescript-orm-using-the-active-record-pattern-2oeh"&gt;Introduction to "Accel Record": A TypeScript ORM Using the Active Record Pattern&lt;/a&gt; and the &lt;a href="https://github.com/koyopro/accella/blob/main/packages/accel-record/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>rails</category>
      <category>orm</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Introduction to "Accel Record": A TypeScript ORM Using the Active Record Pattern</title>
      <dc:creator>koyopro</dc:creator>
      <pubDate>Tue, 04 Jun 2024 18:57:50 +0000</pubDate>
      <link>https://dev.to/koyopro/introduction-to-accel-record-a-typescript-orm-using-the-active-record-pattern-2oeh</link>
      <guid>https://dev.to/koyopro/introduction-to-accel-record-a-typescript-orm-using-the-active-record-pattern-2oeh</guid>
      <description>&lt;p&gt;In this article, we'll briefly introduce &lt;a href="https://www.npmjs.com/package/accel-record" rel="noopener noreferrer"&gt;Accel Record&lt;/a&gt;, an ORM for TypeScript that we're developing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of Accel Record
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/accel-record" rel="noopener noreferrer"&gt;accel-record - npm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accel Record is a type-safe, synchronous ORM for TypeScript. &lt;br&gt;
It adopts the Active Record pattern, with an interface heavily influenced by Ruby on Rails' Active Record.&lt;/p&gt;

&lt;p&gt;It uses Prisma for schema management and migration, allowing you to use your existing Prisma schema directly.&lt;/p&gt;

&lt;p&gt;&lt;del&gt;As of June 2024, it supports MySQL and SQLite, with plans to support PostgreSQL in the future.&lt;/del&gt;&lt;br&gt;
MySQL, PostgreSQL, and SQLite are supported.&lt;/p&gt;
&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Active Record pattern&lt;/li&gt;
&lt;li&gt;Type-safe classes&lt;/li&gt;
&lt;li&gt;Synchronous API&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Native ESM&lt;/li&gt;
&lt;li&gt;Support for MySQL, PostgreSQL, and SQLite&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will introduce some of these features in more detail below.&lt;/p&gt;
&lt;h2&gt;
  
  
  Usage Example
&lt;/h2&gt;

&lt;p&gt;For example, if you define a User model as follows,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// prisma/schema.prisma&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;        &lt;span class="nx"&gt;Int&lt;/span&gt;    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;lastName&lt;/span&gt;  &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;age&lt;/span&gt;       &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can use it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;john&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findBy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;john&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also extend models to define custom methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/models/user.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./applicationRecord.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserModel&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Define a method to get the full name&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; "John Doe"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more detailed usage, see the &lt;a href="https://github.com/koyopro/accella/blob/main/packages/accel-record/README-ja.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Active Record Pattern
&lt;/h2&gt;

&lt;p&gt;Accel Record adopts the Active Record pattern. &lt;br&gt;
Its interface is heavily influenced by Ruby on Rails' Active Record. &lt;br&gt;
Those with experience in Rails should find it easy to understand how to use it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example of Creating and Saving Data
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NewUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create a User&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; 1&lt;/span&gt;

&lt;span class="c1"&gt;// You can also write it like this&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NewUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Smith&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Example of Retrieving Data
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`IDs of all users: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;allUsers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Name of the first user: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;firstUser&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;john&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findBy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ID of the user with the name John: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;john&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;does&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Number of users with the last name Doe: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;does&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Type-safe Classes
&lt;/h2&gt;

&lt;p&gt;Accel Record provides type-safe classes. &lt;br&gt;
The query API also includes type information, allowing you to leverage TypeScript's type system. &lt;br&gt;
Effective editor autocompletion and type checking help maintain high development efficiency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F62874%2Ff29fac2e-bbeb-17c0-d0a7-667c993c57b6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F62874%2Ff29fac2e-bbeb-17c0-d0a7-667c993c57b6.gif" alt="demo.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A notable feature is that the type changes based on the model's state, so we'll introduce it here.&lt;/p&gt;

&lt;p&gt;Accel Record provides types called &lt;code&gt;NewModel&lt;/code&gt; and &lt;code&gt;PersistedModel&lt;/code&gt; to distinguish between new and saved models. &lt;br&gt;
Depending on the schema definition, some properties will allow undefined in &lt;code&gt;NewModel&lt;/code&gt; but not in &lt;code&gt;PersistedModel&lt;/code&gt;. &lt;br&gt;
This allows you to handle both new and saved models in a type-safe manner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NewUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/*
Example of NewModel:
The NewUser type represents a model before saving and has the following type.

interface NewUser {
  id: number | undefined;
  firstName: string | undefined;
  lastName: string | undefined;
  age: number | undefined;
}
*/&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NewUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="cm"&gt;/*
Example of PersistedModel:
The User type represents a saved model and has the following type.

interface User {
  id: number;
  firstName: string;
  lastName: string;
  age: number | undefined;
}
*/&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;persistedUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using methods like &lt;code&gt;save()&lt;/code&gt;, you can convert a NewModel type to a PersistedModel type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NewUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Prepare a user of the NewModel type&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NewUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If save is successful, the NewModel is converted to a PersistedModel.&lt;/span&gt;
  &lt;span class="c1"&gt;// In this block, user is treated as a User type.&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// user.id is of type number&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If save fails, the NewModel remains the same type.&lt;/span&gt;
  &lt;span class="c1"&gt;// In this block, user remains of type NewUser.&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// user.id is of type number | undefined&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Synchronous API
&lt;/h2&gt;

&lt;p&gt;Accel Record provides a synchronous API that does not use Promises or callbacks, even for database access. &lt;br&gt;
This allows you to write simpler code without using await, etc. &lt;br&gt;
This was mainly adopted to enhance application development efficiency.&lt;/p&gt;

&lt;p&gt;By adopting a synchronous API, you can perform related operations intuitively, as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Setting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Setting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, World!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Operations on hasOne associations are automatically saved&lt;/span&gt;
&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Operations on hasMany associations are also automatically saved&lt;/span&gt;
&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Related entities are lazily loaded and cached&lt;/span&gt;
&lt;span class="c1"&gt;// You don't need to explicitly instruct to load related entities when fetching a user.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// setting is loaded and cached&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// posts are loaded and cached&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Synchronous APIs have some drawbacks compared to implementations using asynchronous APIs, primarily related to performance. &lt;br&gt;
We will discuss these trade-offs in a separate article. &lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Validation
&lt;/h2&gt;

&lt;p&gt;Like Ruby on Rails' Active Record, Accel Record also provides validation features.&lt;/p&gt;

&lt;p&gt;You can define validations by overriding the &lt;code&gt;validateAttributes&lt;/code&gt; method of the BaseModel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/models/user.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./applicationRecord.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserModel&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="nf"&gt;validateAttributes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Validate that firstName is not empty&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firstName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;presence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using methods like save, validations are automatically executed, and save processing only occurs if there are no errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// If validation errors occur, save returns false.&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If validation errors do not occur, saving succeeds&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If validation errors occur, saving fails&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This concludes our brief introduction to Accel Record. &lt;br&gt;
If you are interested, please check the links below for more details. &lt;/p&gt;

&lt;p&gt;accel-record - npm &lt;br&gt;
&lt;a href="https://www.npmjs.com/package/accel-record" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/accel-record&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seeking a Type-Safe Ruby on Rails in TypeScript, I Started Developing an ORM - DEV Community&lt;br&gt;
&lt;a href="https://dev.to/koyopro/seeking-a-type-safe-ruby-on-rails-in-typescript-i-started-developing-an-orm-1of5"&gt;https://dev.to/koyopro/seeking-a-type-safe-ruby-on-rails-in-typescript-i-started-developing-an-orm-1of5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why We Adopted a Synchronous API for the New TypeScript ORM - DEV Community&lt;br&gt;
&lt;a href="https://dev.to/koyopro/why-we-adopted-a-synchronous-api-for-the-new-typescript-orm-1jm"&gt;https://dev.to/koyopro/why-we-adopted-a-synchronous-api-for-the-new-typescript-orm-1jm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even Server-Side TypeScript Needs the Option to Avoid Asynchronous Processing - DEV Community&lt;br&gt;
&lt;a href="https://dev.to/koyopro/even-server-side-typescript-needs-the-option-to-avoid-asynchronous-processing-1opm"&gt;https://dev.to/koyopro/even-server-side-typescript-needs-the-option-to-avoid-asynchronous-processing-1opm&lt;/a&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://dev.to/koyopro/why-we-adopted-a-synchronous-api-for-the-new-typescript-orm-1jm"&gt;Why We Adopted a Synchronous API for the New TypeScript ORM&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>typescript</category>
      <category>activerecord</category>
      <category>orm</category>
      <category>database</category>
    </item>
  </channel>
</rss>
