<?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: Torsten Dittmann</title>
    <description>The latest articles on Forem by Torsten Dittmann (@torstendittmann).</description>
    <link>https://forem.com/torstendittmann</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%2F206882%2F206ee2e4-c799-4859-93b6-0851db038d34.png</url>
      <title>Forem: Torsten Dittmann</title>
      <link>https://forem.com/torstendittmann</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/torstendittmann"/>
    <language>en</language>
    <item>
      <title>Preview npm packages from any PR</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Tue, 20 May 2025 14:42:28 +0000</pubDate>
      <link>https://forem.com/torstendittmann/preview-npm-packages-from-any-pr-4hf8</link>
      <guid>https://forem.com/torstendittmann/preview-npm-packages-from-any-pr-4hf8</guid>
      <description>&lt;p&gt;I recently launched &lt;a href="https://pkg.vc" rel="noopener noreferrer"&gt;pkg.vc&lt;/a&gt; to solve a problem that’s been a pain for years:&lt;/p&gt;

&lt;p&gt;How do you test npm package changes from a PR or feature branch, without local builds or polluting the public registry with throwaway versions?&lt;/p&gt;

&lt;h2&gt;
  
  
  What is pkg.vc?
&lt;/h2&gt;

&lt;p&gt;It’s a service that lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build and share installable npm packages from any branch or PR&lt;/li&gt;
&lt;li&gt;Get a unique install URL for each commit, branch, or PR&lt;/li&gt;
&lt;li&gt;Integrate with GitHub Actions or use it locally&lt;/li&gt;
&lt;li&gt;Private packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was inspired by &lt;a href="https://pkg.pr.new" rel="noopener noreferrer"&gt;pkg.pr.new&lt;/a&gt; (which is great!), but I needed something that worked with private packages and works outside of GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install the CLI:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; pkg.vc
&lt;span class="c"&gt;# or just use npx&lt;/span&gt;
npx pkg.vc &lt;span class="nt"&gt;--organization&lt;/span&gt; my-org ./my-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll need an API key (see the docs for setup).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Publish a Preview
&lt;/h3&gt;

&lt;p&gt;After publishing, you get a unique install URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;https://pkg.vc/-/@my-org/my-package@5541c6f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use this URL in any project to test the package - no need to publish to npm.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Automate with GitHub Actions
&lt;/h3&gt;

&lt;p&gt;You can set up a workflow so every PR automatically publishes a preview package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pkg-vc/publish-action@main&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;organization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-org&lt;/span&gt;
    &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-package&lt;/span&gt;
    &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PKG_VC_SECRET }}&lt;/span&gt;
    &lt;span class="na"&gt;github-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, every PR gets a comment with an install link.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;I’m still iterating on this, and I’d love feedback.&lt;br&gt;
If you’ve struggled with this workflow, or have ideas for improvement, let me know!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pkg.vc/docs" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/qYGegyRswN" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>javascript</category>
    </item>
    <item>
      <title>New Operators to Query Documents More Efficiently</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Wed, 12 Apr 2023 13:13:26 +0000</pubDate>
      <link>https://forem.com/appwrite/new-operators-to-query-documents-more-efficiently-5gab</link>
      <guid>https://forem.com/appwrite/new-operators-to-query-documents-more-efficiently-5gab</guid>
      <description>&lt;p&gt;Query operators are essential to search and retrieve data from Appwrite. To allow more advanced queries, we have added new operators to the mix with Appwrite 1.3.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 New to Appwrite?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://appwrite.io/" rel="noopener noreferrer"&gt;Appwrite&lt;/a&gt; is an open-source back-end-as-a-service that abstracts all the complexity of building a modern application by providing you with a set of REST, GraphQL, and Realtime APIs for your core back-end needs. Appwrite takes the heavy lifting for developers and handles user authentication and authorization, databases, file storage, cloud functions, webhooks, and much more!&lt;/p&gt;

&lt;h2&gt;
  
  
  Operators
&lt;/h2&gt;

&lt;p&gt;This section will introduce each of these operators and provide examples of how they affect results.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;isNull&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;isNull&lt;/code&gt; operator is used to query for documents with null or missing values. This operator is handy when you need to identify incomplete data or records that lack specific fields. For example, if you're managing customer data and want to find customers who haven't provided their email addresses, you can use the isNull operator to retrieve those records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&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;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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Torsten Dittmann"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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="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;
  
  
  &lt;code&gt;isNotNull&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;isNotNull&lt;/code&gt; operator, on the other hand, is used to query for documents with values. This operator is helpful when you want to exclude records with missing fields from your query results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNotNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&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;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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Christy Jacob"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"christy@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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="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;
  
  
  &lt;code&gt;between&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;between&lt;/code&gt; operator is used to query for documents within a range of values. This operator is helpful when retrieving records that fall between two specified values. It can be used with both string and numeric attributes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;between&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Torsten Dittmann"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&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="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;
  
  
  &lt;code&gt;startsWith&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;startsWith&lt;/code&gt; operator is used to query for documents that begin with a specified string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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;Chris&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;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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Christy Jacob"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&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="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;
  
  
  &lt;code&gt;endsWith&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Conversely, the &lt;code&gt;endsWith&lt;/code&gt; operator is used to query for documents that end with a specified string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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;mann&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;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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Torsten Dittmann"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&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="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;
  
  
  &lt;code&gt;select&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;select&lt;/code&gt; operator is used to select specific fields from a document. This operator is useful when you want to retrieve only certain fields from a document and exclude others.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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;email&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;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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Torsten Dittmann"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Christy Jacob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"christy@example.com"&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="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;Adding these database operators to Appwrite has improved the ability of developers to query data more efficiently. It allows for more complex queries with less code, which reduces development time and increases productivity.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://appwrite.io/docs/databases-queries" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt; for more information and &lt;a href="https://dev.to/appwrite/join-celebrations-appwrite-13-ships-relationships-57fc"&gt;the release announcement&lt;/a&gt; for details on other awesome new features in the latest version of Appwrite.&lt;/p&gt;

&lt;p&gt;You can also use the following resources to learn more and get help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 &lt;a href="https://github.com/appwrite/appwrite" rel="noopener noreferrer"&gt;Appwrite GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📜 &lt;a href="https://appwrite.io/docs" rel="noopener noreferrer"&gt;Appwrite Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;Discord Community&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>news</category>
    </item>
    <item>
      <title>Announcing Appwrite 0.15 with Phone Authentication &amp; More!</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Tue, 28 Jun 2022 14:36:54 +0000</pubDate>
      <link>https://forem.com/appwrite/announcing-appwrite-015-with-phone-authentication-more-5cjj</link>
      <guid>https://forem.com/appwrite/announcing-appwrite-015-with-phone-authentication-more-5cjj</guid>
      <description>&lt;p&gt;Today, we're thrilled to announce the official release of Appwrite 0.15, including one of our most anticipated features: &lt;strong&gt;Phone Authentication&lt;/strong&gt;! 🥁 &lt;/p&gt;

&lt;p&gt;Besides Phone Authentication, Appwrite 0.15 also comes with some exciting features, including improved Metadata on all Resources, Webhook Signature, and multiple bug fixes.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤔 New to Appwrite?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://appwrite.io/" rel="noopener noreferrer"&gt;Appwrite&lt;/a&gt; is an open-source back-end-as-a-service that abstracts all the complexity of building a modern application by providing you with a set of REST and Realtime APIs for your core back-end needs. Appwrite takes the heavy lifting for developers and handles user authentication and &lt;strong&gt;authorization&lt;/strong&gt;, &lt;strong&gt;databases&lt;/strong&gt;, &lt;strong&gt;file storage&lt;/strong&gt;, &lt;strong&gt;cloud functions&lt;/strong&gt;, &lt;strong&gt;webhooks&lt;/strong&gt;, and much more!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  📱 Phone Authentication is Here!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwkgp4oam1mjcbu6xfqh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwkgp4oam1mjcbu6xfqh.png" alt="Phone Authentication" width="800" height="336"&gt;&lt;/a&gt;&lt;br&gt;
With this release, we are introducing a feature, which has been one of our longest open issues on the Appwrite GitHub repository. Now you are able to authenticate Users, by sending them 6 digits to their phone and have them enter them in your application. Phone authentication will be the 6th authentication method that is now supported at Appwrite alongside &lt;strong&gt;email/password&lt;/strong&gt;, &lt;strong&gt;anonymous login&lt;/strong&gt;, &lt;strong&gt;magic links&lt;/strong&gt;, &lt;strong&gt;team invites&lt;/strong&gt; and &lt;strong&gt;JWT&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Initially there are 3 providers which can be used with Appwrite to send SMS verification codes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.textmagic.com/" rel="noopener noreferrer"&gt;TextMagic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.telesign.com/" rel="noopener noreferrer"&gt;Telesign&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following Environment Variables have been added for configuring Phone Authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;_APP_PHONE_PROVIDER&lt;/strong&gt;&lt;br&gt;
Provider used for delivering SMS for Phone authentication. Use the following format: &lt;code&gt;phone://[USER]:[SECRET]@[PROVIDER]&lt;/code&gt;. Available providers are &lt;strong&gt;twilio&lt;/strong&gt;, &lt;strong&gt;text-magic&lt;/strong&gt; and &lt;strong&gt;telesign&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;_APP_PHONE_FROM&lt;/strong&gt;&lt;br&gt;
Phone number used for sending out messages. Must start with a leading '+' and maximum of 15 digits without spaces (like +123456789).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have set up your environment variables with your favorite provider credentials, you can call the &lt;a href="https://appwrite.io/docs/client/account?sdk=web-default#accountCreatePhoneSession" rel="noopener noreferrer"&gt;&lt;code&gt;createPhoneSession&lt;/code&gt;&lt;/a&gt; method, passing the phone number of the User. This method will send a six digit code to your user as an SMS message.&lt;br&gt;
Once received by your user and submitted back to your application or website , you can verify your user’s code - which is valid for 15 minutes - using the  &lt;a href="https://appwrite.io/docs/client/account?sdk=web-default#accountUpdatePhoneSession" rel="noopener noreferrer"&gt;&lt;code&gt;updatePhoneSession&lt;/code&gt;&lt;/a&gt; method.&lt;/p&gt;

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

