<?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: Mohamed El-Bably</title>
    <description>The latest articles on Forem by Mohamed El-Bably (@melbably).</description>
    <link>https://forem.com/melbably</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%2F332871%2F6377cf01-75ca-4a55-b29f-11dad05de8ec.png</url>
      <title>Forem: Mohamed El-Bably</title>
      <link>https://forem.com/melbably</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/melbably"/>
    <language>en</language>
    <item>
      <title>Rate Limiting: A Dynamic Distributed Rate Limiting with Redis</title>
      <dc:creator>Mohamed El-Bably</dc:creator>
      <pubDate>Tue, 14 Nov 2023 16:15:47 +0000</pubDate>
      <link>https://forem.com/melbably/rate-limiting-a-dynamic-distributed-rate-limiting-with-redis-4nlp</link>
      <guid>https://forem.com/melbably/rate-limiting-a-dynamic-distributed-rate-limiting-with-redis-4nlp</guid>
      <description>&lt;p&gt;In our previous article, “&lt;a href="https://dev.to/melbably/rate-limiting-the-sliding-window-algorithm-280a"&gt;Rate Limiting: The Sliding Window Algorithm&lt;/a&gt;” we explored the theoretical underpinnings of rate limiting and how the Sliding Window Algorithm serves as an effective solution. Now, it’s time to roll up our sleeves and dive into the practical side of things. In this new article, we will take that theoretical knowledge and put it into action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slide Limiter
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/m-elbably/slide-limiter"&gt;Slide Limiter&lt;/a&gt; is an open-source TypeScript implementation of a sliding window rate-limiting algorithm that provides distributed rate-limiting functionality using Redis. It allows you to limit the number of requests or actions that can be performed within a specific time window in a dynamic efficient way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture and Implementation
&lt;/h3&gt;

&lt;p&gt;Slide Limiter leverages a dynamic rate-limiting approach, allowing you to fine-tune rate limits based on their specific needs. By creating a &lt;code&gt;SlideLimiter&lt;/code&gt; instance with customizable options, such as the duration of the time window and the maximum limit of requests, users gain the flexibility to enforce rate limits tailored to their application's requirements.&lt;/p&gt;

&lt;p&gt;The architecture involves two essential components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Slide Limiter&lt;/strong&gt; (&lt;code&gt;SlideLimiter&lt;/code&gt;): This is the central orchestrator responsible for managing rate limits. By interfacing with different store options, it offers a seamless way to enforce rate limiting across applications. The heart of the Slide Limiter is the &lt;code&gt;hit&lt;/code&gt; method, which checks and enforces rate limits. If a request exceeds the defined rate limit, it can trigger the necessary actions, ensuring the orderly flow of requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage Options&lt;/strong&gt; (&lt;code&gt;RedisStore&lt;/code&gt; and &lt;code&gt;MemoryStore&lt;/code&gt;): Slide Limiter allows you to choose between &lt;code&gt;RedisStore&lt;/code&gt; and &lt;code&gt;MemoryStore&lt;/code&gt; as storage backends. &lt;code&gt;RedisStore&lt;/code&gt; is particularly well-suited for distributed systems where consistency and shared rate limiting across multiple servers are paramount. It employs a Lua script executed atomically in Redis, ensuring that rate-limiting operations are thread-safe. On the other hand, &lt;code&gt;MemoryStore&lt;/code&gt; is ideal for single-node or testing environments, offering simplicity and ease of use.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The following code snippet is almost what you need to create a simple rate limiter and validate the hits count.&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="nx"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RedisOptions&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;ioredis&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;RedisStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SlideLimiter&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;slide-limiter&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 RedisStore instance  &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;RedisStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_HOST&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;6379&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="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_DATABASE&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;  
&lt;span class="c1"&gt;// SlideLimiter options  &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="na"&gt;windowMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// 1 minute  &lt;/span&gt;
  &lt;span class="na"&gt;maxLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// n + 1  &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;limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SlideLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&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;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="c1"&gt;// Remaining requests  &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;remaining&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;limiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="c1"&gt;// Allow the action  &lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Action performed. Remaining requests: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;remaining&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="c1"&gt;// Rate limit exceeded  &lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rate limit exceeded. Please try again later.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Use the limit you need + 1, as we have one method &lt;code&gt;hit&lt;/code&gt; that does the increment and returns the count after consuming 1 hit&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By creating a &lt;code&gt;SlideLimiter&lt;/code&gt; instance with specific options, such as a &lt;code&gt;one-minute&lt;/code&gt; time window and a maximum limit of &lt;code&gt;10&lt;/code&gt; requests, we enable fine-grained control over the rate-limiting process.&lt;/p&gt;

&lt;p&gt;With a defined &lt;code&gt;bucket&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt;, we can effortlessly track and manage the rate limits for different users or entities. If the remaining requests exceed zero, the action is allowed to proceed, and the remaining request count is displayed. However, if the rate limit has been reached, the system gracefully handles the situation and informs the user that they should try again later.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adaptive Rate Limiting with Dynamic Configuration
&lt;/h4&gt;

&lt;p&gt;Dynamic rate limiting offers significant advantages when dealing with varying rate-limiting requirements across different operations or clients.&lt;/p&gt;

&lt;p&gt;This flexibility enables us to tailor rate limits to specific use cases, such as granting higher limits to premium users or imposing stricter limits on resource-intensive operations.&lt;/p&gt;

&lt;p&gt;The sliding window algorithm provides an elegant solution to this dynamic rate-limiting need by allowing real-time adjustments to the &lt;code&gt;windowMs&lt;/code&gt; and &lt;code&gt;maxLimit&lt;/code&gt; parameters.&lt;/p&gt;

&lt;p&gt;For instance, when working with a specific bucket (e.g., &lt;code&gt;main&lt;/code&gt;) and a key (e.g., &lt;code&gt;1234&lt;/code&gt;) with initial settings of &lt;code&gt;windowMs = 60000&lt;/code&gt; and &lt;code&gt;maxLimit = 5&lt;/code&gt;, we can seamlessly modify these parameters for larger or smaller values with each new call to the &lt;code&gt;hit&lt;/code&gt; function. This adaptability empowers applications to fine-tune rate limits on the fly, ensuring optimal performance and resource allocation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fine-Grained Rate Limiting Control
&lt;/h4&gt;

&lt;p&gt;Slide Limiter uses the concept of &lt;strong&gt;buckets&lt;/strong&gt; with &lt;strong&gt;keys&lt;/strong&gt; to effectively control and manage the flow of requests within an application or system. These parameters serve specific roles in the rate-limiting process:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bucket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The bucket parameter is a higher-level categorization that allows us to group rate limits for different resources or endpoints. It can be used to separate the count for specific endpoints or functionalities within your application.&lt;/p&gt;

