<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kven Ho</title>
    <description>The latest articles on DEV Community by Kven Ho (@imkven).</description>
    <link>https://dev.to/imkven</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%2F1397428%2F8cde6e56-b9f2-44d8-9176-c41244d1600d.jpeg</url>
      <title>DEV Community: Kven Ho</title>
      <link>https://dev.to/imkven</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/imkven"/>
    <language>en</language>
    <item>
      <title>Dev Video Review: Firestore Data Structure, Limitations, and IMHO</title>
      <dc:creator>Kven Ho</dc:creator>
      <pubDate>Tue, 31 Dec 2024 08:09:29 +0000</pubDate>
      <link>https://dev.to/imkven/dev-video-review-firestore-data-structure-limitations-and-imho-302c</link>
      <guid>https://dev.to/imkven/dev-video-review-firestore-data-structure-limitations-and-imho-302c</guid>
      <description>&lt;p&gt;It’s been a long time since I last used Firestore (Before Firebase acquired by Google), as my work has mostly focused on server-side development. Traditional databases often make more sense in terms of cost and flexibility. However, while working on a recent React Native project, I had the chance to revisit Firestore and refresh my knowledge. Inspired by an official Firestore video, I decided to share some of the rules outlined in the video along with my opinions on Firestore's data structure. &lt;/p&gt;

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

&lt;h2&gt;
  
  
  Rule #1: Document have limit
&lt;/h2&gt;

&lt;p&gt;Understanding database limitations is crucial when modeling your data structure. Firestore, like all database systems, has certain limits to prevent exhausting the compute and memory resources. Below are some key limitations you should consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Max 1MB limit per document&lt;/strong&gt;&lt;br&gt;
Technically, Firestore allows any data types, but just because you can store an image inside a document doesn’t mean you should. 1MB document should be enough for 99.99% of your use cases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Max 20k fields limit in document&lt;/strong&gt;&lt;br&gt;
When you modeling the data structure, you have to prevent keep adding new fields in the document.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Max depth of sub-collections is 100&lt;/strong&gt;&lt;br&gt;
Avoid excessively nesting sub-collections in your data structure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more  details, please check this link: &lt;a href="https://firebase.google.com/docs/firestore/quotas#collections_documents_and_fields" rel="noopener noreferrer"&gt;https://firebase.google.com/docs/firestore/quotas#collections_documents_and_fields&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #2: You can't retrieve a partial document
&lt;/h2&gt;

&lt;p&gt;Firestore doesn’t allow partial document retrieval. If you need to check a specific field, Firestore will return the entire document, and you’ll need to extract the field yourself.&lt;/p&gt;