&lt;p&gt;We have also added the &lt;code&gt;phone&lt;/code&gt; and &lt;code&gt;phoneVerified&lt;/code&gt; attributes to the &lt;a href="https://appwrite.io/docs/models/user" rel="noopener noreferrer"&gt;User Object&lt;/a&gt;. These new attributes will help you know the user verification status and decide whether you want to force the user to verify his phone or not.&lt;/p&gt;

&lt;p&gt;In summary, following endpoints have been added to the &lt;strong&gt;Account Service:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;createPhoneSession&lt;/strong&gt;&lt;br&gt;
For initializing the phone authentication.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;updatePhoneSession&lt;/strong&gt;&lt;br&gt;
For completing the phone authentication.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;updatePhone&lt;/strong&gt;&lt;br&gt;
For adding a phone number to your user’s account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;createPhoneVerification&lt;/strong&gt;&lt;br&gt;
For initializing phone verification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;updatePhoneVerification&lt;/strong&gt;&lt;br&gt;
For completing phone verification.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally the &lt;strong&gt;Users Service&lt;/strong&gt; has these new endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;updatePhone&lt;/strong&gt;&lt;br&gt;
For updating the use phone number.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;updatePhoneVerification&lt;/strong&gt;&lt;br&gt;
For changing verification status.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📀 Multiple Databases For Each Project
&lt;/h2&gt;

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

&lt;p&gt;Until now, each Appwrite project could only use a single database. Starting with version 0.15, this is no longer the case. We're happy to introduce a &lt;strong&gt;new database layer&lt;/strong&gt; to allow you to have as many database instances as you want under a single Appwrite project. Currently, all your project databases still have to use the same database adapter under the hood as defined in your Appwrite's instance environment variables, but this is our first step towards adding support for multiple database adapters under the same Appwrite project. &lt;/p&gt;

&lt;p&gt;In the next few months, we will continue to expand our database adapters support, and we can already share that besides existing support for &lt;strong&gt;MariaDB and MySQL&lt;/strong&gt;, we are working on adding support for both &lt;strong&gt;MongoDB and Postgres&lt;/strong&gt;. With the new database layer in place, we will allow you to have multiple adapters under the same project. This will allow you to micro-optimize your project's performance, choose the best technology for your specific use case, and give your team the flexibility and comfort to work with the tools and technologies you already love and trust. All of that is on top of the same Appwrite experience you are already familiar with.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  🎉 New &lt;code&gt;$createdAt&lt;/code&gt; &amp;amp; &lt;code&gt;$updatedAt&lt;/code&gt; Attributes
&lt;/h2&gt;

&lt;p&gt;They are finally here, &lt;code&gt;$createdAt&lt;/code&gt; and &lt;code&gt;$updatedAt&lt;/code&gt; are now part of every Resource in Appwrite and contain a timestamp of the date of creation and the latest update. This comes in very handy, if the ordering of the resources is important or if you want to implement offline sync with your application. The new attributes are &lt;code&gt;$createdAt&lt;/code&gt; and &lt;code&gt;$updatedAt&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔐 Webhook Signatures
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vsu1j8yc2hwenopgz7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vsu1j8yc2hwenopgz7m.png" alt="Appwrite Webhook Signatures" width="800" height="336"&gt;&lt;/a&gt;&lt;br&gt;
Security is vital for everyone here at Appwrite, and we strive to give you easy-to-use tools to keep you and your users safe against web threats. This commitment to security is why with version 0.15, we have introduced the ability to validate webhooks with Appwrite. Webhooks now include a &lt;code&gt;x-appwrite-webhook-signature&lt;/code&gt; which can be used in combination with a unique Signature Key found in the Webhook to validate the Webhook is actually coming from Appwrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔮 New OAuth Provider for Dailymotion and Autodesk, Gitlab Got an Upgrade
&lt;/h2&gt;

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

&lt;p&gt;We introduced new OAuth providers for &lt;strong&gt;Dailymotion&lt;/strong&gt; and &lt;strong&gt;Autodesk&lt;/strong&gt;. These new adapters are taking our count to a total of &lt;strong&gt;32(!) supported OAuth providers&lt;/strong&gt; in Appwrite. On top of the new providers, the GitLab adapter has also been upgraded to support a custom hostname for those of you who are using a self-hosted edition of GitLab.&lt;/p&gt;

&lt;h2&gt;
  
  
  🥳 And More!
&lt;/h2&gt;

&lt;p&gt;We still haven't covered everything! There's lots more bug fixes and other micro optimizations we made under the hood to make your experience with Appwrite better! &lt;/p&gt;

&lt;p&gt;To get all the details on Appwrite 0.15, check out all the changes in the &lt;a href="https://github.com/appwrite/appwrite/blob/master/CHANGES.md#version-0150" rel="noopener noreferrer"&gt;changelog on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⏭ What’s Next?
&lt;/h2&gt;

&lt;p&gt;Appwrite 0.16 is not far away and we promise it's going to be even more exciting 🤩 We'll continue improving upon Appwrite's solid performance, flexibility and adding amazing new features.&lt;/p&gt;

&lt;p&gt;Do you have ideas for an exciting new feature ? Open up &lt;a href="https://github.com/appwrite/appwrite/discussions" rel="noopener noreferrer"&gt;a Github Discussion&lt;/a&gt; so you can get feedback from the core team, maintainers and our ever growing community.&lt;/p&gt;

&lt;p&gt;You're welcomed to join us &lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;on Discord&lt;/a&gt; to get all the latest updates about new Appwrite versions and chat directly with the Appwrite team.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>news</category>
      <category>cloud</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Can Appwrite handle 25 Million documents?</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Fri, 07 Jan 2022 17:06:33 +0000</pubDate>
      <link>https://forem.com/appwrite/appwrite-012-database-improvements-3kmh</link>
      <guid>https://forem.com/appwrite/appwrite-012-database-improvements-3kmh</guid>
      <description>&lt;p&gt;With the release of &lt;a href="https://appwrite.io" rel="noopener noreferrer"&gt;Appwrite&lt;/a&gt; 0.12 come many new features, most notable being a completely overhauled database service.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/appwrite" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2225%2F81202665-3201-4ceb-b247-f8c5feae746f.png" alt="Appwrite" width="800" height="800"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F224437%2F24510e1e-2d7b-414f-9b5c-f6566845bf04.jpeg" alt="" width="229" height="229"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/appwrite/its-here-announcing-the-release-of-appwrite-012-5c8b" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;It's Here! Announcing the Release of Appwrite 0.12!&lt;/h2&gt;
      &lt;h3&gt;Eldad A. Fux for Appwrite ・ Jan 5 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#news&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;The database didn't scale well with many documents in previous Appwrite versions. This was noticeable in collections containing upwards of ten thousand documents.&lt;/p&gt;

&lt;p&gt;Since Appwrite uses the same database for internal resources like &lt;strong&gt;Users&lt;/strong&gt; and &lt;strong&gt;Files&lt;/strong&gt;, this limitation impacted every other service indirectly. The more the data, the slower the requests.&lt;/p&gt;

&lt;p&gt;With 0.12, the database API introduces &lt;a href="https://appwrite.io/docs/database#indexes" rel="noopener noreferrer"&gt;Indexes&lt;/a&gt;, which allow you to improve queries for specific documents by a lot. Complimentary, we also added a new and more powerful way of filtering with &lt;a href="https://appwrite.io/docs/database#querying-documents" rel="noopener noreferrer"&gt;different query operators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These improvements along with a complete rewrite of the database service enhanced Appwrite in a way that we were able to effortlessly query a collection of &lt;strong&gt;25 Million documents&lt;/strong&gt; with excellent response times. &lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 Benchmark
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://k6.io/" rel="noopener noreferrer"&gt;k6&lt;/a&gt; was used with 500 &lt;strong&gt;Virtual Users (VUs)&lt;/strong&gt; over 30 seconds, with each request performing an &lt;code&gt;EQUALS&lt;/code&gt; query against an attribute that is indexed with a &lt;strong&gt;Key index&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&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;k6/http&lt;/span&gt;&lt;span class="dl"&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;Rate&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;k6/metrics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;X-Appwrite-Key&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;[API_KEY]&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;X-Appwrite-Project&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;[PROJECT_ID]&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;Content-Type&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;application/json&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFailRate&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;Rate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed requests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&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;vus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;discardResponseBodies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;thresholds&lt;/span&gt;&lt;span class="p"&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;failed requests&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rate&amp;lt;0.05&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`http://[APPWRITE_URL]/v1/database/collections/[COLLECTION_ID]/documents?queries[]=number.equal(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;25000000&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="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;config&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;myFailRate&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;200&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;
  
  
  📝 Collection