&lt;p&gt;In the context of a web API, for example, we might want to rate limit different endpoints differently. By using a bucket, we can create distinct rate limits for different parts of your application.&lt;/p&gt;

&lt;p&gt;For instance, we could have separate buckets for &lt;code&gt;/users/auth&lt;/code&gt;, &lt;code&gt;/api/orders&lt;/code&gt;, and &lt;code&gt;/api/profile&lt;/code&gt;, each with its own rate limit configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The key parameter represents the identifier for the resource or entity we want to rate limit per bucket. It is typically associated with a specific user, IP address, or any other unique identifier for the client making the request. For example, if we want to rate limit requests from different users or clients separately, we can use the user’s ID or IP address as the key. The key is an essential part of the rate-limiting process because it allows us to track and limit requests on a per-client basis.&lt;/p&gt;

&lt;p&gt;Together, &lt;strong&gt;buckets&lt;/strong&gt; and &lt;strong&gt;keys&lt;/strong&gt; enable fine-grained control over rate limiting. Buckets categorize rate limits, while keys distinguish requests from different clients or entities, ensuring that rate limiting is not only efficient but also customizable to the specific needs of your application.&lt;/p&gt;

&lt;h4&gt;
  
  
  Redis Store
&lt;/h4&gt;

&lt;p&gt;Redis is used here to store and update hits count atomically, which is suitable for distributed systems where rate-limiting needs to be consistent and shared across multiple servers.&lt;/p&gt;

&lt;h4&gt;
  
  
  How Redis Store Works
&lt;/h4&gt;

&lt;p&gt;When we use Redis as a store for Slide Limiter, calls for&lt;code&gt;hit&lt;/code&gt; function will call &lt;code&gt;RedisStore -&amp;gt; hit&lt;/code&gt; function, which actually calls the following LUA script that will be executed by Redis server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;current_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'TIME'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KEYS&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="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KEYS&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="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;  
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARGV&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="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;  
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARGV&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="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;trim_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_time&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="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;  
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ZREMRANGEBYSCORE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&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;trim_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;request_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ZCARD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request_count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;  
  &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ZADD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_time&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="n"&gt;current_time&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="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;current_time&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="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'EXPIRE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;request_count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="k"&gt;end&lt;/span&gt;  
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Lua script is designed to be executed atomically in Redis, ensuring that rate-limiting operations are consistent and thread-safe when multiple requests are made concurrently. It checks if the request count is within the defined limit and records each request’s timestamp, allowing for accurate rate-limiting enforcement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It performs the following steps&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the Current Time: Use &lt;code&gt;redis.call('TIME')&lt;/code&gt; to retrieve the current time in Redis, which is an array with two elements: the seconds and microseconds.&lt;/li&gt;
&lt;li&gt;Construct the Key: Combine the &lt;code&gt;bucket&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; to create a unique key in Redis. This key represents the rate-limiting window for a specific resource (Both are provided when invoking the script).&lt;/li&gt;
&lt;li&gt;Calculate the Time Window: Convert the provided &lt;code&gt;windowMs&lt;/code&gt; (in milliseconds) to seconds by dividing it by 1000.&lt;/li&gt;
&lt;li&gt;Check the Requests Count: Check the number of requests in the specified time window by using &lt;code&gt;redis.call('ZCARD', key)&lt;/code&gt;. This count represents the requests made within the defined time window.&lt;/li&gt;
&lt;li&gt;Rate Limit Check: If the request count is less than the &lt;code&gt;limit&lt;/code&gt;, it means there's still capacity for more requests.&lt;/li&gt;
&lt;li&gt;Add Request Timestamp: Add the current time to the Redis sorted set with the timestamp as the score. This effectively records the time of the request.&lt;/li&gt;
&lt;li&gt;Set Expiry Time: Set an expiry time on the Redis key using &lt;code&gt;redis.call('EXPIRE', key, window)&lt;/code&gt;. This ensures that rate-limiting data is automatically cleared from Redis after the defined time window.&lt;/li&gt;
&lt;li&gt;Calculate Remaining Requests: Calculate the remaining requests by subtracting the request count from the &lt;code&gt;limit&lt;/code&gt; and then subtracting 1. This accounts for the current request.&lt;/li&gt;
&lt;li&gt;Return Remaining Requests: Return the number of remaining requests. If it’s greater than 0, the request is allowed; otherwise, it’s rate-limited.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Using Redis for rate limiting offers several benefits
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Distributed and Scalable&lt;/strong&gt;: Redis is a distributed, in-memory data store that is well-suited for scaling applications. By using a &lt;code&gt;RedisStore&lt;/code&gt;, we can implement rate limiting in a distributed system, making it easier to handle high traffic and load balancing across multiple instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-Performance&lt;/strong&gt;: Redis is known for its exceptional read and write performance, making it an excellent choice for rate limiting. It can quickly store and retrieve rate-limiting data, reducing the latency of rate-limiting checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiration and Automatic Cleanup&lt;/strong&gt;: Redis allows you to set expiration times for keys, making it easy to implement automatic data cleanup. This feature is crucial for rate limiting because it ensures that old rate limit data is removed, maintaining the accuracy of the rate limiter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Operations&lt;/strong&gt;: Redis supports atomic operations, which means rate limit checks and updates can be performed atomically in a single step. This ensures that rate-limiting operations are consistent, even in a multi-threaded or distributed environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Slide Limiter on GitHub
&lt;/h4&gt;

&lt;p&gt;If you’re interested in more details, please visit the project page on GitHub&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/m-elbably/slide-limiter"&gt;https://github.com/m-elbably/slide-limiter&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Slide Limiter is a powerful tool for implementing rate limiting in your distributed applications. By combining the efficiency of the sliding window rate-limiting algorithm with the scalability and consistency of Redis, Slide Limiter offers a robust solution for managing the flow of requests and actions within your system. Whether you need to protect endpoints, manage client-specific rate limits, or dynamically adjust rate-limiting parameters, I think Slide Limiter provides the flexibility and control you will need.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>node</category>
      <category>redis</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Rate Limiting: The Sliding Window Algorithm</title>
      <dc:creator>Mohamed El-Bably</dc:creator>
      <pubDate>Tue, 07 Nov 2023 12:01:00 +0000</pubDate>
      <link>https://forem.com/melbably/rate-limiting-the-sliding-window-algorithm-280a</link>
      <guid>https://forem.com/melbably/rate-limiting-the-sliding-window-algorithm-280a</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Rate limiting is a fundamental method for managing the flow of traffic to a service or server by imposing restrictions on the number of requests that can be made within a specific time window. This essential technique is widely employed in various network and web applications to ensure the orderly and secure operation of systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Rate Limiting?