&lt;p&gt;Fortunately, Firestore charges based on document reads, not size or bandwidth (Talk more in Rule #4 later). However, took this consideration when modeling your data structure, especially when query come from mobile apps, as it may increase battery and data usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #3: Queries are shallow
&lt;/h2&gt;

&lt;p&gt;When you query a document, Firestore retrieves the document but not its sub-collections. Keep this in mind while modeling your data structure to avoid unexpected behavior.&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%2Fm4yp9wf9636szd2zg69o.jpg" 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%2Fm4yp9wf9636szd2zg69o.jpg" alt="Firestore - Queries are shallow" width="421" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #4: You're billed by the number of reads and write your platform
&lt;/h2&gt;

&lt;p&gt;Firestore is charges based on the number of reads and writes rather than the storage size. Optimize your data structure to minimize reads and writes, but don’t over-complicate your code for just saving a few dollars per month, totally not worth for the engineering cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #5: Queries find documents in a single collection
&lt;/h2&gt;

&lt;p&gt;Firestore queries can only retrieve documents from a single collection. This limitation means no joins (or groups) are possible, so you’ll need to plan your data modeling accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #6: Arrays are weird... maybe not now
&lt;/h2&gt;

&lt;p&gt;Firestore is supports arrays, and while there were limitations in the past. However, improvements have make them more reliable for now. You can now use special array operators for manipulation the array. Learn more here: &lt;a href="https://firebase.blog/posts/2018/08/better-arrays-in-cloud-firestore" rel="noopener noreferrer"&gt;https://firebase.blog/posts/2018/08/better-arrays-in-cloud-firestore&lt;/a&gt;&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>database</category>
      <category>firestore</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>Cloudflare D1 and Prisma: Not a Good Combination (For Now)</title>
      <dc:creator>Kven Ho</dc:creator>
      <pubDate>Sat, 07 Dec 2024 03:45:54 +0000</pubDate>
      <link>https://dev.to/imkven/cloudflare-d1-and-prisma-not-a-good-combination-for-now-4511</link>
      <guid>https://dev.to/imkven/cloudflare-d1-and-prisma-not-a-good-combination-for-now-4511</guid>
      <description>&lt;p&gt;Recently, I started migrating my monolith project to a Cloudflare Worker. At the same time, I decided to give Cloudflare D1 a try. Since my project already uses Prisma to handle a PostgreSQL database, I thought the migration would be almost painless to D1. (How naive I was in the beginning!)&lt;/p&gt;

&lt;h2&gt;
  
  
  First Issue: No Support for ENUM Data Types
&lt;/h2&gt;

&lt;p&gt;The first roadblock I encountered was that Cloudflare D1, as a SQLite-like database, doesn’t support the ENUM data type. I uses ENUM almost in every table in my project, and Prisma makes it incredibly straightforward to implement and manage ENUM datatype.&lt;/p&gt;

&lt;p&gt;To work around this limitation, I had to create separate tables to store the ENUM data manually. Technically, PostgreSQL also creates a sort of "table" behind the scenes when you define an ENUM datatype, but here, I had to do it myself. It wasn’t ideal, but I managed to solve this issue.&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%2Fm3vlj3e4vkklnnocphgx.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%2Fm3vlj3e4vkklnnocphgx.png" alt="Create another table for Enum in SQLite" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, Prisma told me, Cloudflare D1 does not support interactive transactions. Prisma have two type of methods to handle database transaction, interactive transactions and sequential operations. I use interactive transactions in every part of my code because it give me control on the flow. I getting this error when I migrated my code to worker and want to create a new user, then get the user ID to create user profile. This is very typical data design and storing. At this point, I still thinking maybe I can generate the UUID as the user ID, before create user and user profile, problem solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Second Issue: No Interactive Transactions
&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%2Fj2yz4jpbgmdvrpz88wey.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%2Fj2yz4jpbgmdvrpz88wey.png" alt="Cloudflare D1 does not support interactive transactions" width="800" height="28"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I found out that Cloudflare D1 doesn’t support interactive transactions. Prisma offers two ways to handle transactions: interactive transactions and sequential operations. In my code, I use interactive transactions extensively because they give me control over the flow of operations.&lt;/p&gt;

&lt;p&gt;I found this issue, when I migrated my code to the worker and tried to create a new user, I needed the user ID immediately to create a corresponding user profile. This is a common and straightforward use case for database design. However, without interactive transactions, we don't know the user ID after the user creation, and can't create the user profile without user ID.&lt;/p&gt;

&lt;p&gt;Although I started to feel annoyed at that moment, I still managed to work around this issue by creating the user ID beforehand. That seemed like a reasonable fix, and it worked to some in extent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Boss: No ACID Compliance
&lt;/h2&gt;

&lt;p&gt;No ACID compliance is a no-go for my use case. While testing further, I encountered this warning from Prisma:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;prisma:warn Cloudflare D1 does not support transactions yet. When using Prisma's D1 adapter, implicit &amp;amp; explicit transactions will be ignored and run as individual queries, which breaks the guarantees of the ACID properties of transactions. For more details see &lt;a href="https://pris.ly/d/d1-transactions" rel="noopener noreferrer"&gt;https://pris.ly/d/d1-transactions&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Transactions are crucial for ensuring data integrity, especially in scenarios where multiple operations need to be treated as a single transaction.&lt;/p&gt;

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

&lt;p&gt;For now, I've decided to stick with my existing PostgreSQL setup and save myself the headache. Today I learned that if something is working, it's better not to change it.&lt;/p&gt;

</description>
      <category>prisma</category>
      <category>cloudflareworker</category>
      <category>sqlite</category>
      <category>cloudflared1</category>
    </item>
    <item>
      <title>4 Essential Tips for RESTful API Design</title>
      <dc:creator>Kven Ho</dc:creator>
      <pubDate>Thu, 10 Oct 2024 05:15:39 +0000</pubDate>
      <link>https://dev.to/imkven/4-essential-tips-for-restful-api-design-1fa9</link>
      <guid>https://dev.to/imkven/4-essential-tips-for-restful-api-design-1fa9</guid>
      <description>&lt;p&gt;Designing a clean and efficient RESTful API is key to ensuring that developers can interact with your service smoothly. A well-structured API not only saves time but also makes it easier to scale and maintain. Here are four tips to help you enhance your RESTful API design.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Avoid Actions in the URI
&lt;/h2&gt;

&lt;p&gt;Instead of using action words in the URI, rely on HTTP methods like GET, POST, PUT, and DELETE to represent the action.&lt;/p&gt;

&lt;p&gt;Why? This promotes consistency across different endpoints and allows developers to make educated assumptions about the API's behavior. Example: &lt;code&gt;GET /users/{user_id}&lt;/code&gt; for fetching data, where GET is used for reading data only, and no modification occurs.&lt;/p&gt;

&lt;p&gt;By following this rule, your API becomes more predictable and easier to work with, which speeds up the development process.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Limit to Two Resources in the URI
&lt;/h2&gt;

&lt;p&gt;While it's perfectly possible to access a resource nested within another, try to avoid over-complicating your URIs by including too many resources in a single URI. Overly long URIs with multiple nested resources can make the API harder to maintain and evolve.&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="c"&gt;# Good Practice: Fetch tasks assigned to a user.&lt;/span&gt;
GET /users/&lt;span class="o"&gt;{&lt;/span&gt;user_id&lt;span class="o"&gt;}&lt;/span&gt;/tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Avoid:&lt;/span&gt;
GET /users/&lt;span class="o"&gt;{&lt;/span&gt;user_id&lt;span class="o"&gt;}&lt;/span&gt;/tasks/&lt;span class="o"&gt;{&lt;/span&gt;task_id&lt;span class="o"&gt;}&lt;/span&gt;/comments 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the URI harder to maintain and change. Instead of adding more complexity, split the API:&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="c"&gt;# Better Solution:&lt;/span&gt;
GET /users/&lt;span class="o"&gt;{&lt;/span&gt;user_id&lt;span class="o"&gt;}&lt;/span&gt;/tasks
GET /tasks/&lt;span class="o"&gt;{&lt;/span&gt;task_id&lt;span class="o"&gt;}&lt;/span&gt;/comments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps your API simple and easier to manage in the long run.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use Query Parameters for Additional Options
&lt;/h2&gt;

&lt;p&gt;For metadata like pagination, sorting, or limiting results, use query parameters to maintain clean URIs.&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="c"&gt;# Example: &lt;/span&gt;
GET /users/&lt;span class="o"&gt;{&lt;/span&gt;user_id&lt;span class="o"&gt;}&lt;/span&gt;/tasks?limit&lt;span class="o"&gt;=&lt;/span&gt;5&amp;amp;sort&lt;span class="o"&gt;=&lt;/span&gt;created:DESC&amp;amp;page&lt;span class="o"&gt;=&lt;/span&gt;2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach keeps the URI clean while still allowing flexible data retrieval options.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Always Wrap the Data in a Second-Level Object
&lt;/h2&gt;

&lt;p&gt;When returning data, wrap it in an outer object, especially if you plan to include additional metadata in the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Avoid:
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"id"&lt;/span&gt;: 1, &lt;span class="s2"&gt;"task"&lt;/span&gt;: &lt;span class="s2"&gt;"Task 1"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"id"&lt;/span&gt;: 2, &lt;span class="s2"&gt;"task"&lt;/span&gt;: &lt;span class="s2"&gt;"Task 2"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&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 shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Good Practice:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"data"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"id"&lt;/span&gt;: 1, &lt;span class="s2"&gt;"task"&lt;/span&gt;: &lt;span class="s2"&gt;"Task 1"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"id"&lt;/span&gt;: 2, &lt;span class="s2"&gt;"task"&lt;/span&gt;: &lt;span class="s2"&gt;"Task 2"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;,
  &lt;span class="s2"&gt;"pagination"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"page"&lt;/span&gt;: 1, &lt;span class="s2"&gt;"limit"&lt;/span&gt;: 5 &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"sort"&lt;/span&gt;: &lt;span class="s2"&gt;"created:DESC"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure not only makes the response more organized but also allows you to include useful information such as pagination details, sort options, and more.&lt;/p&gt;

&lt;p&gt;By following these simple yet effective tips, you can design a RESTful API that is easier to work with, scalable, and maintainable. Happy building!&lt;/p&gt;

</description>
      <category>restapi</category>
      <category>beginners</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Set Up CrewAI on macOS: A Step-by-Step Guide</title>
      <dc:creator>Kven Ho</dc:creator>
      <pubDate>Mon, 07 Oct 2024 02:45:42 +0000</pubDate>
      <link>https://dev.to/imkven/how-to-set-up-crewai-on-macos-a-step-by-step-guide-48d8</link>
      <guid>https://dev.to/imkven/how-to-set-up-crewai-on-macos-a-step-by-step-guide-48d8</guid>
      <description>&lt;p&gt;Setting up CrewAI on macOS requires careful configuration of Python, as the tool depends on specific versions. This guide will walk you through the process using &lt;code&gt;pyenv&lt;/code&gt;, a powerful tool that allows you to manage multiple versions of Python on your system. Let’s dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install and Configure pyenv
&lt;/h2&gt;

&lt;p&gt;To begin, you’ll need to install &lt;code&gt;pyenv&lt;/code&gt;, which simplifies managing different Python versions. However, it’s crucial to use a specific command to avoid missing modules later on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;env &lt;/span&gt;&lt;span class="nv"&gt;PYTHON_CONFIGURE_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"--enable-framework"&lt;/span&gt; pyenv &lt;span class="nb"&gt;install &lt;/span&gt;3.12.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command ensures that the necessary framework support is built into Python, which is essential when running Python-based applications like CrewAI on macOS. CrewAI requires Python versions between 3.10 and 3.13 (during my writing time), so Python 3.12.7 is a safe choice.&lt;/p&gt;

&lt;p&gt;For detailed steps on how to install Python with framework support on macOS, you can refer to the &lt;a href="https://github.com/pyenv/pyenv/wiki#how-to-build-cpython-with-framework-support-on-os-x" rel="noopener noreferrer"&gt;pyenv wiki&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Set up pyenv for Your Shell
&lt;/h2&gt;

&lt;p&gt;Once you’ve installed the correct version of Python, you need to initialize &lt;code&gt;pyenv&lt;/code&gt; so that your shell can use it. Run the following command to get the initialization script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyenv init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, copy the output and paste it into your &lt;code&gt;.zprofile&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt; file, depending on which shell you’re using. This ensures that &lt;code&gt;pyenv&lt;/code&gt; is automatically loaded every time you start a new terminal session. After editing the file, restart your terminal to apply the changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Set Python Version for the Shell
&lt;/h2&gt;

&lt;p&gt;Now that &lt;code&gt;pyenv&lt;/code&gt; is set up, you need to specify which Python version to use for your current shell session. To set Python 3.12.7, use 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;pyenv shell 3.12.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, restart your terminal once more to ensure the change takes effect.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Install CrewAI and Necessary Tools
&lt;/h2&gt;

&lt;p&gt;With the correct version of Python active, you can now install CrewAI and its required tools using &lt;code&gt;pip&lt;/code&gt;. To do this, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'crewai[tools]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the installation is complete, restart the terminal again to ensure everything is correctly set up.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Create Your First CrewAI Project
&lt;/h2&gt;

&lt;p&gt;You’re now ready to create your first CrewAI project. Use the following command to initialize a new project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crewai create crew &amp;lt;project_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will set up a new AI-powered project that you can start building on. By following these steps, you’ll have CrewAI running smoothly on your macOS environment, ready to assist with AI-driven workflows and applications.&lt;/p&gt;

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

&lt;p&gt;Setting up CrewAI on macOS requires a few specific steps, particularly in configuring the right Python environment. By using pyenv, you can easily manage Python versions and ensure compatibility with CrewAI. Remember to follow each step carefully, especially regarding terminal restarts, to avoid any issues along the way. With the right setup, CrewAI will be a powerful addition to your development toolkit!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>crewai</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
