<?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>Forem: Diego Resendez</title>
    <description>The latest articles on Forem by Diego Resendez (@chrnx).</description>
    <link>https://forem.com/chrnx</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%2F254763%2F3f14b1a7-65c4-400e-bb63-319c17d380fe.png</url>
      <title>Forem: Diego Resendez</title>
      <link>https://forem.com/chrnx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/chrnx"/>
    <language>en</language>
    <item>
      <title>Processing 1M Chess Games in 15 Seconds with Rust</title>
      <dc:creator>Diego Resendez</dc:creator>
      <pubDate>Tue, 31 Mar 2026 04:24:01 +0000</pubDate>
      <link>https://forem.com/chrnx/processing-1m-chess-games-in-15-seconds-with-rust-pe3</link>
      <guid>https://forem.com/chrnx/processing-1m-chess-games-in-15-seconds-with-rust-pe3</guid>
      <description>&lt;p&gt;I train self-supervised models on chess game data. My Python pipeline using python-chess took 25 minutes to parse and tokenize 1M games from Lichess PGN dumps. I rewrote it in Rust. It now takes 15 seconds.&lt;/p&gt;

&lt;p&gt;This post covers the architecture, why Rust was the right choice, and what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Training a chess move predictor requires converting PGN (Portable Game Notation) files into tokenized sequences — arrays of integer IDs that a neural network can consume. A typical Lichess monthly dump has 5M+ games in a zstd-compressed PGN file.&lt;/p&gt;

&lt;p&gt;My Python pipeline had three bottlenecks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;PGN parsing&lt;/strong&gt; — python-chess parses SAN notation, validates moves on a board, handles edge cases. Correct, but slow. ~15 minutes for 1M games.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokenization&lt;/strong&gt; — converting validated UCI moves to token IDs, tracking piece types and turns. ~10 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory&lt;/strong&gt; — all games loaded into a Python list of dicts. 1M games = ~4GB RAM.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Rust rewrite
&lt;/h2&gt;

&lt;p&gt;The tool is called &lt;a href="https://github.com/Ailed-AI/ailed-soulsteal" rel="noopener noreferrer"&gt;ailed-soulsteal&lt;/a&gt; (named after a Castlevania ability — the project has a theme).&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;Three layers, each a clean boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input Layer → Filter Layer → Output Layer
(PGN parser)   (ELO, result)  (.somabin binary)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything streams — games are parsed, filtered, tokenized, and written one at a time. Memory usage stays constant regardless of input size.&lt;/p&gt;

&lt;h3&gt;
  
  
  PGN parsing
&lt;/h3&gt;

&lt;p&gt;PGN is a messy format. Tags, comments, variations, NAGs, move numbers, results — all interleaved. I wrote a simple streaming parser that yields one &lt;code&gt;RawGame&lt;/code&gt; at a time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PgnIterator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;line_buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BufRead&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Iterator&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;PgnIterator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;R&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;type&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RawGame&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RawGame&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;// Read tags until blank line, then movetext until next blank line&lt;/span&gt;
        &lt;span class="c1"&gt;// Strip comments, NAGs, variations, move numbers&lt;/span&gt;
        &lt;span class="c1"&gt;// Return (tags, moves, result)&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;No allocations per game beyond the reused line buffer. The &lt;code&gt;RawGame&lt;/code&gt; struct holds tags as a &lt;code&gt;HashMap&amp;lt;String, String&amp;gt;&lt;/code&gt; and moves as &lt;code&gt;Vec&amp;lt;String&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Move validation with shakmaty
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://crates.io/crates/shakmaty" rel="noopener noreferrer"&gt;shakmaty&lt;/a&gt; is a pure Rust chess library. It handles SAN parsing, move validation, and piece type lookup — the same things python-chess does, but at native speed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;san&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;shakmaty&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;san&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;San&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;san_str&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.ok&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;san&lt;/span&gt;&lt;span class="nf"&gt;.to_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.ok&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;uci&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uci_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;role_to_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="nf"&gt;.role&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="nf"&gt;.play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.ok&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;This is where most of the speedup comes from. shakmaty's &lt;code&gt;play()&lt;/code&gt; is essentially a few bitboard operations — no Python overhead, no GC pressure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Binary output format
&lt;/h3&gt;