&lt;/h3&gt;

&lt;p&gt;In simple terms, rate limiting is a way to govern the speed at which clients can execute requests or operations on a service or server within a specific timeframe. The core objective is to strike a balance between serving genuine users efficiently while protecting the system from abuse, overuse, or malicious activities.&lt;br&gt;
Imagine a scenario where one malicious zombie machine or user can launch an astonishing 20 or more HTTP GET requests per second. Compare this to the legitimate rate, which is often much less.&lt;/p&gt;

&lt;p&gt;Research has shown that such flooding rates can wreak havoc on web services, leading to downtime, poor performance, and potential security vulnerabilities. The result? A service that's unreliable and vulnerable to abuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Does Rate Limiting Work?
&lt;/h3&gt;

&lt;p&gt;Rate limiting operates as an integral part of an application, residing within its code rather than on the web server itself. It predominantly relies on tracking the IP addresses from which requests originate and closely monitoring the time intervals between each request. The IP address plays a pivotal role in identifying the source of a request within an application.&lt;/p&gt;

&lt;p&gt;A rate-limiting solution essentially performs a dual measurement: it evaluates the time lapses between requests from individual IP addresses and counts the total number of requests within a predefined time window. If the analysis indicates an excessive number of requests emanating from a single IP address within this designated timeframe, the rate-limiting mechanism intervenes by imposing a temporary halt on fulfilling further requests from that IP address.&lt;/p&gt;

&lt;p&gt;In simpler terms, a rate-limited application effectively communicates a friendly “Slow down, please” to users who are submitting requests at an accelerated pace. This is akin to a traffic police officer pulling over a speeding driver, or a parent kindly advising their child not to consume too much candy in a short span of time. Rate limiting, therefore, serves as a guardian that promotes fair usage, prevents system overload, and ensures a harmonious coexistence between applications and users.&lt;/p&gt;

&lt;h4&gt;
  
  
  Examples
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;API Requests Rate Limiting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A public API service allows free users to make up to 100 API requests per hour. When a user’s request rate exceeds this limit, the service throttles their requests, causing a delay in processing.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F396v6u8wojfsqi09p5be.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F396v6u8wojfsqi09p5be.png" alt="API requests rate limiting"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Password Reset Attempts Rate Limiting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A website’s password reset feature limits users to a maximum of 3 password reset attempts in a 24-hour period. If a user exceeds this limit, they are temporarily locked out of the password reset functionality to prevent misuse.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpu9ao4sldc1f7yruvwgz.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpu9ao4sldc1f7yruvwgz.png" alt="Password reset rate limiting"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These examples showcase different scenarios where rate limiting is applied to control access to resources or services and prevent abuse or overuse. Rate limiting helps maintain fair usage and ensures the stability and availability of systems and services.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Depending on the context, different identifiers can be employed to track and regulate requests. For public requests, the IP address can serve as an identifier, helping to manage traffic and prevent abuse. In other scenarios, a user's unique identifier may be more appropriate, ensuring that rate limiting is applied at an individual level. The choice of identifier depends on the specific needs and objectives of the rate-limiting implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Benefits of Rate Limiting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preventing Overload&lt;/strong&gt;: It helps prevent server overload by controlling the number of requests or operations that can be processed in a given time frame.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protection from Abuse&lt;/strong&gt;: Rate limiting safeguards against abusive or malicious behavior, such as Distributed Denial of Service (DDoS) attacks or brute force attacks.
Improved User Experience: By ensuring fair resource allocation, rate limiting helps maintain a good user experience by preventing a single user or client from monopolizing resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable Performance&lt;/strong&gt;: It provides predictability in system performance by avoiding sudden spikes in traffic that can disrupt operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Efficiency&lt;/strong&gt;: Rate limiting optimizes resource usage and helps in efficient resource allocation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Which attacks are Prevented by rate limiting?
&lt;/h2&gt;

&lt;p&gt;Rate limiting is a powerful defense mechanism against a range of attacks that aim to overwhelm a system through an excessive influx of requests. By regulating the pace of incoming requests, it acts as a shield against resource exhaustion, reduces the vulnerability to downtime, and guards against various attack vectors. Notably, three types of attacks find their adversary in rate limiting:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DDoS Attacks&lt;/strong&gt; — Distributed Denial of Service (DDoS) attacks are notorious for their ability to flood a system or service with a massive wave of requests, rendering it unresponsive or inaccessible. Rate limiting acts as a formidable barrier against DDoS attacks by either blocking or delaying requests from a single IP address or client that surpasses a predefined threshold. This strategic move makes it significantly more challenging for attackers to incapacitate the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brute Force Attacks&lt;/strong&gt; — Brute force attacks involve relentless attempts to guess login credentials or other sensitive information, leveraging automated tools to flood the system with a barrage of requests. Rate limiting comes to the rescue by limiting the number of login attempts that can be made within a specific time frame, thereby raising the difficulty level for attackers attempting to crack valid credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Abuse&lt;/strong&gt; — API abuse entails the excessive sending of requests to an API with the aim of extracting vast amounts of data or causing resource depletion. Rate limiting serves as a robust deterrent against API abuse by restricting the number of requests that a single client or application can make. This ensures the equitable distribution of API resources among all clients, effectively preventing resource exhaustion and ensuring that API services remain available and responsive.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Web Scraping&lt;/strong&gt; — While web scraping is often perceived as a benign activity, it can become a subtle form of attack when performed extensively. Rate limiting, although a valuable measure, may not entirely prevent web scraping. However, it does act as a deterrent by setting restrictions on the speed at which data can be harvested from a website or application. This serves a dual purpose by safeguarding the resources of the target site and ensuring the continuity of data integrity and accessibility.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Applications of Rate Limiting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;APIs&lt;/strong&gt;: Protecting APIs from overuse or abuse is one of the most common use cases. This ensures that API endpoints remain available to all users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Servers&lt;/strong&gt;: Rate limiting can be applied to web servers to control incoming HTTP requests and prevent server overload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microservices&lt;/strong&gt;: In microservices architectures, rate limiting can be used to manage inter-service communication and prevent cascading failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IoT Devices&lt;/strong&gt;: Limiting the rate of data ingestion from IoT devices to cloud services helps maintain data quality and system stability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Rate Limiting Algorithms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token Bucket&lt;/strong&gt;: Tokens are added to a bucket at a fixed rate, and requests are allowed if tokens are available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaky Bucket&lt;/strong&gt;: Requests are placed in a bucket and leak out at a fixed rate. If the bucket overflows, requests are rejected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed Window&lt;/strong&gt;: In this algorithm, the rate limit is applied within fixed time windows, and requests exceeding the limit are rejected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sliding Window&lt;/strong&gt;: The sliding window rate limiting algorithm is based on a dynamic time window that moves with time, allowing for more flexibility in managing bursts of traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Store&lt;/strong&gt;: The data store serves as the repository where information related to the rate limit and request counts is stored and retrieved. Typically, a key-value store like Redis is used for this purpose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sliding Window Rate Limiting Algorithm
&lt;/h2&gt;