&lt;/h2&gt;

&lt;p&gt;Here is a representation of what the collection looks like used for the benchmark, the &lt;code&gt;$id&lt;/code&gt; is automatically generated, and &lt;code&gt;number&lt;/code&gt; is a unique random value up to 25 million.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;$id&lt;/th&gt;
&lt;th&gt;number&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;61d7fe9f91d6fed26fc0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;61d7fe73eb0142ab50ec&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;61d7fe73eb5142c4b5e5&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;61d86657b6179ea91483&lt;/td&gt;
&lt;td&gt;25000000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  ⚙️ Request
&lt;/h2&gt;

&lt;p&gt;Requests were performed against the &lt;a href="https://appwrite.io/docs/client/database?sdk=web-default#databaseListDocuments" rel="noopener noreferrer"&gt;list documents endpoint&lt;/a&gt; with an &lt;code&gt;EQUALS&lt;/code&gt; query against the &lt;code&gt;number&lt;/code&gt; attribute and a random number from 1 - 25,000,000. On top of the query itself, each request goes through authentication, authorization, and permissions validation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;/v1/database/[COLLECTION]/documents?queries[]=number.equal([RANDOM_NUMBER])&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🔬 Results
&lt;/h2&gt;

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

&lt;p&gt;The results speak for themselves. With &lt;strong&gt;40k successful requests&lt;/strong&gt; and &lt;strong&gt;1,334 per second&lt;/strong&gt;, we reached numbers that were very difficult to achieve with previous versions.&lt;/p&gt;

&lt;p&gt;Not only is the new Database better at scale with simple Queries - but it also improves overall performance with more complex queries.&lt;/p&gt;

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

&lt;p&gt;The Server were Appwrite was hosted had 8-Cores with 16GB memory and it's CPU usage during the benchmark peaked around 80%. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;k6 was run on a Mac Mini M1 with 16 GB memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This performance boost is part of a series of steps we take to ensure developers can take full advantage of their Appwrite servers. We plan to share more data and insights from both the development process and Appwrite benchmarks with upcoming new versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Learn more
&lt;/h2&gt;

&lt;p&gt;You can use following resources to learn more and get help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appwrite.io" rel="noopener noreferrer"&gt;🚀 Appwrite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/appwrite/appwrite" rel="noopener noreferrer"&gt;🚀 Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/docs" rel="noopener noreferrer"&gt;📜 Appwrite Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;💬 Discord Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>performance</category>
      <category>opensource</category>
      <category>programming</category>
      <category>database</category>
    </item>
    <item>
      <title>Announcing Appwrite Web SDK 5.0</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Mon, 08 Nov 2021 16:55:55 +0000</pubDate>
      <link>https://forem.com/appwrite/announcing-appwrite-web-sdk-50-58ek</link>
      <guid>https://forem.com/appwrite/announcing-appwrite-web-sdk-50-58ek</guid>
      <description>&lt;p&gt;We are very excited to announce the release of Appwrite's Web SDK version 5.0 with complete TypeScript coverage. It is now available on &lt;a href="https://www.npmjs.com/package/appwrite" rel="noopener noreferrer"&gt;npm&lt;/a&gt;. With this version, each method will now return proper TypeScript definitions. &lt;/p&gt;

&lt;p&gt;We hope this will help a lot of developers out there who are using our Web SDK in combination with TypeScript for building their applications. Having response definitions means you will know what method will return and what properties are available to you via autocomplete without leaving your IDE.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Setup
&lt;/h2&gt;

&lt;p&gt;First, you need to install the Appwrite SDK or upgrade it to the latest version via &lt;code&gt;npm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;appwrite@5.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to import, instantiate and configure the SDK:&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;Appwrite&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;appwrite&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;sdk&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;Appwrite&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;sdk&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost/v1&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="nf"&gt;setProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PROJECT_ID&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;h2&gt;
  
  
  👥 Account
&lt;/h2&gt;

&lt;p&gt;Let's start with the simplest example by getting the current user using the &lt;code&gt;account.get()&lt;/code&gt; method. In previous versions of the SDK, this method returned a &lt;code&gt;unknown&lt;/code&gt; type, but now you don't need to create your own definitions anymore, since the SDK will offer them out-of-the-box.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;user&lt;/code&gt; object will now already contain all possible properties via a TypeScript definition. But there is more, since the &lt;code&gt;User&lt;/code&gt; model also contains the &lt;code&gt;prefs&lt;/code&gt; property containing all of the User's preferences. These can be set by the client, which means the SDK cannot provide you with typings yet.&lt;/p&gt;

&lt;p&gt;Let's assume you store the users preferred theme for your application in their preferences. You will have &lt;code&gt;Type&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MyPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new SDK allows you to pass &lt;code&gt;MyPreferences&lt;/code&gt; via a Generic - this allows you to pass your own structure to the method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyPreferences&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new &lt;code&gt;user&lt;/code&gt; object returned from &lt;code&gt;account.get()&lt;/code&gt; using a generic is now automatically extended by your &lt;code&gt;MyPreferences&lt;/code&gt; on the &lt;code&gt;prefs&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Generics can be used on any method, which can return a data structure that is allowed to be extended by the developer like the User's preferences or documents from the Database service.&lt;/p&gt;

&lt;h2&gt;
  
  
  📀 Database
&lt;/h2&gt;

&lt;p&gt;Talking about Database, let's move on to some examples how the new SDK can be used in combination with it.&lt;/p&gt;

&lt;p&gt;Assuming we have a collection containing Movies with following type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Movie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;published&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="nl"&gt;genres&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="nl"&gt;gotAnOscar&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are all properties that can be set as rules in a collection, but by default documents in Appwrite come with values like &lt;code&gt;$id&lt;/code&gt;, &lt;code&gt;$permissions&lt;/code&gt; and &lt;code&gt;$collection&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can easily import the &lt;code&gt;Models&lt;/code&gt; from the SDK and merge &lt;code&gt;Movie&lt;/code&gt; with the &lt;code&gt;Document&lt;/code&gt; type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Models&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;appwrite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Movie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;published&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="nl"&gt;genres&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="nl"&gt;gotAnOscar&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="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have all our TypeScript definitions in place, let's use them by retrieving a Document from the Database using &lt;code&gt;database.getDocument()&lt;/code&gt;. We can use Generics to tell TypeScript to use our &lt;code&gt;Movie&lt;/code&gt; type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;avatar&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;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getDocument&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&lt;/span&gt;&lt;span class="o"&gt;&amp;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;movies&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;avatar&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;For example with using the &lt;code&gt;database.listDocuments&lt;/code&gt;, which will have 2 pre-defined properties called &lt;code&gt;sum&lt;/code&gt; and &lt;code&gt;documents&lt;/code&gt;,  the type passed as a generic will be used for &lt;code&gt;documents&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;movies&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;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listDocuments&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&lt;/span&gt;&lt;span class="o"&gt;&amp;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;movies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// The sum of all documents.&lt;/span&gt;
&lt;span class="nx"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Will use an array of our Movie type.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can also be used with the &lt;code&gt;subscribe()&lt;/code&gt; method for real-time updates:&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="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&lt;/span&gt;&lt;span class="o"&gt;&amp;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;collection.movies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Will use the Movie type.&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can try it out by yourself using this &lt;a href="https://codesandbox.io/s/appwrite-web-sdk-5-0-example-tkwrm?file=/index.ts" rel="noopener noreferrer"&gt;StackBlitz&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The heavily improved TypeScript support of the new Web SDK allow you to kickstart the development of your Application and keep you focused without leaving your IDE.&lt;/p&gt;

&lt;p&gt;If you have any issues or questions feel free to reach us on &lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;our discord&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Learn more
&lt;/h2&gt;

&lt;p&gt;You can use following resources to learn more and get help&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 &lt;a href="https://appwrite.io/docs/getting-started-for-web" rel="noopener noreferrer"&gt;Getting Started Tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📜 &lt;a href="https://appwrite.io/docs" rel="noopener noreferrer"&gt;Appwrite Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;Discord Community&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cover by &lt;a href="https://www.pexels.com/@kevin-ku-92347?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels" rel="noopener noreferrer"&gt;Kevin Ku&lt;/a&gt; from &lt;a href="https://www.pexels.com/photo/data-codes-through-eyeglasses-577585/?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>appwrite</category>
    </item>
    <item>
      <title>Lessons learned from building a WebSocket server</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Tue, 14 Sep 2021 15:15:24 +0000</pubDate>
      <link>https://forem.com/appwrite/lessons-learned-from-building-a-websocket-server-4i04</link>
      <guid>https://forem.com/appwrite/lessons-learned-from-building-a-websocket-server-4i04</guid>
      <description>&lt;p&gt;&lt;a href="https://appwrite.io" rel="noopener noreferrer"&gt;Appwrite&lt;/a&gt; is an open-source, self-hosted Backend-as-a-Service that aims to make app development easier with SDKs available in a variety of programming languages.&lt;/p&gt;