&lt;p&gt;Instead of writing JSON or CSV, I designed a binary format (&lt;code&gt;.somabin&lt;/code&gt;) optimized for ML training:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Header (64 bytes): magic, version, vocab_size, num_games, ...
Index Table:       byte offset for each game (enables random access)
Data Section:      per game: [seq_len, token_ids, turn_ids, category_ids, outcome]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The index table is the key insight. A PyTorch &lt;code&gt;Dataset.__getitem__(i)&lt;/code&gt; can seek directly to game &lt;code&gt;i&lt;/code&gt; via mmap without scanning the file. Loading 50K games takes 20ms. Random access runs at 500K games/sec.&lt;/p&gt;

&lt;p&gt;The Python reader is 30 lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomabinDataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fileno&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ACCESS_READ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;read_game&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_mm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Zstd decompression
&lt;/h3&gt;

&lt;p&gt;Lichess distributes PGN files as &lt;code&gt;.pgn.zst&lt;/code&gt;. The &lt;code&gt;zstd&lt;/code&gt; crate handles decompression transparently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.extension&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"zst"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;zstd&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Decoder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&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="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;)))&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="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&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;Auto-detected from the file extension. No separate decompression step needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filtering
&lt;/h3&gt;

&lt;p&gt;Games are filtered by metadata before tokenization — no wasted work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;GameFilter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;RawGame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Chain of filters — all must pass&lt;/span&gt;
&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="nf"&gt;.add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;EloFilter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="nf"&gt;.add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;MovesFilter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="nf"&gt;.add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ResultFilter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Decisive&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filters operate on PGN tags (strings), not board positions. Checking &lt;code&gt;WhiteElo &amp;gt;= "1000"&lt;/code&gt; is effectively free compared to move validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarks
&lt;/h2&gt;

&lt;p&gt;Processing Lichess monthly dumps (zstd compressed) on an M1 MacBook:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Month&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Games (1000-1800)&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2016-01&lt;/td&gt;
&lt;td&gt;831 MB .zst&lt;/td&gt;
&lt;td&gt;2,060,197&lt;/td&gt;
&lt;td&gt;45s&lt;/td&gt;
&lt;td&gt;46K/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016-02&lt;/td&gt;
&lt;td&gt;866 MB .zst&lt;/td&gt;
&lt;td&gt;2,071,332&lt;/td&gt;
&lt;td&gt;46s&lt;/td&gt;
&lt;td&gt;45K/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016-03&lt;/td&gt;
&lt;td&gt;994 MB .zst&lt;/td&gt;
&lt;td&gt;2,399,234&lt;/td&gt;
&lt;td&gt;54s&lt;/td&gt;
&lt;td&gt;45K/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016-04&lt;/td&gt;
&lt;td&gt;1.0 GB .zst&lt;/td&gt;
&lt;td&gt;2,438,621&lt;/td&gt;
&lt;td&gt;55s&lt;/td&gt;
&lt;td&gt;44K/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016-07&lt;/td&gt;
&lt;td&gt;1.0 GB .zst&lt;/td&gt;
&lt;td&gt;2,598,733&lt;/td&gt;
&lt;td&gt;59s&lt;/td&gt;
&lt;td&gt;44K/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;11.6M games in 4.3 minutes. The equivalent Python pipeline would take roughly 5 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Streaming wins.&lt;/strong&gt; The biggest architectural decision was making everything an iterator. Games flow through parse → filter → tokenize → write without buffering. Memory usage is constant at ~10MB regardless of input size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Binary formats beat JSON for ML.&lt;/strong&gt; My first version wrote JSONL. A 1M-game JSONL file was 2GB and took 30 seconds to load in Python. The &lt;code&gt;.somabin&lt;/code&gt; binary for the same data is 550MB and loads in 20ms via mmap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;shakmaty is excellent.&lt;/strong&gt; Chess move validation is the bottleneck in any PGN pipeline. shakmaty's bitboard implementation made this a non-issue. The crate is well-documented and the API maps cleanly to what you need for tokenization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rust's type system caught real bugs.&lt;/strong&gt; The &lt;code&gt;GameParser&lt;/code&gt; and &lt;code&gt;GameTokenizer&lt;/code&gt; traits enforce separation between parsing (text → structured data) and tokenization (structured data → integers). When I mixed them up during development, the compiler told me immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;ailed-soulsteal