&lt;p&gt;The Sliding Window Algorithm is a time-based method used to track and control the rate at which requests or operations can be made within a specific time window. It’s a dynamic system that adapts to changing traffic patterns, making it an effective tool for rate limiting in various applications.&lt;/p&gt;

&lt;p&gt;The central concept behind the Sliding Window Algorithm is the utilization of a dynamic time window that moves with the flow of time. Unlike static methods that reset rate limits at fixed intervals, the sliding window continuously adjusts to the current time, allowing for a more flexible and adaptable approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the Sliding Window Algorithm
&lt;/h3&gt;

&lt;p&gt;The Sliding Window Algorithm relies on a combination of two essential components: &lt;strong&gt;a fixed-size time window&lt;/strong&gt; and &lt;strong&gt;a counter&lt;/strong&gt; that tracks the number of requests or operations made within that window.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fixed-Size Time Window&lt;/strong&gt;: The first component is a time window, which can be set to any desired duration, such as one second, one minute, or one hour. This window determines the timeframe within which the rate limit is enforced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Counter&lt;/strong&gt;: The second component is a counter that keeps track of the requests or operations made within the time window. As requests come in, the counter increments.&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8ldh9dtc1zdpr13kxsl.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8ldh9dtc1zdpr13kxsl.png" alt="Sliding Window Algorithm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sliding Window rate limiting for 6 requests / second&lt;/p&gt;

&lt;p&gt;The innovative aspect of the Sliding Window Algorithm is that it continuously adjusts the time window as time progresses. This dynamic approach allows the algorithm to maintain a rolling window of time that moves with the current moment. For example, if we have a one-minute time window, the algorithm tracks requests made in the last 60 seconds, rather than resetting every minute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate Limit Enforcement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To enforce the rate limit, the Sliding Window Algorithm compares the number of requests made within the sliding time window to the predefined rate limit. If the count exceeds the limit, the algorithm can take one of several actions, such as delaying, rejecting, or throttling the excess requests. This ensures that the system remains within the defined rate limits while still allowing for bursts of activity when traffic naturally fluctuates.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Sliding Window Rate Limiting in Action&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In the presented sequence diagram, we can observe the process of Sliding Window Rate Limiting in action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Participants:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client:&lt;/strong&gt; Represents the entity or user making requests to a service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiter:&lt;/strong&gt; The component responsible for rate limiting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service:&lt;/strong&gt; The actual service to which the requests are being made.&lt;/li&gt;
&lt;/ul&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fop0z4enq8igqlfv5k8of.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fop0z4enq8igqlfv5k8of.png" alt="Sliding Window Rate Limiting in Action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram depicts a sequence of events for handling incoming requests:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The client&lt;/strong&gt; initiates requests to access a service.&lt;/li&gt;
&lt;li&gt;These requests are intercepted by the &lt;strong&gt;Rate Limiter&lt;/strong&gt; to ensure they comply with the defined rate limit.&lt;/li&gt;
&lt;li&gt;Within each request, the following actions occur:
— The &lt;strong&gt;Rate Limiter&lt;/strong&gt; checks if the sliding window needs to be adjusted. This is a crucial aspect of sliding window rate limiting. The window slides over time to maintain the rate limit.
— Expired requests that fall outside the sliding window are removed from consideration.
— The timestamp of the current request is added to the sliding window, indicating its occurrence.&lt;/li&gt;
&lt;li&gt;Each request has two scenarios according to the window limit:
— &lt;strong&gt;Within Limit:&lt;/strong&gt; If the current request rate is within the defined limit, the &lt;strong&gt;Rate Limiter&lt;/strong&gt; forwards the request to the &lt;strong&gt;Service&lt;/strong&gt;. The &lt;strong&gt;Service&lt;/strong&gt; processes the request and sends a response back to the &lt;strong&gt;Rate Limiter&lt;/strong&gt;, which, in turn, forwards the response to the &lt;strong&gt;Client&lt;/strong&gt;.
— &lt;strong&gt;Exceeds Limit:&lt;/strong&gt; If the current request rate exceeds the defined limit, the &lt;strong&gt;Rate Limiter&lt;/strong&gt; responds to the &lt;strong&gt;Client&lt;/strong&gt; with a request rejection, indicating that the limit has been exceeded.&lt;/li&gt;
&lt;li&gt;The process continues in a loop for incoming requests.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Benefits of the Sliding Window Algorithm
&lt;/h3&gt;

&lt;p&gt;The Sliding Window Algorithm offers several advantages, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: It adapts to varying traffic patterns and prevents abrupt cutoffs when rate limits are exceeded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fairness&lt;/strong&gt;: The algorithm ensures that resources are allocated fairly, preventing any single client or user from monopolizing them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protection&lt;/strong&gt;: It safeguards against abuse, ensuring that the system remains secure and available even in the face of excessive traffic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Comprehensive Rate Limiting
&lt;/h2&gt;

&lt;p&gt;Rate limiting every endpoint in an API, including internal health checks, is a thoughtful approach with several compelling reasons. Firstly, it ensures a consistent and fair allocation of resources across all aspects of your system. Whether it’s a critical external API endpoint serving thousands of users or an internal health check that monitors system well-being, rate limiting helps maintain an equitable balance.&lt;/p&gt;

&lt;p&gt;Furthermore, by rate-limiting internal health checks, you prevent the accidental or deliberate overuse of these checks. In a busy or complex system, frequent health checks can inadvertently strain resources, leading to performance issues. Rate limiting mitigates this risk, ensuring that essential monitoring activities remain effective without becoming a source of contention.&lt;/p&gt;