&lt;p&gt;Before the release of a Realtime API with version 0.10.0, applications were only able to communicate with our REST API.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/appwrite" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2225%2F81202665-3201-4ceb-b247-f8c5feae746f.png" alt="Appwrite" width="800" height="800"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F224437%2F24510e1e-2d7b-414f-9b5c-f6566845bf04.jpeg" alt="" width="229" height="229"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/appwrite/it-s-here-announcing-appwrite-0-10-and-the-new-appwrite-realtime-api-lbm" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;It's Here! Announcing Appwrite 0.10 and the new Realtime API!&lt;/h2&gt;
      &lt;h3&gt;Eldad A. Fux for Appwrite ・ Aug 31 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#flutter&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#android&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Why did we build a Realtime API?
&lt;/h2&gt;

&lt;p&gt;REST APIs have been a popular architecture for data delivery in the past. So why do we need Realtime now?&lt;/p&gt;

&lt;p&gt;Our REST API works great and is very simple, but in order for us to allow more flexibility, and to allow developers to create new use-cases such as game development and reactive applications, we needed to add a new API layer for realtime interaction.&lt;/p&gt;

&lt;p&gt;Rather than the API client only getting new data on their next query, the new data is pushed to them immediately. If a developer is already polling the REST API for data changes, it doesn't just mean they want to access the data faster, but  is a strong indication that they really want a realtime API. &lt;/p&gt;

&lt;p&gt;Realtime APIs provide a more enjoyable developer experience that can significantly reduce application processing overhead and code complexity. Once the data is transferred to the system in real time, you allow the developers to focus on adding value to the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Since the Realtime Service is implemented on top of an already existing REST API, all messages sent over Realtime are triggered from the HTTP Server. This means that  if a resource is created or updated, the WebSocket Server will be triggered to send this action to its subscribers.&lt;/p&gt;

&lt;p&gt;The backbone of the data exchange between REST and WebSocket is a &lt;a href="https://redis.io" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; instance. We are using a single &lt;a href="https://redis.io/topics/pubsub" rel="noopener noreferrer"&gt;Pub/Sub&lt;/a&gt; channel, which is the Source of Truth for the WebSocket Server. If a new resource is added via the REST API, the HTTP server will publish the payload alongside metadata in this channel. The WebSocket Server subscribes to the channel, processes the message, decides which client is allowed to receive the message, and sends it to the destined client.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Data Flow
&lt;/h2&gt;

&lt;p&gt;At Appwrite, resources from the REST API are separated by projects, secured by permissions, and the events are categorized in channels. When a client establishes a connection to the realtime server, a project identifier is sent along with information to authenticate the connection to a user and channels via which the client will receive messages. In the following, we take the &lt;strong&gt;Car&lt;/strong&gt; resource as an example, and tell the WebSocket Server to subscribe to the &lt;strong&gt;Cars&lt;/strong&gt; channel.&lt;/p&gt;

&lt;p&gt;The WebSocket Server now allocates all roles of the user, the project, and the channels to the unique connection identifier for the client.&lt;/p&gt;

&lt;p&gt;If the &lt;strong&gt;Car&lt;/strong&gt; resource now gets updated via the REST API, the HTTP Server publishes this event to the Redis channel with its payload. The WebSocket server will then receive this event and start checking who the receiver of this event will be.&lt;/p&gt;

&lt;p&gt;The following conditions need to meet for a client:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Project ID must be equal.&lt;/li&gt;
&lt;li&gt;The Permissions of the resource must meet the user's roles.&lt;/li&gt;
&lt;li&gt;The Channel must be subscribed to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The WebSocket Server will then send the payload of the resource to all clients that meet the conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Structure
&lt;/h2&gt;

&lt;p&gt;Speed is vital for building Applications with Realtime Updates. Our Data Structure needs to be processed as quickly as possible to decide which Client is supposed and allowed to receive an event. For this, we are maintaining 2 Hashmaps in memory. One of them holding all &lt;strong&gt;Subscriptions&lt;/strong&gt;, and the other, all &lt;strong&gt;Connections&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;Looking at the previous conditions, we can see the pattern reflected in this tree. You may realize that this structure has a disadvantage, namely, there are many duplicate data entries of the connection ID. However, this disadvantage is intentional and has a specific reason – speed.&lt;/p&gt;

&lt;p&gt;The tradeoff of memory for speed is essential in a WebSocket server. This structure allows us, even with a high number of subscribers, to quickly identify them and forward the message to them, even though this might use up more memory.&lt;/p&gt;

&lt;p&gt;Below is an example of our implementation which distributes subscribers evenly across 20 different channels, then using one event to gather all subscribers for that event.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subscriptions&lt;/th&gt;
&lt;th&gt;Time used&lt;/th&gt;
&lt;th&gt;Memory used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;0.022ms&lt;/td&gt;
&lt;td&gt;11MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;0.238ms&lt;/td&gt;
&lt;td&gt;90MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;1.525ms&lt;/td&gt;
&lt;td&gt;427MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;3.678ms&lt;/td&gt;
&lt;td&gt;852MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5,000,000&lt;/td&gt;
&lt;td&gt;19.334ms&lt;/td&gt;
&lt;td&gt;4,289MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These numbers are more than fast enough for everyday applications, especially considering that a single WebSocket server is unlikely to maintain more than a million connections simultaneously. Since the WebSocket server is stateless and only manages its own subscriptions, it can easily scale horizontally and balance off the work.&lt;/p&gt;

&lt;p&gt;Now we come to our next data structure and the reason why we need it in the first place.&lt;/p&gt;

&lt;p&gt;Let's assume a client connects to our WebSocket server and subscribes to some channels. After some time, the client disconnects and we have to clean up after them and remove their connection from all channels.&lt;/p&gt;

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

&lt;p&gt;To avoid endless loops of identifying every legacy, we have an auxiliary data table that holds the project and roles of each connection easily accessible for us. Using this data, we can remove all the information from the subscribers without much searching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stumbling Blocks
&lt;/h2&gt;

&lt;p&gt;Of course, we didn't get everything right the first time. Every time we encountered and solved one hurdle, the next one was already there waiting for us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change of Permissions
&lt;/h3&gt;

&lt;p&gt;One of the first hurdles we encountered was: What happens if a user's permissions change while they are connected? What if a user is deactivated and the connection is still open?&lt;/p&gt;

&lt;p&gt;The WebSocket server would not know about this change and would continue to send all the messages that the user was allowed to receive at the beginning of the connection. This would result in exposing a resource to someone who is not authorized to read it.&lt;/p&gt;

&lt;p&gt;To prevent this phenomenon, we have added a flag to the message sent to the WebSocket server, which indicates whether the permissions for a particular user have changed. When the WebSocket Server receives this message, it checks if this user is currently connected and matches their roles with those in the backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Operating System
&lt;/h3&gt;

&lt;p&gt;Linux’s networking stack comes with sane defaults for many workloads, but the stack isn't tuned for 1+ million concurrent connections. We expected to face some form of the &lt;a href="https://en.wikipedia.org/wiki/C10k_problem" rel="noopener noreferrer"&gt;C10k problem&lt;/a&gt;, so we prepared our systems in advance &lt;a href="https://www.ibm.com/support/pages/tuning-and-debugging-maximum-connections-accepted-messagesight-v20" rel="noopener noreferrer"&gt;[1]&lt;/a&gt;&lt;a href="https://www.linkedin.com/pulse/ec2-tuning-1m-tcp-connections-using-linux-stephen-blum/" rel="noopener noreferrer"&gt;[2]&lt;/a&gt;&lt;a href="https://cromwell-intl.com/open-source/performance-tuning/tcp.html" rel="noopener noreferrer"&gt;[3]&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased the default TCP buffer sizes for the system&lt;/li&gt;
&lt;li&gt;Increased the default IPv4 port range&lt;/li&gt;
&lt;li&gt;Increased the limit for open files and file handles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite this tuning, we hit a limit of around 260k connections - past that point, the HTTP server stopped responding to our clients. We observed that our server wasn't completing the &lt;a href="https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/three-way-handshake-via-tcpip" rel="noopener noreferrer"&gt;TCP 3-way handshake&lt;/a&gt;: it would receive SYN packets from the client (as observed with &lt;a href="https://www.tcpdump.org/" rel="noopener noreferrer"&gt;tcpdump&lt;/a&gt;) but wasn't responding with ACK.&lt;/p&gt;

&lt;p&gt;After hours of fruitless debugging, we tapped other maintainers to lend their eyes to the problem. Through the power of open-source collaboration, we had our culprit in a matter of minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/sys/net/netfilter/nf_conntrack_max
262144
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because websocket connections are long-lived, we needed to increase the connection tracking limit in the networking stack. Once increased, we cruised all the way to 1 million connections with ease.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asynchronous Delivery
&lt;/h3&gt;