&lt;span class="c"&gt;# Generate vocabulary&lt;/span&gt;
soulsteal vocab &lt;span class="nt"&gt;--generate&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; vocab.json

&lt;span class="c"&gt;# Tokenize a Lichess dump&lt;/span&gt;
soulsteal tokenize lichess_2016-02.pgn.zst &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; train.somabin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vocab&lt;/span&gt; vocab.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--elo&lt;/span&gt; 1000:1800

&lt;span class="c"&gt;# Inspect&lt;/span&gt;
soulsteal info train.somabin
soulsteal stats train.somabin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pre-tokenized datasets are available on &lt;a href="https://huggingface.co/datasets/Ailed-AI/lichess-chess-1000-1800" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The tool is designed to support any turn-based game — Go (SGF), Shogi (KIF), etc. Chess is the v1 implementation, but the &lt;code&gt;GameParser&lt;/code&gt; and &lt;code&gt;GameTokenizer&lt;/code&gt; traits are game-agnostic.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was drafted with assistance from Claude (Anthropic), an AI language model. The code, benchmarks, and technical decisions described are entirely my own work.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;br&gt;
Source: &lt;a href="https://github.com/Ailed-AI/ailed-soulsteal" rel="noopener noreferrer"&gt;github.com/Ailed-AI/ailed-soulsteal&lt;/a&gt;&lt;br&gt;
crates.io: &lt;a href="https://crates.io/crates/ailed-soulsteal" rel="noopener noreferrer"&gt;ailed-soulsteal&lt;/a&gt;&lt;br&gt;
License: MIT&lt;/p&gt;

</description>
      <category>rust</category>
      <category>chess</category>
      <category>machinelearning</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Introducing Expressive Tea</title>
      <dc:creator>Diego Resendez</dc:creator>
      <pubDate>Wed, 26 May 2021 22:52:46 +0000</pubDate>
      <link>https://forem.com/expressive-tea/introducing-expressive-tea-26k7</link>
      <guid>https://forem.com/expressive-tea/introducing-expressive-tea-26k7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When I started coding &lt;a href="https://expressive-tea.io"&gt;Expressive Tea&lt;/a&gt; almost two years ago, it was just a tool to increase my laziness (as most engineers) avoid setting up a project on &lt;a href="https://expressjs.com"&gt;ExpressJS&lt;/a&gt;. I was thinking about combining a language that I started to learn (Typescript) and making easier a configuration for &lt;strong&gt;ExpressJS&lt;/strong&gt;; by that moment, I did not know about &lt;a href="https://nestjs.com"&gt;NestJS&lt;/a&gt;, a very mature and fantastic Framework. I did not have a chance to research, just because I got the idea to start working in this new (at that moment) personal library.&lt;/p&gt;

&lt;p&gt;In September 2019, that personal library turned into a microframework to help build &lt;strong&gt;server-side&lt;/strong&gt; applications in &lt;strong&gt;NodeJS&lt;/strong&gt; and use modern &lt;strong&gt;Javascript&lt;/strong&gt; powered by &lt;strong&gt;Typescript&lt;/strong&gt;. Also, one of the main goals is to make this compatible with all the express middleware and plugins available when I wrote this article; the list continues to expand with ideas and recommendations from friends and users that already started using Expressive Tea.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the heck is Expressive Tea?
&lt;/h2&gt;

&lt;p&gt;I define Expressive Tea as a flexible framework that gives freedom to build their own architectures by providing descriptive decorators, a plugin engine, shareable modules, and modern Javascript.&lt;/p&gt;

&lt;p&gt;But what does that mean? As I mentioned in the introduction, it is just making developers lazier (for a good reason), helping them forget how to set up ExpressJS, and just working on the business logic. &lt;/p&gt;