&lt;p&gt;Additionally, rate limiting provides a layer of defense against potential security threats. Even internal endpoints can be vulnerable to abuse or misuse. By implementing rate limits, you can protect your system from unintentional or intentional disruptions and ensure that resources are allocated where they are genuinely needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond the Limit: Handling Excess Requests
&lt;/h2&gt;

&lt;p&gt;When the rate limit is exceeded, two common strategies come into play: dropping or throttling requests. The choice between these strategies depends on the specific requirements and objectives of the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dropping Requests
&lt;/h3&gt;

&lt;p&gt;When the rate limit is exceeded, the “drop” strategy involves simply rejecting or discarding the excess requests. In this scenario, the server does not process the requests beyond the rate limit. This can be an effective way to ensure that the server remains within its operational limits and maintains optimal performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Cases for Dropping Requests:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Dropping requests is an excellent choice when dealing with potentially malicious traffic, such as Distributed Denial of Service (DDoS) attacks. By immediately discarding excess requests, you can protect the system from being overwhelmed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable Performance&lt;/strong&gt;: In situations where predictable performance is paramount, like critical real-time applications, dropping requests can help maintain a consistent quality of service for legitimate users by preventing the server from becoming overburdened.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Throttling Requests
&lt;/h3&gt;

&lt;p&gt;Throttling, on the other hand, is a more gentle approach to managing excess requests. Instead of rejecting them outright, the server processes them at a reduced rate, slowing down the response time. Throttling aims to ensure that the system remains responsive even when the rate limit is exceeded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Cases for Throttling Requests:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User Experience&lt;/strong&gt;: Throttling can be ideal when maintaining a positive user experience is essential. It allows legitimate users to continue accessing the service, albeit at a slower rate, rather than abruptly blocking their requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful Degradation&lt;/strong&gt;: Throttling is beneficial in scenarios where graceful degradation of service is preferable. For instance, during periods of high traffic, it allows the system to remain operational, though at a reduced capacity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the choice between dropping and throttling requests, when the rate limit is exceeded, depends on the specific use case and the desired outcome. Dropping requests provides swift and strict enforcement of rate limits, making it suitable for security-critical and performance-sensitive scenarios. On the other hand, throttling requests offer a more lenient approach that prioritizes maintaining user access and system availability, making it a good fit for situations where user experience and graceful service degradation are vital.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Rate_limiting" rel="noopener noreferrer"&gt;Rate limiting&lt;/a&gt; (Wikipedia)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cloudflare.com/learning/bots/what-is-rate-limiting/" rel="noopener noreferrer"&gt;What is rate limiting&lt;/a&gt; (Cloudflare)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.radware.com/cyberpedia/bot-management/rate-limiting/" rel="noopener noreferrer"&gt;What is rate limiting&lt;/a&gt; (Radware)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.solo.io/topics/rate-limiting/" rel="noopener noreferrer"&gt;Ultimate guide to rate limiting&lt;/a&gt; (solo.io)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://konghq.com/blog/engineering/how-to-design-a-scalable-rate-limiting-algorithm" rel="noopener noreferrer"&gt;How to Design a Scalable Rate Limiting Algorithm&lt;/a&gt; (Kong)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blogs.halodoc.io/taming-the-traffic-redis-and-lua-powered-sliding-window-rate-limiter-in-action/" rel="noopener noreferrer"&gt;Redis and Lua Powered Sliding Window Rate Limiter&lt;/a&gt; (halodoc.io)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@ludo_photos?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Ludovic Charlet&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/speed-limit-55-signage-on-road-CGWK6k2RduY?utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>backend</category>
    </item>
    <item>
      <title>Distributed High-Speed Spell Checking &amp; Correction In Node.js</title>
      <dc:creator>Mohamed El-Bably</dc:creator>
      <pubDate>Wed, 03 May 2023 19:13:35 +0000</pubDate>
      <link>https://forem.com/melbably/distributed-high-speed-spell-checking-correction-in-nodejs-3g7j</link>
      <guid>https://forem.com/melbably/distributed-high-speed-spell-checking-correction-in-nodejs-3g7j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Spell-checking is a widely used feature in software applications such as search engines, email clients, word processors and chatbots.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/m-elbably/symspell-ex" rel="noopener noreferrer"&gt;SymSpellEx&lt;/a&gt; (Extended SymSpell)
&lt;/h2&gt;

&lt;p&gt;Node.js spelling correction &amp;amp; Fuzzy search library based on Symmetric Delete Spelling Correction algorithm (SymSpell)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SymSpellEx
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Simple spell checking algorithms, such as the Norvig Algorithm, are not efficient and can be slow.&lt;/li&gt;
&lt;li&gt;I needed a spell checker that would work on a distributed system with multiple nodes, to perform spelling correction without duplicating data on each node.&lt;/li&gt;
&lt;li&gt;I needed to ensure that any changes or updates to the training data would be reflected across all connected nodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Edit Distance
&lt;/h2&gt;

&lt;p&gt;Edit distance is a measure of how different two strings are in terms of the minimum number of operations required to transform one string into the other.&lt;/p&gt;

&lt;p&gt;Edit distance algorithms compute the minimum number of edit operations required to transform one string into another, where edit operations include insertion, deletion, and substitution of characters. Some popular edit distance algorithms include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Levenshtein distance: This algorithm computes the minimum number of insertions, deletions, and substitutions required to transform one string into another.&lt;/li&gt;
&lt;li&gt;Damerau-Levenshtein distance: This is similar to Levenshtein distance, but also allows for transpositions (i.e., swapping adjacent characters).&lt;/li&gt;
&lt;li&gt;Jaro distance: This algorithm computes the similarity between two strings based on the number of matching characters and the number of transpositions needed to make the strings identical.&lt;/li&gt;
&lt;li&gt;Jaro-Winkler distance: This is an extension of the Jaro distance that assigns higher weights to prefix matches.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;The Levenshtein distance between "kitten" and "sitting" is 3. A minimal edit script that transforms the former into the latter is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kitten → sitten (substitute "s" for "k")
sitten → sittin (substitute "i" for "e")
sittin → sitting (insert "g" at the end)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The total number of operations needed to transform "kitten" into "sitting" is three, which is the edit distance between the two strings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naive approach
&lt;/h2&gt;

&lt;p&gt;It involves checking each word in the dictionary to find the word with the smallest edit distance to the misspelled word.&lt;/p&gt;

&lt;p&gt;For example, if the misspelled word is "teh", the algorithm will check every word in the dictionary to find the word with the smallest edit distance. The algorithm may find "the" as a possible correction, since it only requires one edit (a substitution of "h" with "e")&lt;/p&gt;