&lt;p&gt;When we checked the performance of sending messages everything was going well, that is,  until the moment we ran higher scaled tests and were surprised with very poor results. The culprit was the fact that we sent each message serially instead of in parallel.&lt;/p&gt;

&lt;p&gt;Fortunately, the solution was only a few lines of code away.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication with Cookies
&lt;/h3&gt;

&lt;p&gt;The first implementation of the WebSocket Server only communicated 1-way, which was sending updates to clients. This turned out to be a problem in retrospect, as our current implementation uses the HTTP-only cookie which is transmitted to the WebSocket server with the handshake.&lt;/p&gt;

&lt;p&gt;Later, when developing a demo application, we noticed that under certain circumstances this cookie is not sent, for example, when the client and server are on different domains.&lt;/p&gt;

&lt;p&gt;After a bit of research, we came across the information that the handshake is not intended as a method for authentication at all. Reason for this can be found &lt;a href="https://github.com/whatwg/html/issues/3062#issuecomment-332065542" rel="noopener noreferrer"&gt;here&lt;/a&gt; from one of the maintainers of Chrome's WebSocket implementation. This was solved by additionaly authenticating via a message over the WebSocket protocol. If the user was not authenticated via the cookie, we decided to fall back to authentication via a message and send the token of the cookie to the WebSocket Server.&lt;/p&gt;

&lt;p&gt;So, relying on the handshake for authentication alone was obviously a bad idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Of course, the above approaches might not apply for every use-case - but they are for us at this point. As &lt;a href="https://en.wikipedia.org/wiki/Donald_Knuth" rel="noopener noreferrer"&gt;Donald Knuth&lt;/a&gt; said in his book &lt;em&gt;The Art of Computer Programming&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We could micro optimize our data structure to achieve even better results with many more subscribers. However, it is easier to add another instance of the WebSocket Server behind a Load Balancer and scale horizontally. &lt;/p&gt;

&lt;p&gt;As long as this works for us, we’ll follow Donald’s advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;Thank you for your attention and we hope you enjoyed this article!&lt;/p&gt;

&lt;p&gt;Here are some handy links for more informations about Appwrite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appwrite.io" rel="noopener noreferrer"&gt;Appwrite Homepage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/appwrite" rel="noopener noreferrer"&gt;Appwrite Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;Appwrite Discord&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>serverless</category>
      <category>php</category>
      <category>pubsub</category>
    </item>
    <item>
      <title>Introducing Magic URL Login to Appwrite</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Tue, 07 Sep 2021 10:17:45 +0000</pubDate>
      <link>https://forem.com/appwrite/introducing-magic-url-login-to-appwrite-2l</link>
      <guid>https://forem.com/appwrite/introducing-magic-url-login-to-appwrite-2l</guid>
      <description>&lt;p&gt;Appwrite 0.10 introduces &lt;strong&gt;Magic URL&lt;/strong&gt; as an authentication method, which allows users to create an account without providing a password and login with a URL sent via an E-Mail.&lt;/p&gt;

&lt;p&gt;This feature is especially useful if you want to provide a passwordless authentication process for your application.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  ⚙️ Setup
&lt;/h1&gt;

&lt;p&gt;Let's learn how we can add Magic URL Authentication to a Web Application using our &lt;a href="https://appwrite.io/docs/getting-started-for-web" rel="noopener noreferrer"&gt;Web SDK&lt;/a&gt;. The same can be done with our &lt;a href="https://appwrite.io/docs/getting-started-for-flutter" rel="noopener noreferrer"&gt;Flutter SDK&lt;/a&gt; and &lt;a href="https://appwrite.io/docs/getting-started-for-android" rel="noopener noreferrer"&gt;Android SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first step is to add our Web SDK to our project with NPM like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;appwrite &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using a bundler (like &lt;a href="https://rollupjs.org/" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt; or &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;webpack&lt;/a&gt;), you can import the Appwrite module when you need it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Appwrite&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;appwrite&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;To install with a CDN (content delivery network) add the following script to your HTML file before you use any Appwrite services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/appwrite"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to initialize your SDK code with your project ID which can be found in your project settings page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Init your Web SDK&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appwrite&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;Appwrite&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;appwrite&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your Appwrite Endpoint&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;455x34dfkj&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your Appwrite Project ID&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🎩 Create a Magic URL
&lt;/h1&gt;

&lt;p&gt;Once your SDK is setup, access the &lt;strong&gt;Account service&lt;/strong&gt; and call the &lt;a href="https://appwrite.io/docs/client/account?sdk=web#accountCreateMagicURLSession" rel="noopener noreferrer"&gt;&lt;code&gt;createMagicURLSession()&lt;/code&gt;&lt;/a&gt; method. The method accepts an e-mail address and a redirect URL as arguments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Initiate the Magic URL login&lt;/span&gt;
&lt;span class="nx"&gt;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createMagicURLSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name@example.com&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;http://localhost/&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Success&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Failure&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the &lt;a href="https://appwrite.io/docs/client/account?sdk=web#accountCreateMagicURLSession" rel="noopener noreferrer"&gt;&lt;code&gt;createMagicURLSession()&lt;/code&gt;&lt;/a&gt; method completes without error, the request sends the user an email with a URL containing a secret key for the next step. When the user clicks the link, they are redirected back to the URL you provided with the secret key and userId values attached to the URL query string. This link is valid for 1 hour. If the e-mail passed did not belong to any existing user - this request will also create a user for the passed e-mail address.&lt;/p&gt;

&lt;h1&gt;
  
  
  🔐 Login with a Magic URL
&lt;/h1&gt;

&lt;p&gt;Now that the user is able to initialize the authentication process, we need to complete it by handling the redirect from the URL provided in the e-mail.&lt;/p&gt;

&lt;p&gt;Use the &lt;a href="https://appwrite.io/docs/client/account?sdk=web#accountUpdateMagicURLSession" rel="noopener noreferrer"&gt;&lt;code&gt;updateMagicURLSession()&lt;/code&gt;&lt;/a&gt; method and call it with the secret and userId values from the URL's query string.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that in order to avoid a &lt;a href="https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md" rel="noopener noreferrer"&gt;Redirect Attack&lt;/a&gt; the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlParams&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;urlParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userId&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;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;urlParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateMagicURLSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Success&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Failure&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the &lt;a href="https://appwrite.io/docs/client/account?sdk=web#accountUpdateMagicURLSession" rel="noopener noreferrer"&gt;&lt;code&gt;updateMagicURLSession()&lt;/code&gt;&lt;/a&gt; succeeded, the user is now logged in. Note that once a link is used, it cannot be used again.&lt;/p&gt;

&lt;h1&gt;
  
  
  🏁 Conclusion
&lt;/h1&gt;

&lt;p&gt;If you need help or encounter any difficulties setting up Magic URL Login with Appwrite, please &lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;join our Discord&lt;/a&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;Appwrite Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/docs" rel="noopener noreferrer"&gt;Appwrite Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appwrite.io" rel="noopener noreferrer"&gt;Appwrite Homepage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>news</category>
    </item>
    <item>
      <title>Introducing the New Appwrite Anonymous Login</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Fri, 04 Jun 2021 12:44:50 +0000</pubDate>
      <link>https://forem.com/appwrite/introducing-the-new-appwrite-anonymous-login-4d7j</link>
      <guid>https://forem.com/appwrite/introducing-the-new-appwrite-anonymous-login-4d7j</guid>
      <description>&lt;p&gt;It is important to consider the user experience when someone first comes to your application. The registration process can often prove to be a hurdle for users to use an application, especially if parts of the application are bound to an authentication process and hidden behind a login screen. Moreover, in today's world, the issue of privacy and data protection is a delicate one, especially because of new data privacy regulation, like &lt;a href="https://www.smashingmagazine.com/2018/02/gdpr-for-web-developers/" rel="noopener noreferrer"&gt;GDPR&lt;/a&gt; and &lt;a href="https://www.forbes.com/sites/forbestechcouncil/2020/11/17/cpra-could-bring-stricter-data-privacy-enforcement-heres-how-to-prepare/" rel="noopener noreferrer"&gt;CPRA&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Appwrite 0.8 introduces &lt;strong&gt;Anonymous Login&lt;/strong&gt; as an authentication method, which allows users to create an account without providing personal information such as an email address, username or password.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why do we need anonymous users?
&lt;/h1&gt;

&lt;p&gt;This feature is especially useful if you want to provide an easy authentication process for an &lt;a href="https://www.websitepolicies.com/blog/coppa-guide" rel="noopener noreferrer"&gt;underage audience&lt;/a&gt;, mobile apps or use cases where you don't want to store personal information to ensure users anonymity for sensitive subjects like politics or religion.&lt;/p&gt;

&lt;p&gt;With Appwrite you can offer your users the option to create an anonymous account, as well as the possibility to convert it into a full-fledged account with an e-mail address and password or linking to an OAuth2 service provider at a later stage.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup
&lt;/h1&gt;