&lt;p&gt;Take into consideration that Expressive Tea has not come with anything out of the box; this means no additional plugins or middlewares with exceptions of certain features like &lt;strong&gt;Websockets&lt;/strong&gt;, and you might need to provide the necessary plugins to achieve your unique server flavor.&lt;/p&gt;

&lt;p&gt;So, telling that, you might still wonder where the flexibility or freedom is in something with nothing more than essential matters. Pleasantly, Expressive Tea has no attachment on any naming convention, any special plugin, or any data source, so this allows you to freely change the application in the way you prefer and introduce two critical entities in the Expressive Tea framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plugins
&lt;/h3&gt;

&lt;p&gt;A Plugin is an entity to implement features that we need for our applications, such as assigning a connection to MongoDB or changing the server behavior such as adding a view engine, adding authorization middleware, or just adding a body parser.&lt;/p&gt;

&lt;p&gt;But more importantly, this can be share between Expressive Tea projects; the method depends on you (like npm package, git submodule, or copy and paste). Still, essentially you will get a path to create many plugins and combine them to flavor on your own Expressive Tea.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modules
&lt;/h3&gt;

&lt;p&gt;A Module is pretty similar to Plugin, but instead of change the behavior of the server is used to create a placeholder route to encapsulate controllers and services to respond to a user request through declared endpoints. &lt;/p&gt;

&lt;p&gt;All the endpoints are defined in each ** controller** that have a corresponding method to respond to the client; in simple terms, a Module helps to contain routers in one place, by example, we can have a signing module that contains all the endpoints to sing in or sign up a user into our application.&lt;/p&gt;

&lt;p&gt;As same as Plugins, Modules can be share between Expressive Tea applications if they have the following specifications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All code must be in the same place; anyone has their own structure and scaffolding methods, ideally set all in the same root directory; the other thing is depends on you ;).&lt;/li&gt;
&lt;li&gt;Should not contain external code, like external classes, services, or constants with certain exceptions like Expressive Tea settings and node packages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;We have enough for boring theory; it might be better with a bit of demonstration of how this is working, shall we?..&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies.
&lt;/h3&gt;

&lt;p&gt;We can start by installing &lt;code&gt;tea&lt;/code&gt; CLI with the following command:&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; &lt;span class="nt"&gt;-g&lt;/span&gt; @expressive-tea/tea
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Tea CLI is under development; take patience ;), send comments or open an issue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Create a project.
&lt;/h3&gt;

&lt;p&gt;Once &lt;code&gt;tea&lt;/code&gt; finished to install, now is time to start to create a project with the following command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tea brew to-do
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;Follow all the instructions and change them if you want them. Once create project finished and create our &lt;code&gt;to-do&lt;/code&gt; project, just go inside with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;to-do
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and execute the project with:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tea serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;if you get the following message, congratulations; this is your first Expressive Tea application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] 14:08:05 ts-node-dev ver. 1.1.6 (using ts-node ver. 9.1.1, typescript ver. 3.9.9)
Fri, 21 May 2021 19:08:06 GMT helmet deprecated helmet.noCache is deprecated and will be removed in helmet@4. You can use the `nocache` module instead. For more, see https://github.com/helmetjs/helmet/issues/215. at server/plugins/express.ts:59:31
Running HTTP Server on [8080]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;if you open your browser and type &lt;code&gt;http://localhost:8080/&lt;/code&gt;&lt;br&gt;
you will get the following response:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mQ_ca5TK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vz6vxaru0kfl9hfvyxzn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mQ_ca5TK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vz6vxaru0kfl9hfvyxzn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  time to play
&lt;/h3&gt;

&lt;p&gt;Let the fun start :), it is time to move our a little bit our project; the main goal is to create a REST API for a To-Do application, and the first thing to do is remove the static HTML that shows as the index.&lt;/p&gt;

&lt;p&gt;It is time to open the &lt;code&gt;main.ts&lt;/code&gt; file from the application and remove the line &lt;code&gt;@Static('./public')&lt;/code&gt; (you can use the IDE that you like) as is the following screencast.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now the main page is gone is time to implement our first endpoint to this new REST API.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;The application now will return a To-Do list that is on memory, for now, just to not overcomplicate the example.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;For this example need to add one of the elements from Expressive Tea for decorating arguments in the endpoints methods; the &lt;code&gt;@body()&lt;/code&gt; annotation is part of the package on &lt;code&gt;@zerooneit/expressive-tea/decorators/annotations&lt;/code&gt; and helps to extract the whole body object as is showing in the screencast above.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More details you will get in the documentation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Extra
&lt;/h3&gt;