&lt;p&gt;This approach is computationally very expensive, which makes it not practical to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Peter Norvig Algorithm
&lt;/h2&gt;

&lt;p&gt;Peter Norvig Algorithm is a spell-checking algorithm that generates all possible terms with a given edit distance from the query term and searches them in a dictionary to find the correct spelling. The edit distance is calculated based on the number of operations needed to transform the query term to the correct spelling, where an operation can be a deletion, transposition, replacement, or insertion of a single character.&lt;/p&gt;

&lt;p&gt;The algorithm follows these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Generate all possible candidate words within an edit distance of &lt;code&gt;n&lt;/code&gt; from the input word, by applying deletions, transpositions, replacements, and insertions to the input word.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filter the candidate words to keep only the ones that appear in a pre-existing corpus of text, in this case, a large English text corpus.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rank the remaining candidate words based on their frequency of occurrence in the corpus.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Return the most likely candidate word as the corrected spelling.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
Suppose the input word is "the", and we have a large English text corpus to draw from. We first generate all possible candidate words within an edit distance of 2 from "the". This gives us a set of words such as "thee", "them", "then", "there", "these", "theta", and so on.&lt;/p&gt;

&lt;p&gt;Next, we filter this set to only include the candidate words that appear in the English text corpus. Suppose this narrows down our set to just "the" and "they".&lt;/p&gt;

&lt;p&gt;Finally, we rank the remaining candidate words by their frequency of occurrence in the corpus. Since "the" is a very common word in English, it is much more likely to be the intended spelling than "they". Therefore, "the" is returned as the corrected spelling for the input word "the".&lt;/p&gt;

&lt;p&gt;However, this method can be expensive and language-dependent. For a word of length n, an alphabet size a, and an edit distance of 1, there will be &lt;code&gt;n&lt;/code&gt; deletions, &lt;code&gt;n-1&lt;/code&gt; transpositions, &lt;code&gt;a * n&lt;/code&gt; alterations, and &lt;code&gt;a * (n+1)&lt;/code&gt; insertions.&lt;/p&gt;

&lt;p&gt;Candidate words number can be calculated using:&lt;br&gt;
&lt;code&gt;2n+2an+a-1&lt;/code&gt; or &lt;code&gt;54n + 25&lt;/code&gt; (Assuming we are using english alphabet having &lt;code&gt;a = 26&lt;/code&gt;), will lead to &lt;code&gt;90902&lt;/code&gt; candidate words search when &lt;code&gt;n = 8&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In some languages, such as Chinese, the alphabet can be enormous, resulting in even more possible words to search through.&lt;/p&gt;
&lt;h2&gt;
  
  
  Symmetric Delete Spelling Correction algorithm
&lt;/h2&gt;

&lt;p&gt;The Symmetric Delete spelling correction algorithm simplifies the process of generating possible spellings and searching in a dictionary. It does so by only using deletion as an operation, instead of deletion combined with transpose, replace, and insert operations. As a result, it is much faster, being six orders of magnitude faster than traditional methods for edit distance of 3. Moreover, it is language-independent.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why the algorithm is fast?
&lt;/h3&gt;

&lt;p&gt;Pre-calculation in training phase, while all possible spelling error variants as generated (deletes only) and stored in hash table, which makes it also fast when searching with an average search time complexity of O(1).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This makes the algorithm very fast, but it also required a large memory footprint, and the training phase takes a considerable amount of time to build the dictionary first time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more details check &lt;a href="https://seekstorm.com/blog/1000x-spelling-correction/" rel="noopener noreferrer"&gt;SymSpell&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  SymSpellEx
&lt;/h2&gt;

&lt;p&gt;SymSpellEx is building on SymSpell to provide extensibility by implementing different edit distance algorithms or implementing different data store. &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhrb7tvkmzymgqpxozi88.png" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhrb7tvkmzymgqpxozi88.png" alt="SymSpellEx Design" width="800" height="638"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Very fast&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Word suggestions&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Word correction&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Multiple languages supported&lt;/em&gt;&lt;/strong&gt; - &lt;em&gt;The algorithm, and the implementation are language independent&lt;/em&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Extendable&lt;/em&gt;&lt;/strong&gt; - &lt;em&gt;Edit distance and data stores can be implemented to extend library functionalities&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Main Components
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. Tokenizer
&lt;/h4&gt;

&lt;p&gt;This interface can be implemented to provide a different tokenizer for the library, a simple core tokenizer is provided.&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;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Tokenizer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="o"&gt;&amp;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;h4&gt;
  
  
  2. Edit Distance
&lt;/h4&gt;

&lt;p&gt;This interface can be implemented to provide different algorithms to use to calculate edit distance between two words.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;EditDistance&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&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="nf"&gt;calculateDistance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&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="nx"&gt;target&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="kr"&gt;number&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;Built-in Edit Distance Algorithms&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Damerau–Levenshtein distance&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Data Store
&lt;/h4&gt;

&lt;p&gt;This interface can be implemented to provide additional method to store data other than built-in stores (Memory, Redis)&lt;/p&gt;

&lt;p&gt;Data store should handle storage for these 2 data types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terms: List data structure to store terms and retrieve it by index&lt;/li&gt;
&lt;li&gt;Entries: Hash Table data structure to store dictionary entries and retrieve data by term (Key)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Data store should also handle storage for multiple languages and switch between them, check implemented data stores.&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;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;DataStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&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="nf"&gt;initialize&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="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;isInitialized&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setLanguage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;language&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;pushTerm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="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="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;getTermAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&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;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;getTermsAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indexes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&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="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="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;getEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Array&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;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;getEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Array&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;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&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="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;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;hasEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;maxEntryLength&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="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;clear&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="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;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;Built-in data stores&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory: Stores data in memory, using array structure for terms and high speed hash table (megahash) to manage dictionary entries&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;May be limited by node process memory limits, which can be overridden&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Redis: Stores data into Redis database using list structure to store terms and hash to store dictionary data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Very efficient way to train and store data, it will allow accessing by multiple processes and/or nodes/machines, training data one time on centralized distributed store, with enough memory data can be updated in production using different namespace on redis without interruptions, also dumping and migrating data will be easy.&lt;/p&gt;