&lt;p&gt;Enough talking, let's learn how we can add Anonymous Authentication to a Web and a Flutter Application using our &lt;a href="https://appwrite.io/docs/getting-started-for-web" rel="noopener noreferrer"&gt;Web SDK&lt;/a&gt; and &lt;a href="https://appwrite.io/docs/getting-started-for-flutter" rel="noopener noreferrer"&gt;Flutter SDK&lt;/a&gt;. The same can be done with other client SDKs we might release in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web
&lt;/h3&gt;

&lt;p&gt;The first step is to add our Web SDK to our project with NPM like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;appwrite &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using a bundler (like &lt;a href="https://rollupjs.org/" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt; or &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;webpack&lt;/a&gt;), you can import the Appwrite module when you need it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Appwrite&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;appwrite&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;To install with a CDN (content delivery network) add the following scripts to the bottom of your tag, but before you use any Appwrite services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/appwrite"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to initialize your SDK code with your project ID which can be found in your project settings page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Init your Web SDK&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appwrite&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;Appwrite&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;appwrite&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your Appwrite Endpoint&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;455x34dfkj&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your Appwrite Project ID&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flutter
&lt;/h3&gt;

&lt;p&gt;The first step is to add our Flutter SDK to our project.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;appwrite: ^0.6.0&lt;/code&gt; or the latest version in your project's &lt;code&gt;pubspec.yaml&lt;/code&gt; file under dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;appwrite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^0.6.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run following command to download the dependencies or upon saving your IDE might automatically run this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter pub get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next step is to initialize your SDK code with your project ID which can be found in your project settings page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:appwrite/appwrite.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://localhost/v1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your Appwrite Endpoint&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'455x34dfkj'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your Appwrite Project ID&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Create an Anonymous User
&lt;/h1&gt;

&lt;p&gt;Once your SDK is setup, access the Account service and call the &lt;a href="https://appwrite.io/docs/client/account?sdk=web#accountCreateAnonymousSession" rel="noopener noreferrer"&gt;&lt;code&gt;createAnonymousSession()&lt;/code&gt;&lt;/a&gt; method. &lt;/p&gt;

&lt;h3&gt;
  
  
  Web
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create an anonymous user and login&lt;/span&gt;
&lt;span class="nx"&gt;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createAnonymousSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Success&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Failure&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flutter
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create an anonymous user and login&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createAnonymousSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kd"&gt;on&lt;/span&gt; &lt;span class="n"&gt;AppwriteException&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;message&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;If the &lt;a href="https://appwrite.io/docs/client/account?sdk=web#accountCreateAnonymousSession" rel="noopener noreferrer"&gt;&lt;code&gt;createAnonymousSession()&lt;/code&gt;&lt;/a&gt; method completes without error, the request will create an anonymous user and automatically logs in the user, setting up the cookie for following requests. Now the user is authenticated and is allowed to access his or her private data and settings.&lt;/p&gt;

&lt;h1&gt;
  
  
  Convert to Permanent User
&lt;/h1&gt;

&lt;p&gt;When an anonymous user signs up, you might want to allow them to continue their work with a permanent account. This will also allow the user to recover his account in the future and switch between devices. &lt;/p&gt;

&lt;p&gt;With Appwrite there is 2 ways of doing so.&lt;/p&gt;

&lt;h2&gt;
  
  
  E-Mail
&lt;/h2&gt;

&lt;p&gt;By &lt;a href="https://appwrite.io/docs/client/account#accountUpdateEmail" rel="noopener noreferrer"&gt;updating the email address&lt;/a&gt; of an anonymous account, we can pass an email address and a password to the &lt;code&gt;account.updateEmail&lt;/code&gt; method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email@example.com&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;secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Success&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Failure&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flutter
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;email:&lt;/span&gt; &lt;span class="s"&gt;'email@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;password:&lt;/span&gt;&lt;span class="s"&gt;'secret'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kd"&gt;on&lt;/span&gt; &lt;span class="n"&gt;AppwriteException&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;message&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;This will convert the anonymous user to a permanent account with &lt;strong&gt;&lt;a href="mailto:email@example.com"&gt;email@example.com&lt;/a&gt;&lt;/strong&gt; as email and &lt;strong&gt;secret&lt;/strong&gt; as his or her password.&lt;/p&gt;

&lt;h2&gt;
  
  
  OAuth2
&lt;/h2&gt;

&lt;p&gt;By calling the &lt;a href="https://appwrite.io/docs/client/account#accountCreateOAuth2Session" rel="noopener noreferrer"&gt;&lt;code&gt;account.createOAuth2Session&lt;/code&gt;&lt;/a&gt; method from an anonymous account, the user will be automatically converted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createOAuth2Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;google&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;https://localhost/success&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;https://localhost/failure&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;h3&gt;
  
  
  Flutter
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createOAuth2Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;provider:&lt;/span&gt; &lt;span class="s"&gt;'google'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Both options offer users to transfer all their information to an account and use them on other devices.&lt;/p&gt;

&lt;p&gt;If you need help or encounter any difficulties setting up Anonymous Login with Appwrite, please &lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;join our Discord&lt;/a&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;Appwrite Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/docs" rel="noopener noreferrer"&gt;Appwrite Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appwrite.io" rel="noopener noreferrer"&gt;Appwrite Homepage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@jdent?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Jason Dent&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/privacy?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>flutter</category>
      <category>javascript</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Appwrite's Isomorphic Web SDK</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Thu, 03 Jun 2021 18:09:29 +0000</pubDate>
      <link>https://forem.com/appwrite/appwrite-s-isomorphic-web-sdk-3a55</link>
      <guid>https://forem.com/appwrite/appwrite-s-isomorphic-web-sdk-3a55</guid>
      <description>&lt;p&gt;We at Appwrite recently announced our new web SDK and would love to share the improvements we introduced. This article is a quick summary of what has changed and things you need to keep in mind while using the latest version of our SDK.&lt;/p&gt;

&lt;p&gt;For the developers not much has changed, but internally some things have been rewritten that enable completely new use cases.&lt;/p&gt;

&lt;h1&gt;
  
  
  Breaking Changes
&lt;/h1&gt;

&lt;p&gt;Let's start with the first and only breaking change: the way our Web SDK is imported. We have switched from default to named exports. &lt;/p&gt;

&lt;p&gt;This allows for innovative auto completion and takes away the user's decision making when importing. Every decision you need to make slows you down, which is why things like coding conventions lead to faster development.&lt;/p&gt;

&lt;p&gt;It also unifies the importing process, which could be different depending on the bundler in our previous approach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Appwrite&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;appwrite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// is now&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;Appwrite&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;appwrite&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;h1&gt;
  
  
  Isomorphic
&lt;/h1&gt;

&lt;p&gt;Previously, our SDK was initialized in the &lt;code&gt;window&lt;/code&gt; object of the browser. This meant that the SDK only worked in the browser and did not cooperate with technologies like &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, &lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt.js&lt;/a&gt; or &lt;a href="https://kit.svelte.dev/" rel="noopener noreferrer"&gt;Svelte Kit&lt;/a&gt;, which also interact server-side in &lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's why we refactored parts of our SDK so that it acts isomorphically and according to the environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Node.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Appwrite&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;appwrite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ESM - Modern Javascript&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;Appwrite&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;appwrite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// IIFE - Browser&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Appwrite&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Appwrite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  JWT
&lt;/h1&gt;

&lt;p&gt;Talking about server-side rendering, when doing SDK calls in your users scope from the server, it is not possible right away, since the HTTP-only cookie used for authentication is saved in the user's browser. That's why the Web SDK now allows to use JWT for authentication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sdk&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;Appwrite&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setJWT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eyJhbGciOiJIUzI1NiI.....&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Prints out the user attached to the JWT&lt;/span&gt;
&lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Typescript
&lt;/h1&gt;

&lt;p&gt;Also, the first preparations for the upcoming response models have been integrated, which is going to provide full Typescript coverage across every service.&lt;/p&gt;

&lt;p&gt;For this release, the &lt;code&gt;Promise&amp;lt;unknown&amp;gt;&lt;/code&gt; generic has been added to all methods that will receive a response from the server. This way it is easier for developers right now to implement their own definitions when working with Appwrite and Typescript.&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;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userA&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;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Learn More
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;You can find the new version of the NPM package &lt;a href="https://www.npmjs.com/package/appwrite" rel="noopener noreferrer"&gt;here&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;Checkout &lt;a href="https://github.com/appwrite/appwrite" rel="noopener noreferrer"&gt;Appwrite's&lt;/a&gt; Github Repo.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;Our Discord Server&lt;/a&gt; is the place to be if you ever get stuck.&lt;/li&gt;
&lt;li&gt;You can find all our Documentation &lt;a href="https://appwrite.io/docs" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>flutter</category>
      <category>showdev</category>
    </item>
    <item>
      <title>#30DaysOfAppwrite : JWT Support in Appwrite</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Wed, 26 May 2021 13:57:39 +0000</pubDate>
      <link>https://forem.com/appwrite/30daysofappwrite-jwt-l08</link>
      <guid>https://forem.com/appwrite/30daysofappwrite-jwt-l08</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://30days.appwrite.io/" rel="noopener noreferrer"&gt;#30DaysOfAppwrite&lt;/a&gt; is a month-long event focused on giving developers a walk-through of all of Appwrite's features, starting from the basics to more advanced features like Cloud Functions! Alongside, we will also be building a fully-featured Medium clone to demonstrate how these concepts can be applied when building a real-world app. We also have some exciting prizes for developers who follow along with us!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a JWT