&lt;p&gt;The main goal is to present you a little bit of Expressive Tea, but how to leave without giving you an extra; in this case, I will show you how to modify the Express plugin in the demo to just show a console log.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Check our live playground.
&lt;/h3&gt;

&lt;p&gt;I know this is not extensive details in this article, and this is because I just want to inform you more than training you, but if you're going to sneak pick a little more content, there is a series of articles that you can follow, it might be a little old but contains the essential:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@diego.resendez/a-simple-rest-api-with-expressive-tea-part-three-6b09f2ad73ad" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QplbVQgo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/56/56/2%2AtQQDZttulH9Uzvzs5M5-JA.jpeg" alt="Diego Resendez"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@diego.resendez/a-simple-rest-api-with-expressive-tea-part-three-6b09f2ad73ad" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;A Simple REST API with Expressive Tea | Part Three | by Diego Resendez | Medium&lt;/h2&gt;
      &lt;h3&gt;Diego Resendez ・ &lt;time&gt;Oct 3, 2019&lt;/time&gt; ・ 
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ze5yh_2q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/medium_icon-90d5232a5da2369849f285fa499c8005e750a788fdbf34f5844d5f2201aae736.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@diego.resendez/a-simple-rest-api-with-expressive-tea-part-two-718b1e3ed265" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QplbVQgo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/56/56/2%2AtQQDZttulH9Uzvzs5M5-JA.jpeg" alt="Diego Resendez"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@diego.resendez/a-simple-rest-api-with-expressive-tea-part-two-718b1e3ed265" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;A Simple REST API with Expressive Tea | Part Two | by Diego Resendez | Medium&lt;/h2&gt;
      &lt;h3&gt;Diego Resendez ・ &lt;time&gt;Oct 8, 2019&lt;/time&gt; ・ 
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ze5yh_2q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/medium_icon-90d5232a5da2369849f285fa499c8005e750a788fdbf34f5844d5f2201aae736.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@diego.resendez/a-simple-rest-api-with-expressive-tea-part-one-97ef6c08f9c4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QplbVQgo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/56/56/2%2AtQQDZttulH9Uzvzs5M5-JA.jpeg" alt="Diego Resendez"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@diego.resendez/a-simple-rest-api-with-expressive-tea-part-one-97ef6c08f9c4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;A Simple REST API with Expressive Tea | Part One. | by Diego Resendez | Medium&lt;/h2&gt;
      &lt;h3&gt;Diego Resendez ・ &lt;time&gt;Sep 23, 2019&lt;/time&gt; ・ 
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ze5yh_2q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/medium_icon-90d5232a5da2369849f285fa499c8005e750a788fdbf34f5844d5f2201aae736.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Or you can use our sandbox in CodeSandbox, where you will be able to play a little bit or understand more about Expressive Tea.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/expressive-tea-2kmg7?runonclick=1"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;As I continue improving this project, I learned and still learn many things technically. Still, the real value is discovering new things to do, challenges, and, more importantly, being active in the Open Source community. Even if this project does not grow much or maybe overextend it, the self-learning of many soft skills comes with this, for example, managing your time, executing a project, and actively checking new features.&lt;/p&gt;

&lt;p&gt;Hence, as this work I just take it as a personal challenge it will be good to have help, so if any of you want to help, improving code, adding new issues, do a review, write an article, maybe a YouTube video, or ask for new feature down below you will get the link to the Github Repository of this project.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Expressive-Tea"&gt;
        Expressive-Tea
      &lt;/a&gt; / &lt;a href="https://github.com/Expressive-Tea/expresive-tea"&gt;
        expresive-tea
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Express and Typescript REST Service Template.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Without more to aggregate, let me thank you for reading my article, which is the first on this platform.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>node</category>
      <category>express</category>
    </item>
  </channel>
</rss>