&lt;p&gt;Redis data store uses LUA scripting to efficiently set entries using multiple Redis commands on server side, by defining custom command &lt;code&gt;hSetEntry&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="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;initialize&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="k"&gt;void&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;await&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;_redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hSetEntry&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;numberOfKeys&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="na"&gt;lua&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="s2"&gt;`
                local olen = redis.call("hget", "&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;_configNamespace&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;", ARGV[2])
                local value = redis.call("hset", KEYS[1], KEYS[2], ARGV[1])
                local nlen = #KEYS[2]
                if(not olen or nlen &amp;gt; tonumber(olen)) then
                  redis.call("hset", "&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;_configNamespace&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;", ARGV[2], nlen)
                end
                return value
                `&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;_initialized&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Training
&lt;/h3&gt;

&lt;p&gt;For single term training you can use &lt;code&gt;add&lt;/code&gt; function:&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;SymSpellEx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MemoryStore&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;symspell-ex&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;LANGUAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&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 SymSpellEx instnce and inject store new store instance&lt;/span&gt;
&lt;span class="nx"&gt;symSpellEx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SymSpellEx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MemoryStore&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;symSpellEx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Train data&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;symSpellEx&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;argument&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LANGUAGE&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;symSpellEx&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;computer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LANGUAGE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For multiple terms (Array) you can use &lt;code&gt;train&lt;/code&gt; function:&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;terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;argument&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;computer&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;symSpellEx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;terms&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="nx"&gt;LANGUAGE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Searching
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;search&lt;/code&gt; function can be used to get multiple suggestions if available up to the &lt;code&gt;maxSuggestions&lt;/code&gt; value&lt;/p&gt;

&lt;p&gt;Arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;input&lt;/code&gt; &lt;em&gt;String&lt;/em&gt; (Wrong/Invalid word we need to correct)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;language&lt;/code&gt; &lt;em&gt;String&lt;/em&gt; (Language to be used in search)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxDistance&lt;/code&gt; &lt;em&gt;Number&lt;/em&gt;, &lt;em&gt;optional&lt;/em&gt;, default = &lt;code&gt;2&lt;/code&gt; (Maximum distance for suggestions)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxSuggestions&lt;/code&gt; &lt;em&gt;Number&lt;/em&gt;, &lt;em&gt;optional&lt;/em&gt;, default = &lt;code&gt;5&lt;/code&gt; (Maximum suggestions number to return)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Return: &lt;code&gt;Array&amp;lt;Suggetion&amp;gt;&lt;/code&gt; Array of suggestions&lt;/p&gt;

&lt;p&gt;Example&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;symSpellEx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;argoments&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example &lt;code&gt;Suggestion Object&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"argoments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"suggestion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
  &lt;/span&gt;&lt;span class="nl"&gt;"distance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"frequency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;155&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Correction
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;correct&lt;/code&gt; function can be used to get the best suggestion for input word or sentence in terms of edit distance and frequency&lt;/p&gt;

&lt;p&gt;Arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;input&lt;/code&gt; &lt;em&gt;String&lt;/em&gt; (Wrong/Invalid word we need to correct)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;language&lt;/code&gt; &lt;em&gt;String&lt;/em&gt; (Language to be used in search)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxDistance&lt;/code&gt; &lt;em&gt;Number&lt;/em&gt;, &lt;em&gt;optional&lt;/em&gt;, default = &lt;code&gt;2&lt;/code&gt; (Maximum distance for suggestions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Return: &lt;code&gt;Correction&lt;/code&gt; object which contains original &lt;code&gt;input&lt;/code&gt; and corrected &lt;code&gt;output&lt;/code&gt; string, with array of suggestions  &lt;/p&gt;

&lt;p&gt;Example&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;symSpellEx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;correct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Special relatvity was orignally proposed by Albert Einstein&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns this &lt;code&gt;Correction&lt;/code&gt; object:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This output is totally depending on the quality of the training data that was push into the store&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"suggestions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Special relatvity was orignally proposed by Albert Einstein"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Special relativity was originally proposed by Albert Einstein"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Github Repository
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/m-elbably/symspell-ex" rel="noopener noreferrer"&gt;https://github.com/m-elbably/symspell-ex&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Spell-checking is a widely used feature in software applications. Simple spell-checking algorithms are not efficient and can be slow.&lt;/p&gt;

&lt;p&gt;There are many popular edit distance algorithms such as Levenshtein distance, Damerau-Levenshtein distance, Jaro distance, and Jaro-Winkler distance.&lt;/p&gt;

&lt;p&gt;Naive approach involves checking each word in the dictionary to find the word with the smallest edit distance to the misspelled word, which is computationally expensive.&lt;/p&gt;

&lt;p&gt;Peter Norvig Algorithm is a spell-checking algorithm that generates all possible terms with a given edit distance from the query term and searches them in a dictionary to find the correct spelling.&lt;/p&gt;

&lt;p&gt;Symmetric Delete spelling correction algorithm simplifies the process of generating possible spellings and searching in a dictionary by only using deletion as an operation, resulting in being much faster and language-independent.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://norvig.com/spell-correct.html" rel="noopener noreferrer"&gt;Peter Norvig's spelling corrector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wolfgarbe/SymSpell" rel="noopener noreferrer"&gt;SymSpell&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>nlp</category>
      <category>node</category>
      <category>ai</category>
    </item>
    <item>
      <title>GPT Graph: A Simple Tool for Knowledge Graph Exploration</title>
      <dc:creator>Mohamed El-Bably</dc:creator>
      <pubDate>Sun, 30 Apr 2023 09:28:18 +0000</pubDate>
      <link>https://forem.com/melbably/gpt-graph-a-simple-tool-for-knowledge-graph-exploration-257k</link>
      <guid>https://forem.com/melbably/gpt-graph-a-simple-tool-for-knowledge-graph-exploration-257k</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mol6JiW2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/91no8t4va7lfhv816ewo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mol6JiW2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/91no8t4va7lfhv816ewo.png" alt="GPT Graph" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a developer, exploring and organizing information is a crucial part of the job. That's why I wanted to share with you an open-source tool that I developed called "GPT Graph" It is a knowledge graph explorer that utilizes the powerful GPT 3.5 turbo model to help users explore information in an organized and intuitive manner.&lt;/p&gt;

&lt;p&gt;I believe graphs are an excellent way to leverage LLMs in a variety of use cases, including brainstorming, studying, and reasoning about topics and how they relate to each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Knowledge Graph
&lt;/h2&gt;

&lt;p&gt;A knowledge graph is a type of database that is used to store and represent knowledge in a machine-readable format. It uses a graph-based model, consisting of nodes (entities) and edges (relationships), to represent information and the connections between them. Knowledge graphs are often used to represent complex information in a structured and intuitive way, making it easier for machines to understand and analyze. They can be used in various domains, such as natural language processing, search engines, recommendation systems, and data analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why GPT Graph?
&lt;/h2&gt;

&lt;p&gt;It's a unique way to explore information in an organized and intuitive manner. With GPT Graph, you can easily navigate through different topics, discover new relationships between them, and generate creative ideas.&lt;/p&gt;

&lt;p&gt;It leverages the power of GPT-3 to generate relevant and high-quality content. Unlike traditional keyword-based searches, GPT Graph takes a more semantic approach to explore the topics and generate the graph. It helps to uncover hidden relationships between different topics and provides a comprehensive view of the entire knowledge domain.&lt;/p&gt;

&lt;p&gt;Moreover, GPT Graph provides a user-friendly interface that allows users to interact with the graph easily. Users can ask questions, generate prompts, and add their own ideas to the graph. It's a powerful tool that enables users to collaborate, brainstorm, and generate new insights in a very efficient way.&lt;/p&gt;

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

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/E4xKhRbNQig"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;The ability to describe a specific query and generate a graph of related topics.&lt;/li&gt;
&lt;li&gt;Auto-generated prompts for generated nodes to discover more about the topic.&lt;/li&gt;
&lt;li&gt;Custom prompts support, asking questions and getting answers, along with generated prompts to branch from your own ideas.&lt;/li&gt;
&lt;li&gt;Markdown formatted descriptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adding the following prompt as a starting point for the graph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Solve 5x^2 + 6x + 1 = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will solve the equation and provide these helpful prompts to expand your knowledge about Quadratic Equation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is a quadratic equation?&lt;/li&gt;
&lt;li&gt;How do you derive the quadratic formula?&lt;/li&gt;
&lt;li&gt;What are some methods to solve quadratic equations?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These auto-generated prompts may vary from time to time, based on used temperature parameter value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Details
&lt;/h2&gt;

&lt;p&gt;This project uses the following technologies and frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI GPT 3.5 turbo model&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt; 3.0 JavaScript Framework&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://antdv.com/"&gt;Ant Design Vue&lt;/a&gt; UI Framework&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://g6.antv.antgroup.com/en/"&gt;G6&lt;/a&gt; Graph Visualization Engine&lt;/li&gt;
&lt;/ul&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Content Parsing
&lt;/h3&gt;

&lt;p&gt;Most of the time GPT 3 returns a consistent JSON object, but unfortunately this is not always the case, so additional layer added to extract actual JSON or transform it to the valid format through &lt;a href="https://www.npmjs.com/package/jsonrepair"&gt;jsonrepair&lt;/a&gt; library&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Rendering
&lt;/h3&gt;

&lt;p&gt;G6 library is used for canvas rendering and graph arrangement, with custom nodes and edges.&lt;br&gt;
Two tree graph layouts used&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dendrogram for vertical layout&lt;/li&gt;
&lt;li&gt;mindmap for horizontal layout&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Model Parameters
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;temprature: 0.7&lt;/code&gt; is used to get different and more creative responses every time, you may play around with different values and check the output&lt;/p&gt;

&lt;h3&gt;
  
  
  API Key
&lt;/h3&gt;

&lt;p&gt;An OpenAI API key is required to run the tool, You can add your own key to .env file with the key &lt;code&gt;VITE_OPENAI_KEY&lt;/code&gt;, Check &lt;code&gt;.env.example&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Repository
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/m-elbably/gpt-graph"&gt;https://github.com/m-elbably/gpt-graph&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Enhancements
&lt;/h2&gt;

&lt;p&gt;While this is just a limited technical example, I think there are many ways to improve it. Here are some of the potential enhancements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use GPT message context with the path the user took to discover specific topics, allowing the user to control the divergence of the information and get creative ideas through auto-generated prompts or questions.&lt;/li&gt;
&lt;li&gt;Use GPT inference capabilities to label topics and automatically connect or group related nodes.&lt;/li&gt;
&lt;li&gt;Infer topics relations and add to edges&lt;/li&gt;
&lt;li&gt;Store the result in a NoSQL database labeled with the primary input and retrieve it later to draw the graph and search instead of querying GPT again.&lt;/li&gt;
&lt;li&gt;Allow users to add and delete custom nodes, enabling them to use the tool as a powerful mind-mapping tool with AI behind the scenes to provide creative ideas or discuss user-created ones.&lt;/li&gt;
&lt;li&gt;Use a normal graph structure instead of a tree graph (DAG), will allow developers and architects to design systems, by adding different components and the flow of data, then get GPT to generate mermaid diagram code for built graphs, which opens up many possibilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Potential Uses for GPT Graph
&lt;/h2&gt;

&lt;p&gt;GPT Graph can be used in various domains where knowledge exploration is required. Here are a few examples of how GPT Graph can be used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Research: GPT Graph can be used by researchers to explore new topics, discover new relationships between them, and generate new insights. It can help researchers to get a comprehensive view of the entire knowledge domain and generate creative ideas.&lt;/li&gt;
&lt;li&gt;Education: GPT Graph can be used by students to study different topics, discover new relationships between them, and generate new insights. It can help students to get a better understanding of the subject matter and generate creative ideas.&lt;/li&gt;
&lt;li&gt;Architecture and System Design: GPT Graph can be used by architects and designers to explore new solutions, discover new relationships between different components, and generate new ideas. It can help them to generate creative and innovative designs.&lt;/li&gt;
&lt;li&gt;Business: GPT Graph can be used by businesses to explore new opportunities, discover new relationships between different markets and products, and generate new ideas. It can help businesses to innovate and stay ahead of the competition.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, the idea of using graphs with GPT can lead to building powerful tools that can be used in various domains where knowledge exploration is required. It’s a unique way to explore information, generate new insights, and collaborate with others in an efficient way.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GPT Graph is a limited technical example that demonstrates the concept. However, it can also serve as an excellent example of how to build GPT 3 applications and use prompts as a developer to get results in a specific format and schema.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;I hope that this small project serves as an example for you and other developers on how to build GPT 3 applications, or better yet, inspires you to create innovative tools that can be of assistance to others.&lt;/p&gt;

&lt;p&gt;Feel free to check out the &lt;a href="https://github.com/m-elbably/gpt-graph"&gt;GitHub Repository&lt;/a&gt; and give it a try, Or play with the &lt;a href="https://m-elbably.github.io/gpt-graph/"&gt;online demo&lt;/a&gt; if you have an OpenAI API Key.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Original Article Published on &lt;a href="https://medium.com/@m-elbably/gpt-graph-a-simple-tool-for-knowledge-graph-exploration-70e0e3861716"&gt;Medium&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>gpt3</category>
      <category>chatgpt</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
