<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Suragch on Medium]]></title>
        <description><![CDATA[Stories by Suragch on Medium]]></description>
        <link>https://medium.com/@suragch?source=rss-d85b5f6a2d6c------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*mZO2ZGLvUUu0Jps8.jpg</url>
            <title>Stories by Suragch on Medium</title>
            <link>https://medium.com/@suragch?source=rss-d85b5f6a2d6c------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 09:36:21 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@suragch/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Creating a profile site in Dart with Jaspr]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://suragch.medium.com/creating-a-profile-site-in-dart-with-jaspr-7129421c4c1d?source=rss-d85b5f6a2d6c------2"><img src="https://cdn-images-1.medium.com/max/1932/1*XmlZ06Cah9_8oqZ5OT98Dw.png" width="1932"></a></p><p class="medium-feed-snippet">A getting-started guide</p><p class="medium-feed-link"><a href="https://suragch.medium.com/creating-a-profile-site-in-dart-with-jaspr-7129421c4c1d?source=rss-d85b5f6a2d6c------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://suragch.medium.com/creating-a-profile-site-in-dart-with-jaspr-7129421c4c1d?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/7129421c4c1d</guid>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Wed, 08 May 2024 11:21:45 GMT</pubDate>
            <atom:updated>2024-05-08T11:21:45.334Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Using PostgreSQL on a Dart server]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://suragch.medium.com/using-postgresql-on-a-dart-server-ad0e40b11947?source=rss-d85b5f6a2d6c------2"><img src="https://cdn-images-1.medium.com/max/1707/1*3AMZESfFffXDMWVbZGALSQ.png" width="1707"></a></p><p class="medium-feed-snippet">A tutorial on integrating the Dart Shelf server with a Postgres database</p><p class="medium-feed-link"><a href="https://suragch.medium.com/using-postgresql-on-a-dart-server-ad0e40b11947?source=rss-d85b5f6a2d6c------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://suragch.medium.com/using-postgresql-on-a-dart-server-ad0e40b11947?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/ad0e40b11947</guid>
            <category><![CDATA[dartlang]]></category>
            <category><![CDATA[backend-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[postgres]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Sat, 03 Feb 2024 09:22:58 GMT</pubDate>
            <atom:updated>2024-02-03T09:35:22.362Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[A PocketBase backend for Flutter and Dart]]></title>
            <link>https://suragch.medium.com/a-pocketbase-backend-for-flutter-and-dart-c962bea4e3f1?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/c962bea4e3f1</guid>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[progra]]></category>
            <category><![CDATA[baas]]></category>
            <category><![CDATA[pocketbase]]></category>
            <category><![CDATA[dart]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Sat, 27 Jan 2024 10:13:49 GMT</pubDate>
            <atom:updated>2024-02-14T11:42:46.115Z</atom:updated>
            <content:encoded><![CDATA[<p><em>A step-by-step tutorial</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nYYihrx2SB3IYaVOrt01JA.png" /></figure><p><em>Updated February 14, 2024.</em></p><p>I’ve been looking into solutions for self-hosting a backend server for my Flutter app. In addition to performing normal read-write operations, it also needs to support user authentication. To that end, I’ve been experimenting with <a href="https://suragch.medium.com/dart-shelf-server-tutorial-513ea23485a3?sk=96aeb067e6e9b3d96836bafc581cf6ec">Shelf</a>, <a href="https://supabase.com/">Supabase</a>, and <a href="https://suragch.medium.com/dart-authentication-server-with-supertokens-137540ecb2cc?sk=46284ac21e6908c8cab26f95431131c9">SuperTokens</a>.</p><p>Then I discovered <a href="https://pocketbase.io/">PocketBase</a>.</p><p>Look at what this Backend as a Service (BaaS) offers:</p><ul><li>Open source.</li><li>30k stars on GitHub and under active development.</li><li>Has a <a href="https://github.com/pocketbase/dart-sdk">Dart SDK</a>.</li><li>Email-password or social auth (Google, Apple, etc).</li><li>SQLite database, configurable with rules and user roles.</li><li>Realtime updates.</li><li>File storage.</li><li>Admin dashboard.</li><li>Super easy deployment with a single file.</li></ul><p>And that’s just a partial list.</p><p>The article that follows will walk you through the steps of connecting your Flutter app to PocketBase. I wrote it partly to teach myself how to do it and partly to teach you. If you still have any questions when you’re finished, please leave a comment. I’ll include some of my own thoughts at the end of the article as well as some reasons you might not choose PocketBase.</p><p><strong><em>Note</em></strong><em>: This is an intermediate-level tutorial. I’ll assume you already know how to do basic Flutter tasks like building a UI and adding packages.</em></p><h3>Getting started</h3><p>Start by creating a new Flutter project.</p><p>Then build a UI like the one in the image below. It should include a Text widget to display the user’s logged-in status and eight buttons titled Sign up, Sign in, Refresh token, Sign out, Create, Read, Update, and Delete:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wLuj9o_YXL1Y-5MDqb23rA.png" /></figure><p>You’ll use the buttons to learn how to authenticate a user and perform CRUD operations with PocketBase.</p><h3>Running PocketBase locally</h3><p>During development, it will be more convenient to have the PocketBase server running on your local machine than on a remote one.</p><p>Go to the <a href="https://pocketbase.io/docs/">PocketBase documentation page</a> and download the version for your local machine.</p><p>Extract the folder and run the following to start the server:</p><pre>./pocketbase serve</pre><p><strong><em>Note</em></strong><em>: On Mac, you need to open the folder in Finder, right-click the </em><strong><em>pocketbase</em></strong><em> file, and choose </em><strong><em>Open</em></strong><em> to bypass the security settings for downloaded files. See more </em><a href="https://support.apple.com/en-is/guide/mac-help/mh40616/mac"><em>here</em></a><em>. After that, run the </em>./pocketbase serve<em> command again to start the server.</em></p><p>PocketBase will also create a folder named pb_data where it will store the data.</p><p>Go to <a href="http://127.0.0.1:8090/">http://127.0.0.1:8090/</a> in your browser and you should get a 404 response. In this case, that good. It means the server is working.</p><h3>Creating an admin user</h3><p>Next, go to <a href="http://127.0.0.1:8090/_/">http://127.0.0.1:8090/_/</a> to set up the admin account. You’ll see the following screen:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*99YLv4nJn16_4UM4VODlBw.png" /></figure><p>Fill in the fields and then log in. You’ll be greeted with the following admin dashboard:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U8X1eewyuXB6QxDLj9dyMA.png" /></figure><p>You don’t need to do anything now, but keep this browser window open. You’ll come back here later to see the result of the changes you make from your Flutter client.</p><h3>Adding the PocketBase Dart SDK to Flutter</h3><p>Although you could communicate with the PocketBase server using its <a href="https://pocketbase.io/docs/api-records/">REST API</a>, it will be much easier to directly use the official Dart SDK.</p><p>Back in your Flutter app, add the <a href="https://pub.dev/packages/pocketbase">pocketbase</a> package to <strong>pubspec.yaml</strong>:</p><pre>dependencies:<br>  pocketbase: ^0.18.0</pre><p>You’ll import PocketBase wherever you need it like so:</p><pre>import &#39;package:pocketbase/pocketbase.dart&#39;;</pre><p>You’ll also need an instance of PocketBase to connect with the server. Create it wherever it makes sense for your state management approach:</p><pre>final pb = PocketBase(&#39;http://127.0.0.1:8090/&#39;);</pre><p>If you’re doing development using the Android emulator, then use port 10.0.2.2:</p><pre>final pb = PocketBase(&#39;http://10.0.2.2:8090/&#39;);</pre><p>I use the <a href="https://suragch.medium.com/flutter-state-management-for-minimalists-4c71a2f2f0c1?sk=6f9cedfb550ca9cc7f88317e2e7055a0">minimalist state management approach</a>. Since I only need a reference to PocketBase in one file for today’s tutorial, I’ll initialize it directly in my state management class. However, if I needed it in multiple places throughout the app, I’d use <a href="https://pub.dev/packages/get_it">GetIt</a>.</p><h3>Signing up a new user</h3><p>PocketBase makes it very easy to perform the various authentication flows.</p><p>Add a method to your Flutter project that will run when the <strong>Sign up</strong> button is pressed:</p><pre>Future&lt;void&gt; signUp() async {<br>  final body = &lt;String, dynamic&gt;{<br>    &quot;username&quot;: &quot;Bob&quot;,<br>    &quot;email&quot;: &quot;bob@example.com&quot;,<br>    &quot;password&quot;: &quot;12345678&quot;,<br>    &quot;passwordConfirm&quot;: &quot;12345678&quot;,<br>    &quot;name&quot;: &quot;Bob Smith&quot;<br>  };<br><br>  final record = await pb.collection(&#39;users&#39;).create(body: body);<br>  print(record);<br>}</pre><p>Here are some notes about that code:</p><ul><li>pb is the PocketBase instance that you created in the last step.</li><li>users is the default collection (table) that PocketBase uses for authentication.</li><li>create adds a new record (row) to the collection. In this case, that means a new user.</li><li>You pass the email, password, and other parameters as a map to the create body. You can read about the other auth record fields in the <a href="https://pocketbase.io/docs/api-records/#create-record">docs</a>.</li></ul><p>Run the code above and you should see something similar to the response below:</p><pre>{<br>  &quot;id&quot;:&quot;79yvk2r0lxnt6ob&quot;,<br>  &quot;created&quot;:&quot;2024-01-26 03:02:11.967Z&quot;,<br>  &quot;updated&quot;:&quot;2024-01-26 03:02:11.967Z&quot;,<br>  &quot;collectionId&quot;:&quot;_pb_users_auth_&quot;,<br>  &quot;collectionName&quot;:&quot;users&quot;,<br>  &quot;expand&quot;:{},<br>  &quot;avatar&quot;:&quot;&quot;,<br>  &quot;emailVisibility&quot;:false,<br>  &quot;name&quot;:&quot;Bob Smith&quot;,<br>  &quot;username&quot;:&quot;Bob&quot;,<br>  &quot;verified&quot;:false<br>}</pre><p>Back in the browser dashboard, press the <strong>Refresh</strong> button and you’ll see that a new user record has been added:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q6NAr4TpwEGKS0ieaexjIw.png" /></figure><p>Signing up a new user doesn’t sign them in yet. That’s another step.</p><p><strong><em>Note</em></strong><em>: I won’t cover it in this tutorial, but before you allow a new user to sign in, you’ll probably want to verify their email. Think about what might happen if you don’t. Some malicious hacker could write a script to register thousands of fake users and then use those accounts to spam your site. PocketBase supports sending email verifications. </em><a href="https://www.youtube.com/watch?v=pw58zxqxHOk"><em>Here</em></a><em> is a video about it. You’ll either want to set up a quality SMTP server or use a third-party email service. Otherwise, your emails will probably be flagged as spam.</em></p><h3>Signing in</h3><p>Next add the code that corresponds with the <strong>Sign in</strong> button in your app:</p><pre>Future&lt;void&gt; signIn() async {<br>  final authData = await pb<br>      .collection(&#39;users&#39;)<br>      .authWithPassword(&#39;bob@example.com&#39;, &#39;12345678&#39;);<br>  print(authData);<br>}</pre><p>authWithPassword sends the email and password for PocketBase to check.</p><p>Refresh your app and press the <strong>Sign in</strong> button. You should see a result similar to the following:</p><pre>{<br>  &quot;token&quot;:&quot;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3MDc0NDk1OTIsImlkIjoiNzl5dmsycjBseG50Nm9iIiwidHlwZSI6ImF1dGhSZWNvcmQifQ.uee--OI5bagsyVjLHq0kqbrbKQfyQ9UxpVV9g6eR8vc&quot;,<br>  &quot;record&quot;:{<br>    &quot;id&quot;:&quot;79yvk2r0lxnt6ob&quot;,<br>    &quot;created&quot;:&quot;2024-01-26 03:02:11.967Z&quot;,<br>    &quot;updated&quot;:&quot;2024-01-26 03:02:11.967Z&quot;,<br>    &quot;collectionId&quot;:&quot;_pb_users_auth_&quot;,<br>    &quot;collectionName&quot;:&quot;users&quot;,<br>    &quot;expand&quot;:{},<br>    &quot;avatar&quot;:&quot;&quot;,<br>    &quot;email&quot;:&quot;bob@example.com&quot;,<br>    &quot;emailVisibility&quot;:false,<br>    &quot;name&quot;:&quot;Bob Smith&quot;,<br>    &quot;username&quot;:&quot;Bob&quot;,<br>    &quot;verified&quot;:false<br>  },<br>  &quot;meta&quot;:{}<br>}</pre><p>That token is a JWT, which if you were to <a href="https://jwt.io/">decode</a>, would contain the following information in its payload:</p><pre>{<br>  &quot;collectionId&quot;: &quot;_pb_users_auth_&quot;,<br>  &quot;exp&quot;: 1707449592,<br>  &quot;id&quot;: &quot;79yvk2r0lxnt6ob&quot;,<br>  &quot;type&quot;: &quot;authRecord&quot;<br>}</pre><p>The exp field is when the token expires. The default duration is two weeks. You can change the default duration in the PocketBase dashboard under <strong>Settings &gt; Token Options</strong>.</p><p><strong><em>Note</em></strong><em>: PocketBase doesn’t use refresh tokens. It only uses long-lived access tokens. The reason is that PocketBase is a monolith. The data and the auth all happen on the same server. PocketBase also doesn’t store the token on the server. It just verifies the access tokens that it has previously issued. You can read more </em><a href="https://github.com/pocketbase/pocketbase/discussions/2154#discussioncomment-5426111"><em>here</em></a><em>.</em></p><p>After you’ve signed in, you can use the authStore property. This will give you access to token and methods like isValid.</p><p>It’s time to update the UI now that the user has signed in. Feel free to use setState or your own preferred state management method. With the <a href="https://suragch.medium.com/flutter-state-management-for-minimalists-4c71a2f2f0c1?sk=6f9cedfb550ca9cc7f88317e2e7055a0">minimalist approach</a>, I use Flutter’s built-in ValueNotifier and ValueListenableBuilder.</p><pre>final statusNotifier = ValueNotifier(&#39;Logged out&#39;);</pre><p>Then I update the notifier at the end of the signIn method:</p><pre>statusNotifier.value = (pb.authStore.isValid) ? &#39;Logged in&#39; : &#39;Logged out&#39;;</pre><p>pb.authStore.isValid returns true now that you’ve logged in.</p><p>Restart the app and press the <strong>Sign in</strong> button. Your status text should indicate that you’ve logged in:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/289/1*fOg0QZSoXioGNXMqDCJ4Vg.gif" /></figure><p><strong><em>Note</em></strong><em>: Rather than manually updating the notifier, another option would be to listen to the </em><em>authStore.onChange stream and pair that with a </em><em>StreamBuilder to update the UI. In a larger app, you could combine this with </em><a href="https://pub.dev/packages/go_router"><em>go_router</em></a><em> to automatically redirect to the login screen.</em></p><p>Sign-in is great, but you don’t want to make your users enter their email and password every time they open your app. What you should do is automatically refresh the access token. You’ll learn how to do that in the next step.</p><h3>Refreshing the token</h3><p>When you sign in, PocketBase gives you an access token. This token is valid for two weeks by default. It’s your responsibility to manage the token on the Flutter side. If you lose the token or it expires, then you’ll have to ask the user to sign in again.</p><p>Add a method that will run when the <strong>Refresh token</strong> button is pressed:</p><pre>Future&lt;void&gt; refresh() async {<br>  if (!pb.authStore.isValid) {<br>    // TODO: The token has expired. Ask the user to sign in again.<br>    return;<br>  }<br>  final authData = await pb.collection(&#39;users&#39;).authRefresh();<br>  print(authData);<br>}</pre><p>authRefresh requests a new token with an updated expiration date from PocketBase.</p><p>Hot reload your app so that you are still logged in, and then press the <strong>Refresh token</strong> button. You should get a new JWT token back. If you <a href="https://jwt.io/">decode</a> it, you’ll see that the exp expiration timestamp has also been updated.</p><h3>Persisting the token (optional)</h3><p>The default AuthStore only keeps the access token in memory, so when you close the app the access token is lost. If you want the user to stay logged in the next time they open your app, you need to provide PocketBase with a custom AuthStore. You can do that with PocketBase’s AsyncAuthStore class.</p><p>I’m not going to go into the implementation details of how to store the token string in local storage, but the following code shows how to handle things on the PocketBase side.</p><p>Replace your pb instance in your state management class with the following:</p><pre>late final PocketBase pb;<br><br>// 1<br>Future&lt;void&gt; init() async {<br>  // 2<br>  final storage = getIt&lt;LocalStorage&gt;();<br>  // 3<br>  final token = await storage.getToken();<br>  final customAuthStore = AsyncAuthStore(<br>    initial: token,<br>    save: storage.setToken,<br>    clear: storage.deleteToken,<br>  );<br>  // 4<br>  pb = PocketBase(<br>    &#39;http://$_host:8090/&#39;,<br>    authStore: customAuthStore,<br>  );<br>  <br>  if (pb.authStore.isValid) {<br>    statusNotifier.value = &#39;Logged in&#39;;<br>    // 5<br>    final authRecord = await pb.collection(&#39;users&#39;).authRefresh();<br>    print(authRecord);<br>  } else {<br>    statusNotifier.value = &#39;Logged out&#39;;<br>  }<br>}<br><br>String get _host =&gt; (Platform.isAndroid) ? &#39;10.0.2.2&#39; : &#39;127.0.0.1&#39;;</pre><p>Here are a few notes:</p><ol><li>You should call the init method when the app starts. In the demo project, I called it in the initState method of the home page.</li><li>In the demo project, I used get_it to provide a reference to my LocalStorage service. Feel free to use provider or some form of dependency injection.</li><li>You can implement LocalStorage any way you like as long as it has a way to save, read and delete the token string. Check out <a href="https://pub.dev/packages/flutter_secure_storage">flutter_secure_storage</a> as one option for encrypted storage. In the demo project for this tutorial, I used shared_preferences only for its simplicity. A production app shouldn’t use shared_preferences because it isn’t secure. If a hacker were to get the token, they could do anything the user can do.</li><li>Provide your custom auth store to PocketBase when you initialize it.</li><li>You can also refresh the auth token when the app first starts up. As you saw earlier, this will update the expiration date, minimizing the chance that the user will need to sign in again. Your custom auth store automatically takes care of saving the token.</li></ol><p>Close the app and restart it. You should still be logged in.</p><p>Read <a href="https://github.com/pocketbase/dart-sdk#authstore">this</a> and <a href="https://github.com/pocketbase/pocketbase/discussions/1887#discussioncomment-5057297">this</a> for a few more pointers on implementing a custom AuthStore.</p><h3>Signing out</h3><p>The last authentication-related step this tutorial will cover is signing out. It’s just as easy as the other steps have been.</p><p>PocketBase doesn’t store a user’s session data or even their access token on the server. So, in order to log a user out, all you need to do is clear the auth data on the client side.</p><p>Add a method that will run when the user presses the <strong>Sign out</strong> button:</p><pre>Future&lt;void&gt; signOut() async {<br>  pb.authStore.clear();<br>  statusNotifier.value = &#39;Logged out&#39;;<br>}</pre><p>That was easy, huh?</p><p>Refresh the app and test it out:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/289/1*SOyHwxqlyuIte_MW9JfL8Q.gif" /></figure><p>There are other auth tasks that you may want to do in the future, such as resetting a forgotten password or requesting an email change. You can check out the PocketBase <a href="https://pocketbase.io/docs/api-records/">documentation</a> for details. In fact, if you choose <strong>API Preview</strong> from the admin dashboard, it will give you code samples of how to do everything.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gk8bSAIH7Ro9_mxtmM_pbw.png" /></figure><p>In the next section, you’ll add a regular collection so that you can perform CRUD operations on it from your Flutter client.</p><h3>Creating a new collection</h3><p>In the admin dashboard, click the <strong>New collection</strong> button:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lWfyYGbnCev4ZPopw-ojeQ.png" /></figure><h4>Adding fields</h4><p>A new window will pop up. Follow these directions to match the image below:</p><ul><li>You’ll make a collection to save user exam scores, so write <strong>scores</strong> for the collection name.</li><li>There are three different types of collections in PocketBase. Since we want users to be able to edit the contents of this collection, choose <strong>Base</strong> from the drop-down menu in the top right. (This is the default.)</li><li>On the <strong>Fields</strong> tab, click <strong>New field</strong> to add two new fields.</li><li>Choose <strong>Relation</strong> and name it <strong>user</strong>. Under<strong> Select collection</strong>, choose the <strong>users</strong> collection. This will link the user field to the users collection. (It’s like adding a foreign key to a table in SQL.)</li><li>Click <strong>New field</strong> again and this time add a <strong>Number</strong> field. Call it <strong>score</strong>.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8Na-E6w9CMpqa_Pip7_8bw.png" /></figure><h4>Adding API rules</h4><p>For security reasons, only admin users can read and write to the collection by default. However, you want to allow the users of your Flutter app to also edit their own data. PocketBase handles this with API Rules.</p><p>Click the <strong>API Rules</strong> tab:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UwShHU0iMdTH4YTK4ILWzw.png" /></figure><p>There are five different categories.<strong> List/Search</strong> rules are for requesting a list of records, while <strong>View</strong> rules are for requesting a single record. You only want a user to be able to see their own exam scores, not other people’s scores. So for both of these categories, add the following rule:</p><pre>user = @request.auth.id</pre><p>This means that the user field of your scores collection must match the authenticated user ID in the request. This effectively filters out all other users.</p><p>The same is true for<strong> Create</strong> and <strong>Update</strong>. However, you also want to enforce the exam score to be within the range of 0–100. Add the following rule to those two categories:</p><pre>user = @request.auth.id &amp;&amp; <br>@request.data.score &gt;= 0 &amp;&amp; <br>@request.data.score &lt;= 100</pre><p>This ensures that the score field of the incoming request is within the proper bounds.</p><p>Finally, users should only be allowed to delete their own scores, so add the following to the <strong>Delete</strong> rules:</p><pre>user = @request.auth.id</pre><p>When you’re done, save your changes by clicking the <strong>Create </strong>button<strong> </strong>in the bottom right corner of the admin dashboard.</p><h3>Creating a record from Flutter</h3><p>Back in your Flutter app, add a method that will be called when a user presses the <strong>Create</strong> button on your Flutter app:</p><pre>Future&lt;void&gt; create() async {<br>  final body = &lt;String, dynamic&gt;{<br>    &quot;user&quot;: pb.authStore.model.id,<br>    &quot;score&quot;: 89,<br>  };<br><br>  final record = await pb.collection(&#39;scores&#39;).create(body: body);<br>  print(record);<br>}</pre><p>You’re again using the create method just like when you added a new user. This time, though, you’re creating a record in the scores collection. Notice that the keys (user and score) of the body map match the field names you chose when you created the collection.</p><p>Refresh your app and press the <strong>Create</strong> button. You should see the following result for record:</p><pre>{<br>  &quot;id&quot;:&quot;k4oaijxf47jroc0&quot;,<br>  &quot;created&quot;:&quot;2024-01-26 09:39:27.934Z&quot;,<br>  &quot;updated&quot;:&quot;2024-01-26 09:39:27.934Z&quot;,<br>  &quot;collectionId&quot;:&quot;0q5mu8dg1ohw7iz&quot;,<br>  &quot;collectionName&quot;:&quot;scores&quot;,<br>  &quot;expand&quot;:{},<br>  &quot;score&quot;:89,<br>  &quot;user&quot;:&quot;79yvk2r0lxnt6ob&quot;<br>}</pre><p>Great, it’s working.</p><p>You need some more user data for the future steps. Let’s create that now. Change the contents of the signUp and signIn methods to add a new user named Mary.</p><pre>Future&lt;void&gt; signUp() async {<br>  final body = &lt;String, dynamic&gt;{<br>    &quot;username&quot;: &quot;Mary&quot;,             // updated<br>    &quot;email&quot;: &quot;mary@example.com&quot;,    // updated<br>    &quot;password&quot;: &quot;12345678&quot;,<br>    &quot;passwordConfirm&quot;: &quot;12345678&quot;,<br>    &quot;name&quot;: &quot;Mary Smith&quot;            // updated<br>  };<br><br>  final record = await pb.collection(&#39;users&#39;).create(body: body);<br>  print(record);<br>}<br><br>Future&lt;void&gt; signIn() async {<br>  final authData = await pb<br>      .collection(&#39;users&#39;)<br>      .authWithPassword(&#39;mary@example.com&#39;, &#39;12345678&#39;); // updated<br>  print(authData);<br><br>  statusNotifier.value = (pb.authStore.isValid) ? &#39;Logged in&#39; : &#39;Logged out&#39;;<br>}</pre><p>Now refresh the app and press <strong>Sign up</strong>, <strong>Sign in</strong>, and <strong>Create</strong> in that order.</p><p>Next, change the score in the create method to 92. Then refresh the app and press the <strong>Create</strong> button again.</p><p>Now you should have three records in the scores table: one from Bob and two from Mary. Go to the admin dashboard and press the <strong>Refresh</strong> button to check it out:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iXefz6YBi7UbFoEC_PNB9g.png" /></figure><p>Nice! It worked. You’ve created three new records.</p><h3>Reading a list of records</h3><p>Next you’ll try to get a list of all the records for one user. You’re currently logged in as Mary so you would expect to get both of Mary’s exam scores. And if you set up the rules correctly, you shouldn’t get Bob’s score.</p><p>Add a method that will be run when the user presses the <strong>Read</strong> button:</p><pre>Future&lt;void&gt; read() async {<br>  final records = await pb.collection(&#39;scores&#39;).getFullList(<br>        sort: &#39;-score&#39;,<br>      );<br>  print(records);<br>}</pre><p>Here are a few notes:</p><ul><li>getFullList returns all of the records in the collection (filtered by the API rules you defined). If that would be too many records, you can paginate the results using getList.</li><li>sort defines which field you want to sort by, in this case by score. The - dash in front of the field name means that you want to sort in reverse order, in this case from high to low score.</li><li>There are a number of additional parameters you can include in addition to sort. Others are batch, filter, and fields.</li></ul><p>Refresh your app and press the <strong>Read</strong> button. You should see Mary’s two exam scores:</p><pre>[<br>  {<br>    &quot;id&quot;:&quot;m1fkfqdv8r20oif&quot;,<br>    &quot;created&quot;:&quot;2024-01-26 09:51:04.343Z&quot;,<br>    &quot;updated&quot;:&quot;2024-01-26 09:51:04.343Z&quot;,<br>    &quot;collectionId&quot;:&quot;0q5mu8dg1ohw7iz&quot;,<br>    &quot;collectionName&quot;:&quot;scores&quot;,<br>    &quot;expand&quot;:{},<br>    &quot;score&quot;:92,<br>    &quot;user&quot;:&quot;eokvshdupfla4v4&quot;<br>  },<br>  {<br>    &quot;id&quot;:&quot;3fkk1f4sgwqgzje&quot;,<br>    &quot;created&quot;:&quot;2024-01-26 09:49:07.460Z&quot;,<br>    &quot;updated&quot;:&quot;2024-01-26 09:49:07.460Z&quot;,<br>    &quot;collectionId&quot;:&quot;0q5mu8dg1ohw7iz&quot;,<br>    &quot;collectionName&quot;:&quot;scores&quot;,<br>    &quot;expand&quot;:{},<br>    &quot;score&quot;:89,<br>    &quot;user&quot;:&quot;eokvshdupfla4v4&quot;<br>  }<br>]</pre><p>It’s good to see that Bob (user ID 79yvk2r0lxnt6ob) isn’t there. Your API rule worked. Also, Mary’s scores are sorted from highest to lowest.</p><p>There’s a lot more data there than you really need, though. All you want are the score IDs and the scores themselves. You can tell PocketBase which fields you want using the fields parameter of getFullList. Add the following line after sort: ‘-score’,:</p><pre>fields: &#39;id,score&#39;,</pre><p>When combining multiple field names, you split them with a comma.</p><p>Press the <strong>Read</strong> button again, and this time, this is what you see:</p><pre>[<br>  {<br>    &quot;id&quot;:&quot;m1fkfqdv8r20oif&quot;,<br>    &quot;created&quot;:&quot;&quot;,<br>    &quot;updated&quot;:&quot;&quot;,<br>    &quot;collectionId&quot;:&quot;&quot;,<br>    &quot;collectionName&quot;:&quot;&quot;,<br>    &quot;expand&quot;:{},<br>    &quot;score&quot;:92<br>  },<br>  {<br>    &quot;id&quot;:&quot;3fkk1f4sgwqgzje&quot;,<br>    &quot;created&quot;:&quot;&quot;,<br>    &quot;updated&quot;:&quot;&quot;,<br>    &quot;collectionId&quot;:&quot;&quot;,<br>    &quot;collectionName&quot;:&quot;&quot;,<br>    &quot;expand&quot;:{},<br>    &quot;score&quot;:89<br>  }<br>]</pre><p>I don’t know that PocketBase needed to give you all the empty fields, but at least you saved it the work of passing in useless values.</p><h3>Updating a record</h3><p>Mary isn’t satisfied with her score of 89. She wants a higher grade. In this step, you’ll replace the lowest score with 100.</p><p>Create a method that will be called when the <strong>Update</strong> button is pushed:</p><pre>Future&lt;void&gt; update() async {<br>  // Find the record with the lowest score<br>  final recordList = await pb.collection(&#39;scores&#39;).getList(<br>        page: 1,<br>        perPage: 1,<br>        skipTotal: true,<br>        sort: &#39;score&#39;,<br>        fields: &#39;id,score&#39;,<br>      );<br>  print(recordList);<br>  final record = recordList.items.first;<br><br>  // Update the record<br>  final body = &lt;String, dynamic&gt;{&quot;score&quot;: 100};<br>  final updatedRecord = await pb.collection(&#39;scores&#39;).update(<br>        record.id,<br>        body: body,<br>      );<br>  print(updatedRecord);<br>}</pre><p>Here are some notes:</p><ul><li>This time you use getList rather than getFullList because you only need one record.</li><li>For the same reason, you also only select 1 page and 1 record perPage. You don’t care about the total number of pages or records, so you can skipTotal. All of these are performance optimizations.</li><li>Once you have the record, you can update the score field using the record ID.</li></ul><p>Refresh the app and press the <strong>Update</strong> button. Here’s what you’ll see:</p><pre>{<br>  &quot;page&quot;:1,<br>  &quot;perPage&quot;:1,<br>  &quot;totalItems&quot;:-1,<br>  &quot;totalPages&quot;:-1,<br>  &quot;items&quot;:[<br>    {<br>      &quot;id&quot;:&quot;3fkk1f4sgwqgzje&quot;,<br>      &quot;created&quot;:&quot;&quot;,<br>      &quot;updated&quot;:&quot;&quot;,<br>      &quot;collectionId&quot;:&quot;&quot;,<br>      &quot;collectionName&quot;:&quot;&quot;,<br>      &quot;expand&quot;:{},<br>      &quot;score&quot;:89<br>    }<br>  ]<br>}<br><br>{<br>  &quot;id&quot;:&quot;3fkk1f4sgwqgzje&quot;,<br>  &quot;created&quot;:&quot;2024-01-26 09:49:07.460Z&quot;,<br>  &quot;updated&quot;:&quot;2024-01-27 04:10:09.986Z&quot;,<br>  &quot;collectionId&quot;:&quot;0q5mu8dg1ohw7iz&quot;,<br>  &quot;collectionName&quot;:&quot;scores&quot;,<br>  &quot;expand&quot;:{},<br>  &quot;score&quot;:100,<br>  &quot;user&quot;:&quot;eokvshdupfla4v4&quot;<br>}</pre><p>The score of 89 was updated to 100.</p><p>You can also see the same result by refreshing the scores collection in the dashboard:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BWmezDUVngMuYoFKYqa9OQ.png" /></figure><p>Hmm, come to think of it, allowing students to update their own exam scores probably isn’t such a great idea. You may want to change the API rules and even introduce user <a href="https://github.com/pocketbase/pocketbase/discussions/94">roles</a> like teacher and student.</p><h3>Deleting a record</h3><p>The last task you’ll implement in this tutorial is how to delete a record. The only thing you need to know is the record ID.</p><p>Add a method that will be called when the user presses the <strong>Delete</strong> button:</p><pre>Future&lt;void&gt; delete() async {<br>  // Find the record with the lowest score<br>  final recordList = await pb.collection(&#39;scores&#39;).getList(<br>        page: 1,<br>        perPage: 1,<br>        skipTotal: true,<br>        sort: &#39;score&#39;,<br>        fields: &#39;id,score&#39;,<br>      );<br>  final record = recordList.items.first;<br><br>  // Delete the record<br>  await pb.collection(&#39;scores&#39;).delete(record.id);<br>}</pre><p>Refresh the app and press the <strong>Delete</strong> button. There is no return value, but you can refresh the scores collection in the dashboard to see that Mary is missing one value.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2sf6-hgT5XQGoQXtqkYxJQ.png" /></figure><p>That brings you to the end of the tutorial. If you successfully followed along this far, you should have a good idea of how PocketBase works.</p><h3>Going on</h3><p>The tutorial didn’t implement any error handling. You can handle that by wrapping the PocketBase calls in try-catch blocks.</p><p>Read the <a href="https://pocketbase.io/docs/going-to-production/">Going to Production</a> section of the documentation for how to deploy your server.</p><h3>Final thoughts</h3><p>I really like PocketBase. It’s by far the easiest way of setting up a self-hosted auth backend that I’ve found. However, there are a few caveats that I discovered along the way.</p><ul><li>Currently, bulk create, update, and delete operations are not supported. That means if a user wants to import a lot of data or update or delete many records at once, the only way to do it is by handling one record at a time. If you need to do that for thousands of records, it could be a deal breaker. There is <a href="https://github.com/pocketbase/pocketbase/issues/48">an issue open on GitHub</a> for this topic, but the solution is apparently non-trivial and currently the status is On Hold in the <a href="https://github.com/orgs/pocketbase/projects/2">1.0 roadmap</a>.</li><li>It’s possible to <a href="https://pocketbase.io/docs/use-as-framework/">extend PocketBase</a> and do things like define additional API routes. However, you have to use Go or JavaScript. Unfortunately, Dart isn’t an option.</li></ul><p>Both of these drawbacks make me wonder, what if I just used PocketBase as the auth server and ran a <a href="https://suragch.medium.com/dart-shelf-server-tutorial-513ea23485a3?sk=96aeb067e6e9b3d96836bafc581cf6ec">Dart Shelf server</a> on the same machine to handle database operations and other logic? It’s not quite as ideal, but I think it could work. To do this, the Flutter client app would first authenticate with PocketBase and get an access token. Then the client would pass the token in the Authorization header to the Dart server. The Dart server would verify the token with PocketBase and if valid would proceed to perform whatever task the user requested. This functionality isn’t designed into PocketBase, but the author acknowledges that it is possible to verify a token by calling authRefresh. Read <a href="https://github.com/pocketbase/pocketbase/discussions/1611">this GitHub discussion</a> for more.</p><p><strong><em>Update</em></strong><em>: I’ve written a tutorial about using PostgreSQL on a Dart server. The only other step would be to authenticate the calls with a PocketBase access token. Read that article here: </em><a href="https://suragch.medium.com/using-postgresql-on-a-dart-server-ad0e40b11947?sk=82a05bd43621e8b2484afc63816036c0"><em>Using PostgreSQL on a Dart server</em></a><em>.</em></p><h3>Full code</h3><p>If you’d like to support me, I’m selling the project source code as a download:</p><ul><li><a href="https://www.buymeacoffee.com/suragchdev/e/212160">Download the project source code</a></li></ul><p>Thank you for your support!</p><p><em>(If you can’t afford to pay for it, though, no worries. Send me an email, and I’ll give it to you for free.)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c962bea4e3f1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dart authentication server with SuperTokens]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://suragch.medium.com/dart-authentication-server-with-supertokens-137540ecb2cc?source=rss-d85b5f6a2d6c------2"><img src="https://cdn-images-1.medium.com/max/1000/1*g_wRUPaidsX2fqeGrmyIuA.png" width="1000"></a></p><p class="medium-feed-snippet">An open-source alternative</p><p class="medium-feed-link"><a href="https://suragch.medium.com/dart-authentication-server-with-supertokens-137540ecb2cc?source=rss-d85b5f6a2d6c------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://suragch.medium.com/dart-authentication-server-with-supertokens-137540ecb2cc?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/137540ecb2cc</guid>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[authentication]]></category>
            <category><![CDATA[servers]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Sat, 13 Jan 2024 08:45:31 GMT</pubDate>
            <atom:updated>2024-01-13T08:45:31.703Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Dart Shelf server tutorial]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://suragch.medium.com/dart-shelf-server-tutorial-513ea23485a3?source=rss-d85b5f6a2d6c------2"><img src="https://cdn-images-1.medium.com/max/2600/1*Mk9ZBCgfw9apk1G1pIA4Ww.jpeg" width="3109"></a></p><p class="medium-feed-snippet">Getting started with your first Shelf web server</p><p class="medium-feed-link"><a href="https://suragch.medium.com/dart-shelf-server-tutorial-513ea23485a3?source=rss-d85b5f6a2d6c------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://suragch.medium.com/dart-shelf-server-tutorial-513ea23485a3?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/513ea23485a3</guid>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[servers]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Fri, 05 Jan 2024 09:23:11 GMT</pubDate>
            <atom:updated>2024-02-02T09:34:55.478Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Text styling and typography in SwiftUI]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://suragch.medium.com/text-styling-and-typography-in-swiftui-cab55365d2c6?source=rss-d85b5f6a2d6c------2"><img src="https://cdn-images-1.medium.com/max/1148/1*hkchA86j8ZU4QqHpO5O5bA.png" width="1148"></a></p><p class="medium-feed-snippet">A visual guide</p><p class="medium-feed-link"><a href="https://suragch.medium.com/text-styling-and-typography-in-swiftui-cab55365d2c6?source=rss-d85b5f6a2d6c------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://suragch.medium.com/text-styling-and-typography-in-swiftui-cab55365d2c6?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/cab55365d2c6</guid>
            <category><![CDATA[typography]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Fri, 29 Dec 2023 07:44:01 GMT</pubDate>
            <atom:updated>2024-01-05T01:54:53.135Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Flutter minimalist state management: Weather app]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://suragch.medium.com/flutter-minimalist-state-management-weather-app-708b01417b9a?source=rss-d85b5f6a2d6c------2"><img src="https://cdn-images-1.medium.com/max/1400/1*eHPBtVxwgZxRtn7Aq_EH8w.png" width="1400"></a></p><p class="medium-feed-snippet">An additional example to clarify the process</p><p class="medium-feed-link"><a href="https://suragch.medium.com/flutter-minimalist-state-management-weather-app-708b01417b9a?source=rss-d85b5f6a2d6c------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://suragch.medium.com/flutter-minimalist-state-management-weather-app-708b01417b9a?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/708b01417b9a</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[state-management]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Sat, 14 Oct 2023 06:51:15 GMT</pubDate>
            <atom:updated>2023-10-16T00:28:15.168Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Flutter minimalist state management: Counter app]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://suragch.medium.com/flutter-minimalist-state-management-counter-app-ab1671a1f877?source=rss-d85b5f6a2d6c------2"><img src="https://cdn-images-1.medium.com/max/2000/1*1I04px1pcNbJ13LN7iimfA.jpeg" width="2000"></a></p><p class="medium-feed-snippet">An additional example to clarify the process</p><p class="medium-feed-link"><a href="https://suragch.medium.com/flutter-minimalist-state-management-counter-app-ab1671a1f877?source=rss-d85b5f6a2d6c------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://suragch.medium.com/flutter-minimalist-state-management-counter-app-ab1671a1f877?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/ab1671a1f877</guid>
            <category><![CDATA[state-management]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Fri, 06 Oct 2023 01:20:54 GMT</pubDate>
            <atom:updated>2023-10-16T00:27:54.107Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Web and desktop support for file upload and download in Flutter]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://suragch.medium.com/web-and-desktop-support-for-file-upload-and-download-in-flutter-ad8e701e2745?source=rss-d85b5f6a2d6c------2"><img src="https://cdn-images-1.medium.com/max/2600/1*vURtYifTL6yIU3RTcn2dMg.jpeg" width="5026"></a></p><p class="medium-feed-snippet">Managing conditional imports and CORS issues</p><p class="medium-feed-link"><a href="https://suragch.medium.com/web-and-desktop-support-for-file-upload-and-download-in-flutter-ad8e701e2745?source=rss-d85b5f6a2d6c------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://suragch.medium.com/web-and-desktop-support-for-file-upload-and-download-in-flutter-ad8e701e2745?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/ad8e701e2745</guid>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[cloudflare]]></category>
            <category><![CDATA[cors]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Wed, 19 Jul 2023 07:29:49 GMT</pubDate>
            <atom:updated>2023-07-20T02:39:43.727Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[How the GetIt service locator package works in Dart]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/flutter-community/how-the-getit-service-locator-package-works-in-dart-fc16a2998c07?source=rss-d85b5f6a2d6c------2"><img src="https://cdn-images-1.medium.com/max/1098/1*okK9YOLlJJ_fndfITDzf3Q.png" width="1098"></a></p><p class="medium-feed-snippet">Unveiling the magic</p><p class="medium-feed-link"><a href="https://medium.com/flutter-community/how-the-getit-service-locator-package-works-in-dart-fc16a2998c07?source=rss-d85b5f6a2d6c------2">Continue reading on Flutter Community »</a></p></div>]]></description>
            <link>https://medium.com/flutter-community/how-the-getit-service-locator-package-works-in-dart-fc16a2998c07?source=rss-d85b5f6a2d6c------2</link>
            <guid isPermaLink="false">https://medium.com/p/fc16a2998c07</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[app-development]]></category>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[Suragch]]></dc:creator>
            <pubDate>Wed, 12 Jul 2023 03:44:40 GMT</pubDate>
            <atom:updated>2023-08-16T16:05:09.569Z</atom:updated>
        </item>
    </channel>
</rss>