&lt;/h2&gt;

&lt;p&gt;JWT (&lt;strong&gt;J&lt;/strong&gt;SON &lt;strong&gt;W&lt;/strong&gt;eb &lt;strong&gt;T&lt;/strong&gt;oken) is a standard used to create access tokens for an application. It works this way: the server generates a token that certifies the user identity and sends it to the client. The client will send the token back to the server for every subsequent request, so the server knows the request comes from a particular identity.&lt;/p&gt;

&lt;p&gt;A well-formed JWT consists of three concatenated &lt;strong&gt;Base64&lt;/strong&gt; url-encoded strings, separated by dots (&lt;code&gt;.&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Header&lt;/strong&gt;: contains metadata about the type of token and the cryptographic algorithms used to secure its contents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload&lt;/strong&gt;: contains verifiable security statements, such as the identity of the user and the permissions they are allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature&lt;/strong&gt;: used to validate that the token is trustworthy and has not been tampered with.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This architecture proves to be very effective in modern Web Apps, whereafter the user is authenticated, we perform API requests either to a REST or a GraphQL API. &lt;/p&gt;

&lt;p&gt;Anyway, it is not always recommended to use JWTs for sessions. Using a regular server-side session combined with Cookies is usually much more efficient and less prone to data exposure.&lt;/p&gt;

&lt;h3&gt;
  
  
  So, why do we need a JWT then?
&lt;/h3&gt;

&lt;p&gt;In the modern web, you will often have several entities communicating with each other. Certain features will naturally be restricted and require some sort of authorization mechanism. At Appwrite we use Cookies for the client-side to communicate with the backend. &lt;/p&gt;

&lt;p&gt;Using a JWT, you will be able to authorize the user on the Server-Side within a Cloud Function, Microservice, or SSR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a JWT
&lt;/h2&gt;

&lt;p&gt;Version 0.8 of Appwrite introduced JWT, and it's really easy to generate using the Web or Flutter SDK. Because JWTs are used for authentication and authorization - we can only generate them when we are authenticated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createJWT&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Success&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Failure&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flutter
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;account.createJWT().then((response) {
    print(response);
}).catchError((error) {
    print(error.response);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;createJWT()&lt;/code&gt; method will receive an object like this:&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="err"&gt;jwt:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiIsInR5cCI6I..."&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;p&gt;This JWT will be valid for 15 minutes and can only be generated &lt;strong&gt;10 times&lt;/strong&gt; every &lt;strong&gt;60 minutes&lt;/strong&gt; per &lt;strong&gt;user account&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  JWT with Server SDK
&lt;/h2&gt;

&lt;p&gt;Now that we can get our hands on a JWT, we can use it to do actions on the server on behalf of a user without needing to log in or provide an API Key.  &lt;/p&gt;

&lt;p&gt;For demonstration, let's try to get our current user with a Node.js script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir appwrite-jwt-test
cd appwrite-jwt-test
npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add &lt;code&gt;node-appwrite&lt;/code&gt; as a dependency:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;index.js&lt;/code&gt; file and put in the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appwrite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-appwrite&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;client&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;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&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;account&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;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;client&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[ENDPOINT]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your API Endpoint&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[PROJECT_ID]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your project ID&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setJWT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[INSERT_JWT_HERE]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your users JWT&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&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;Remember to fill out the endpoint, project ID, and JWT. Keep in mind that a JWT is only valid for 15 minutes after generation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can execute this file with &lt;code&gt;node index.js&lt;/code&gt;, and if everything goes well, we should see our user's object 👏&lt;/p&gt;

&lt;h2&gt;
  
  
  JWT With Cloud Functions
&lt;/h2&gt;

&lt;p&gt;Remember from &lt;a href="https://dev.to/appwrite/30daysofappwrite-appwrite-cloud-functions-1pf2"&gt;Day 23&lt;/a&gt; that users can execute Cloud Functions over the Rest API? If a user does this, the Cloud Function will be passed a JWT in the &lt;code&gt;APPWRITE_FUNCTION_JWT&lt;/code&gt; environment variable by default for the user who executed the function.&lt;/p&gt;

&lt;p&gt;This way, we don't even have to create a JWT from the client-side and pass it around 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  JWT With SSR
&lt;/h2&gt;

&lt;p&gt;With release 3.0.0 of the Web SDK for Appwrite, we have refactored it to be isomorphic. This is important in the ecosystem of JavaScript - since, with the rising popularity of SSR, libraries need to work in the Browser - as well as on the Server Side with Node.js.&lt;/p&gt;

&lt;p&gt;That's why we have added the &lt;code&gt;setJWT(jwt)&lt;/code&gt; method, found in the Server SDK, to the Web SDK as well - which allows developers to use the same SDK for Client and Server Side actions with Frameworks like &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, &lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt.js&lt;/a&gt; and &lt;a href="https://kit.svelte.dev/" rel="noopener noreferrer"&gt;Svelte Kit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;We hope you liked this write-up. You can follow &lt;a href="https://twitter.com/search?q=%2330daysofappwrite" rel="noopener noreferrer"&gt;#30DaysOfAppwrite&lt;/a&gt; on Social Media to keep up with all of our posts. The complete event timeline can be found &lt;a href="http://30days.appwrite.io" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;Discord Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://appwrite.io/" rel="noopener noreferrer"&gt;Appwrite Homepage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/appwrite" rel="noopener noreferrer"&gt;Appwrite's Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to reach out to us on Discord if you would like to learn more about Appwrite, Aliens, or Unicorns 🦄. Stay tuned for tomorrow's article! Until then 👋&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>flutter</category>
      <category>30daysofappwrite</category>
    </item>
    <item>
      <title>#30DaysOfAppwrite : Cloud Function with CRON</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Tue, 25 May 2021 13:13:26 +0000</pubDate>
      <link>https://forem.com/appwrite/30daysofappwrite-cloud-function-with-cron-258c</link>
      <guid>https://forem.com/appwrite/30daysofappwrite-cloud-function-with-cron-258c</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://30days.appwrite.io/" rel="noopener noreferrer"&gt;#30DaysOfAppwrite&lt;/a&gt; is a month-long event focused on giving developers a walk-through of all of Appwrite's features, starting from the basics to more advanced features like Cloud Functions! Alongside we will also be building a fully-featured Medium clone to demonstrate how these &lt;br&gt;
concepts can be applied when building a real-world app. We also have some exciting prizes for developers who follow along with us!&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating Statistics
&lt;/h2&gt;

&lt;p&gt;On Day 24, we created a Cloud Function that was triggered by an event. This comes in handy when you want to react to interactions from the Client Side. For Day 25, we are going to create a Cloud Function that will be triggered at particular intervals of time. We can accomplish this by adding a CRON Schedule to our Cloud Function. &lt;/p&gt;

&lt;p&gt;For Day 25, we are creating a Cloud Function that will run every day and create statistics for our App. We are going to save the number of profiles and posts every day in a Collection - this data allows us to develop Graphs and Statistics to track.&lt;/p&gt;

&lt;p&gt;First of all, we are going to create a new &lt;strong&gt;Statistics&lt;/strong&gt; collection with the following &lt;strong&gt;Rules&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Profiles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; profiles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule Type:&lt;/strong&gt; Integer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; posts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule Type:&lt;/strong&gt; Integer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Timestamp:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; timestamp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule Type:&lt;/strong&gt; Integer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;Permissions&lt;/strong&gt; will be &lt;code&gt;role:all&lt;/code&gt; for &lt;em&gt;read&lt;/em&gt;, so anyone can retrieve the statistic, and we are going to leave the &lt;em&gt;write&lt;/em&gt; permissions empty. Leaving the &lt;em&gt;write&lt;/em&gt; empty will block anyone from writing to that Collection, except when they're using an API key.&lt;/p&gt;

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

&lt;p&gt;Now that the Collection is prepared let's start with our cloud function. For this example, we are going to create another Node.js Cloud Function. As environment variables, we are going to add the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;APPWRITE_PROJECT_ID&lt;/strong&gt;: Insert your project ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APPWRITE_ENDPOINT&lt;/strong&gt;: Insert your Appwrite endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APPWRITE_API_KEY&lt;/strong&gt;: Insert an API key that has &lt;em&gt;documents.read&lt;/em&gt; and &lt;em&gt;documents.write&lt;/em&gt; permissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STATISTICS_COLLECTION&lt;/strong&gt;: Insert the ID of the Statistics collection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PROFILE_COLLECTION&lt;/strong&gt;: Insert the ID of the Profile collection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST_COLLECTION&lt;/strong&gt;: Insert the ID of the Post collection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the &lt;strong&gt;Settings&lt;/strong&gt; page of this Cloud Function, you also need to add a value in the &lt;strong&gt;Schedule (CRON Syntax)&lt;/strong&gt; field. For our use case, we are setting it to &lt;code&gt;0 12 * * *&lt;/code&gt;, which will execute this function every day at 12:00.&lt;/p&gt;

&lt;p&gt;We will again move to our project directory and use the Appwrite CLI to create a NodeJS 16.0 function called &lt;code&gt;create-statistics&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;appwrite init &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the &lt;code&gt;src/index.js&lt;/code&gt;, put in the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appwrite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node-appwrite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STATISTICS_COLLECTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&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;STATISTICS_COLLECTION&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;PROFILE_COLLECTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&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;PROFILE_COLLECTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;POST_COLLECTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&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;POST_COLLECTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Initialise the client SDK&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&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;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&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;database&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;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;client&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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;APPWRITE_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your API Endpoint&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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;APPWRITE_PROJECT_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your project ID&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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;APPWRITE_API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Your secret API key&lt;/span&gt;
  &lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Get the sum of Profiles and Posts&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PROFILE_COLLECTION&lt;/span&gt;&lt;span class="p"&gt;,&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&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;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;POST_COLLECTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;appwrite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;published&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


  &lt;span class="c1"&gt;// Waiting for all promises to resolve and write into the Statistics Collection&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;profiles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;profiles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STATISTICS_COLLECTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unique()&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;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;profiles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;role:all&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&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;We will then deploy the function to our instance, go ahead and change our directory to the project one and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;appwrite deploy &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to select our &lt;code&gt;create-statistics&lt;/code&gt; function and deploy it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Our Cloud Function
&lt;/h2&gt;

&lt;p&gt;We can easily test out our function by waiting for 12:00 or just executing it manually on your Functions page. If the function was executed successfully, you could head over to the &lt;strong&gt;Statistics&lt;/strong&gt; Collection, and you should find a document like this:&lt;/p&gt;

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

&lt;p&gt;With this data, we can create Charts and Statistics to monitor the usage of our application. &lt;/p&gt;

&lt;p&gt;Feel free to share how you would leverage this data and implement it in the Medium clone!&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;We hope you liked this write-up. You can follow &lt;a href="https://twitter.com/search?q=%2330daysofappwrite" rel="noopener noreferrer"&gt;#30DaysOfAppwrite&lt;/a&gt; on Social Media to keep up with all of our posts. The complete event timeline can be found &lt;a href="http://30days.appwrite.io" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;Discord Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://appwrite.io/" rel="noopener noreferrer"&gt;Appwrite Homepage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/appwrite" rel="noopener noreferrer"&gt;Appwrite's Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to reach out to us on Discord if you would like to learn more about Appwrite, Aliens, or Unicorns 🦄. Stay tuned for tomorrow's article! Until then 👋&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>flutter</category>
      <category>30daysofappwrite</category>
    </item>
    <item>
      <title>#30DaysOfAppwrite : Our First Cloud Function</title>
      <dc:creator>Torsten Dittmann</dc:creator>
      <pubDate>Mon, 24 May 2021 13:10:16 +0000</pubDate>
      <link>https://forem.com/appwrite/30daysofappwrite-our-first-cloud-function-59k6</link>
      <guid>https://forem.com/appwrite/30daysofappwrite-our-first-cloud-function-59k6</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://30days.appwrite.io/" rel="noopener noreferrer"&gt;#30DaysOfAppwrite&lt;/a&gt; is a month-long event focused on giving developers a walk-through of all of Appwrite's features, starting from the basics to more advanced features like Cloud Functions! Alongside we will also be building a fully-featured Medium clone to demonstrate how these &lt;br&gt;
concepts can be applied when building a real-world app. We also have some exciting prizes for developers who follow along with us!&lt;/p&gt;
&lt;h2&gt;
  
  
  Reading Time
&lt;/h2&gt;

&lt;p&gt;The first Cloud Function we will implement in our Medium clone will be a function that calculates the reading time of a post. Calculating the reading time of posts as you browse can be quite an expensive task, depending on the length of the content. In order not to slow down your application unnecessarily, we will run this process on our server.&lt;/p&gt;

&lt;p&gt;We will use a formula suggested in this &lt;a href="https://infusion.media/content-marketing/how-to-calculate-reading-time/" rel="noopener noreferrer"&gt;blog post from Infusion Media&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First of all, we are going to add the following rule to our &lt;strong&gt;Posts&lt;/strong&gt; collection:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; readingTime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule Type:&lt;/strong&gt; String&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Now that the Database is prepared let's start with our cloud function. For this, we will create a Cloud function with Node.js runtime. In your &lt;strong&gt;Function Dashboard&lt;/strong&gt; under the &lt;strong&gt;Settings Tab&lt;/strong&gt; we need to enable the trigger for the events &lt;strong&gt;database.documents.create&lt;/strong&gt; and &lt;strong&gt;database.documents.update&lt;/strong&gt;. As environment variables, we are going to add the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;APPWRITE_PROJECT_ID&lt;/strong&gt;: Insert your project ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APPWRITE_ENDPOINT&lt;/strong&gt;: Insert your appwrite endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APPWRITE_API_KEY&lt;/strong&gt;: Insert an API key with &lt;em&gt;documents.write&lt;/em&gt; permission.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POSTS_COLLECTION&lt;/strong&gt;: Insert the ID of the Posts collection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To stay true to the language of our demo project, we will write it in Node.js.&lt;/p&gt;

&lt;p&gt;Change your current directory to the project folder we created two articles ago. Then, we are going to create a new function using the Appwrite CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;appwrite init &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the name to &lt;code&gt;reading-time&lt;/code&gt;, and the runtime should be &lt;code&gt;node-16.0&lt;/code&gt;. This will create a new folder called &lt;code&gt;reading-time&lt;/code&gt; with a ready-to-go template for nodeJS.&lt;/p&gt;

&lt;p&gt;Move into the &lt;code&gt;reading-time&lt;/code&gt; folder and edit the &lt;code&gt;src/index.js&lt;/code&gt; file, then replace the entire content with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node-appwrite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DATA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;APPWRITE_FUNCTION_EVENT_DATA&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;POSTS_COLLECTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POSTS_COLLECTION&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;$collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;published&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DATA&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;$collection&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;POSTS_COLLECTION&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed, invalid collection or not published&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&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;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&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;database&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;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APPWRITE_ENDPOINT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APPWRITE_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Environment variables are not set. Function cannot use Appwrite SDK.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APPWRITE_ENDPOINT&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="nf"&gt;setProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APPWRITE_PROJECT_ID&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="nf"&gt;setKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APPWRITE_API_KEY&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="nf"&gt;setSelfSigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;00C0-&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;017F&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+|&lt;/span&gt;&lt;span class="se"&gt;[\u&lt;/span&gt;&lt;span class="sr"&gt;0400-&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;04FF&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;0500–&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;052F&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+|&lt;/span&gt;&lt;span class="se"&gt;[\u&lt;/span&gt;&lt;span class="sr"&gt;0370-&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;03FF&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;1F00-&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;1FFF&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+|&lt;/span&gt;&lt;span class="se"&gt;[\u&lt;/span&gt;&lt;span class="sr"&gt;4E00–&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;9FFF&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;|&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minutes&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="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;readingTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;m &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;s`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Don't update Post if reading time has not changed&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;readingTime&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;DATA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readingTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Post &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; has not changed`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&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;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;readingTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;readingTime&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&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;This function is triggered on every document &lt;strong&gt;write&lt;/strong&gt; and &lt;strong&gt;update&lt;/strong&gt; events, calculates the reading time, and saves it to the &lt;em&gt;readingTime&lt;/em&gt; attribute. We are also checking if the reading time changes - this is necessary not to create an infinite loop and unnecessarily hit the API with our Cloud Function.&lt;/p&gt;

&lt;p&gt;We will then deploy the function to our instance, go ahead and change our directory to the project one and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;appwrite deploy &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to select our &lt;code&gt;reading-time&lt;/code&gt; function and deploy it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Our Cloud Function
&lt;/h2&gt;

&lt;p&gt;We can quickly test our reading time calculation by navigating to the Posts collection and editing the text of a published post. You can either navigate to the Functions Dashboard and check the log or refresh the document we just updated and see how the &lt;strong&gt;readingTime&lt;/strong&gt; attribute has magically been updated!&lt;/p&gt;

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

&lt;p&gt;The only thing left for us is to add the reading time to our Medium clone at the top of each post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// src/routes/Post.svelte

//...
&lt;span class="nt"&gt;&amp;lt;i&amp;gt;&lt;/span&gt;
  {post.readingTime}
&lt;span class="nt"&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
//...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;We hope you liked this write-up. You can follow &lt;a href="https://twitter.com/search?q=%2330daysofappwrite" rel="noopener noreferrer"&gt;#30DaysOfAppwrite&lt;/a&gt; on Social Media to keep up with all of our posts. The complete event timeline can be found &lt;a href="http://30days.appwrite.io" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appwrite.io/discord" rel="noopener noreferrer"&gt;Discord Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://appwrite.io/" rel="noopener noreferrer"&gt;Appwrite Homepage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/appwrite" rel="noopener noreferrer"&gt;Appwrite's Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to reach out to us on Discord if you would like to learn more about Appwrite, Aliens or Unicorns 🦄. Stay tuned for tomorrow's article! Until then 👋&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>flutter</category>
      <category>30daysofappwrite</category>
    </item>
  </channel>
</rss>
