<?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"><channel><title><![CDATA[Kelly J Andrews]]></title><description><![CDATA[Sarcastic old guy.]]></description><link>https://kellyjandrews.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 05 Sep 2026 01:54:29 GMT</lastBuildDate><atom:link href="https://kellyjandrews.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Developer Relations: Building Success]]></title><description><![CDATA[Fellow developers, have you ever wondered why some companies have an entire team dedicated to "developer relations?" This may seem like a new concept, but in reality, developer relations has been around for quite some time.
In this article, we will e...]]></description><link>https://kellyjandrews.dev/developer-relations-building-success</link><guid isPermaLink="true">https://kellyjandrews.dev/developer-relations-building-success</guid><category><![CDATA[developer relations]]></category><category><![CDATA[community]]></category><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Wed, 29 Mar 2023 17:13:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680109522750/549916a4-568f-4cf1-80db-3692a93c282d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fellow developers, have you ever wondered why some companies have an entire team dedicated to "developer relations?" This may seem like a new concept, but in reality, developer relations has been around for quite some time.</p>
<p>In this article, we will explore what developer relations is, the purpose it serves, and why it is vital for the success of both developers and companies.</p>
<h2 id="heading-what-is-developer-relations">What is Developer Relations?</h2>
<p>In simple terms, developer relations (DevRel) is a set of activities and practices aimed at building and maintaining relationships between developers and companies. These activities may include organizing hackathons, creating open-source projects, offering developer-focused content and resources, and more.</p>
<p>The ultimate goal of DevRel is to help developers be successful using a company's products and services, while also providing valuable feedback to the company about how to improve those products and services.</p>
<h2 id="heading-the-history-of-developer-relations">The History of Developer Relations</h2>
<p>While developer relations may seem like a modern concept, it has been around since the early days of computing. One of the earliest examples of DevRel is the IBM Developer Network, which was established in the 1980s to support developers working with IBM products. Since then, many other companies, from startups to large corporations, have established their own DevRel programs.</p>
<h2 id="heading-the-purpose-of-developer-relations">The Purpose of Developer Relations</h2>
<p>The purpose of developer relations is twofold: to help developers be successful and to help companies improve their products and services. Developers are the heart and soul of the tech industry, and their success is essential to the success of any company that creates software or technology.</p>
<p>By providing developers with the resources, support, and community they need, companies can help ensure that their products are used and loved by developers around the world.</p>
<p>At the same time, DevRel also helps companies improve their products and services. By engaging with developers and listening to their feedback, companies can learn how to make their products better and more useful for their customers.</p>
<p>DevRel also helps companies build trust and credibility with developers, which can be critical in an industry where word-of-mouth recommendations and referrals are so important.</p>
<h2 id="heading-why-developer-relations-is-vital-for-success">Why Developer Relations is Vital for Success</h2>
<p>Developer relations is vital for the success of both developers and companies. For developers, DevRel provides access to valuable resources, including documentation, sample code, tutorials, and more. DevRel also helps developers build relationships with other developers, which can lead to collaboration, mentoring, and other opportunities.</p>
<p>For companies, DevRel is essential for building and maintaining relationships with developers, who are often the primary users of their products and services. DevRel also helps companies gain valuable feedback about their products and services, which can help them improve those products and better serve their customers.</p>
<h2 id="heading-keep-going">Keep Going</h2>
<p>Developer Relations is a critical component of the tech industry, with a long history and a bright future. By providing developers with the resources, support, and community they need, companies can help ensure the success of both developers and their own products and services.</p>
<p>As developers, we should be grateful for the efforts of DevRel teams around the world, and as companies, we should continue to invest in these programs to ensure the success of our industry as a whole.</p>
]]></content:encoded></item><item><title><![CDATA[Bridging the Gap]]></title><description><![CDATA[The Power of AI in Fostering Developer Communities
Artificial Intelligence (AI) has been making significant strides in a variety of fields, and one area where it's having a profound impact is in fostering developer communities. As the digital landsca...]]></description><link>https://kellyjandrews.dev/bridging-the-gap</link><guid isPermaLink="true">https://kellyjandrews.dev/bridging-the-gap</guid><category><![CDATA[AI]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[developer community]]></category><category><![CDATA[developer relations]]></category><category><![CDATA[Inclusion]]></category><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Mon, 27 Mar 2023 17:51:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679939408740/b33c415f-cff1-440c-8b6a-c26ac969ddfd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-power-of-ai-in-fostering-developer-communities"><strong>The Power of AI in Fostering Developer Communities</strong></h2>
<p>Artificial Intelligence (AI) has been making significant strides in a variety of fields, and one area where it's having a profound impact is in fostering developer communities. As the digital landscape becomes more complex and competitive, engaging developer communities has become crucial for businesses. AI-driven platforms and tools are playing a pivotal role in strengthening these communities by analyzing developer behavior, providing targeted content, and promoting inclusivity.</p>
<h2 id="heading-personalized-experiences-for-developers"><strong>Personalized Experiences for Developers</strong></h2>
<p>One of the most significant ways AI-driven platforms enhance community engagement is by offering personalized experiences for developers. By analyzing individual preferences, needs, and behavior, these platforms can deliver tailored content and recommendations, creating a more satisfying and relevant experience for developers.</p>
<p>Something like <a target="_blank" href="https://www.coveo.com/">Coveo</a> can create content recommendations based on several algorithms like previously viewed together items - which can make flows for documentation tailored to the reader based on previous data.</p>
<p>This personalization not only makes developers feel valued and understood but also helps them work more efficiently and effectively. It fosters a sense of belonging within the community, ultimately leading to stronger connections and loyalty.</p>
<h2 id="heading-ai-powered-community-analytics"><strong>AI-powered Community Analytics</strong></h2>
<p>Understanding the needs and preferences of a developer community is essential for businesses to engage effectively. AI-driven platforms can analyze vast amounts of data to identify trends, patterns, and insights that can help businesses better understand and serve their developer communities.</p>
<p>Armed with this valuable information, businesses can tailor their content, resources, and support to better meet the needs of their developers. This data-driven approach fosters a deeper level of engagement and loyalty within the community.</p>
<p>While there are several data analytics tools out there, a community-specific one is still yet to emerge that I have found. Let me know if you have one in the comments.</p>
<h2 id="heading-inclusive-and-accessible-communities"><strong>Inclusive and Accessible Communities</strong></h2>
<p>Inclusivity and accessibility are essential elements of a thriving developer community. AI-driven platforms can help create more inclusive spaces by identifying and addressing potential barriers to participation. For instance, AI-powered language translation tools can bridge language barriers, enabling developers from diverse backgrounds to communicate and collaborate seamlessly.</p>
<p>By fostering inclusive and accessible communities, businesses can tap into the collective knowledge and skills of a diverse developer base, driving innovation and growth. Here is <a target="_blank" href="https://www.unite.ai/best-ai-translation-software-tools/">a list of various translation tools</a> you can check out!</p>
<h2 id="heading-enhanced-collaboration-and-communication"><strong>Enhanced Collaboration and Communication</strong></h2>
<p>Strong communication is the backbone of any successful developer community. AI-driven platforms can facilitate more effective communication by providing intelligent suggestions, automating repetitive tasks, and streamlining collaboration.</p>
<p>These platforms enable developers to work together more efficiently, share knowledge, and solve problems collectively. As a result, the community becomes more cohesive and collaborative, driving greater success for both developers and businesses.</p>
<p>If you have any recommendations for your favorites - please add them in the comments. I don't personally have any recommendations here.</p>
<h2 id="heading-embracing-ai-driven-community-engagement"><strong>Embracing AI-driven Community Engagement</strong></h2>
<p>The impact of AI-driven community engagement is undeniable. As businesses continue to integrate AI into their developer relations strategies, they can expect to see stronger, more connected, and more engaged developer communities. By leveraging the power of AI, businesses can create a thriving ecosystem that fosters innovation, collaboration, and success.</p>
<p>How are you currently using AI in your community? I would love to hear from you in the comments.</p>
]]></content:encoded></item><item><title><![CDATA[AI: The New Face of Developer Relations]]></title><description><![CDATA[As an industry veteran, I've witnessed firsthand how the landscape of developer relations has evolved. Today, we stand at the cusp of a new era where artificial intelligence has become the driving force behind this change. In this post, I'll explore ...]]></description><link>https://kellyjandrews.dev/ai-the-new-face-of-developer-relations</link><guid isPermaLink="true">https://kellyjandrews.dev/ai-the-new-face-of-developer-relations</guid><category><![CDATA[AI]]></category><category><![CDATA[developer relations]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[communication]]></category><category><![CDATA[developer community]]></category><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Wed, 22 Mar 2023 23:46:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679528632334/17c8bad4-8d9e-4a67-95a1-00bd859881be.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As an industry veteran, I've witnessed firsthand how the landscape of developer relations has evolved. Today, we stand at the cusp of a new era where artificial intelligence has become the driving force behind this change. In this post, I'll explore how AI-powered platforms and tools are becoming key players in developer relations, streamlining tasks, and enabling better communication between developers and businesses.</p>
<h2 id="heading-a-game-changer">A Game Changer</h2>
<p>As the technology landscape grows more complex and competitive, the need for effective developer relations has never been more critical. With AI, we're witnessing a revolution in how companies can now engage with developers.</p>
<p>By leveraging machine learning and natural language processing, AI-powered tools can automate tasks, personalize developer experiences, and revolutionize how businesses and developers communicate.</p>
<p>These tools save time and effort and help build trust and rapport between businesses and developers. The result? Better collaboration, increased productivity, and a stronger sense of community.</p>
<h2 id="heading-streamlining-tasks">Streamlining Tasks</h2>
<p>One of the most significant impacts of AI on developer relations is the automation of routine tasks. With AI-driven tools, manual processes such as documentation generation, issue tracking, and code review can be streamlined, freeing up time for developer advocates to focus on more strategic efforts.</p>
<p>Similarly, AI-powered chatbots are revolutionizing customer support by providing quick and accurate responses to developers' queries, helping to reduce response times, and ensuring developers receive the support they need.</p>
<h2 id="heading-enhancing-communication-and-collaboration">Enhancing Communication and Collaboration</h2>
<p>AI is also helping to bridge the communication gap between developers and businesses. By analyzing developer behavior, AI platforms can provide personalized recommendations and facilitate more effective communication.</p>
<p>By understanding individual preferences and needs, these tools can foster more inclusive and engaging experiences for developers, ultimately creating a stronger sense of community and belonging.</p>
<p>AI can also analyze data to identify trends and patterns, providing businesses with valuable insights into the needs and preferences of their developer communities. This information can be used to tailor content, resources, and support to serve developers better, fostering a deeper level of engagement and loyalty.</p>
<h2 id="heading-a-seamless-developer-experience">A Seamless Developer Experience</h2>
<p>With the rise of AI, we're also seeing a push toward more personalized developer experiences. By leveraging machine learning algorithms, AI-driven tools can analyze developer behavior to create customized learning paths, recommend resources, and even predict potential issues before they arise.</p>
<p>This level of personalization not only helps developers feel more valued and understood, but also enables them to work more efficiently and effectively.</p>
<h2 id="heading-embracing-ai-in-developer-relations">Embracing AI in Developer Relations</h2>
<p>As AI continues to make its presence known, we can expect to see even more innovative tools and platforms that push the boundaries of what's possible. By embracing these new technologies, businesses can foster stronger relationships with their developer communities, streamline their operations, and ultimately drive greater adoption of their products and services.</p>
<h2 id="heading-the-competitive-edge">The Competitive Edge</h2>
<p>As the competition for developers' attention intensifies, businesses must stay ahead of the curve to remain relevant. By leveraging AI-driven tools, companies can provide developers with exceptional experiences and resources, setting themselves apart from the competition.</p>
<h2 id="heading-preparing-for-the-future">Preparing for the Future</h2>
<p>Businesses need to stay informed and adapt to the changes that AI brings with it. By understanding the benefits and potential challenges of AI-driven tools, companies can make informed decisions and better position themselves for success.</p>
<p>Here are a few strategies to consider when integrating AI into your developer relations program:</p>
<ol>
<li><p><strong>Invest in AI-driven tools and platforms</strong>: Explore the latest AI-powered tools to streamline tasks, improve communication, and personalize developer experiences. Stay informed about emerging technologies and consider incorporating them into your developer relations strategy.</p>
</li>
<li><p><strong>Focus on building trust</strong>: Transparency is vital when it comes to AI. Be open about using AI tools and ensure developers understand the benefits these tools can provide. Address any concerns and be proactive in demonstrating these technologies' value and ethical considerations.</p>
</li>
<li><p><strong>Adapt to developers' changing needs</strong>: As AI changes the landscape of developer relations, developers' needs and expectations will also evolve. Stay attuned to these changes, and be prepared to adjust your developer relations strategy accordingly.</p>
</li>
<li><p><strong>Foster a culture of innovation</strong>: Encourage your developer relations team to stay informed about AI and its potential applications. Provide opportunities for learning and experimentation, and create an environment where innovation can thrive.</p>
</li>
</ol>
<p>By embracing AI and its potential in developer relations, businesses can create stronger bonds with their developer communities, drive increased adoption of their products and services, and remain competitive in an ever-changing landscape.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>AI will undoubtedly become the new face of developer relations, transforming how businesses engage with developers and streamlining tasks that were once time-consuming and labor-intensive.</p>
<p>As AI-driven tools continue to revolutionize this space, businesses must adapt to stay ahead of the curve and provide the best possible experiences for their developer communities. By embracing AI and its opportunities, companies can foster deeper connections with their developers, ultimately driving higher success for all parties involved.</p>
<p>As we move forward, the integration of AI in developer relations will only continue to grow, shaping the future of how businesses and developers collaborate and communicate.</p>
]]></content:encoded></item><item><title><![CDATA[Improving the  Developer Experience]]></title><description><![CDATA[The developer experience shapes a developer's perception of a product. It starts when they begin using a product and directly impacts their opinion and decision to stick with it or recommend it to others. Companies that focus on creating a positive a...]]></description><link>https://kellyjandrews.dev/improving-the-developer-experience</link><guid isPermaLink="true">https://kellyjandrews.dev/improving-the-developer-experience</guid><category><![CDATA[developer experience]]></category><category><![CDATA[developer relations]]></category><category><![CDATA[product]]></category><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Wed, 01 Feb 2023 17:45:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/rE3kbKmLmhE/upload/7bd0c13bef79e5884172695ca430ba35.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The developer experience shapes a developer's perception of a product. It starts when they begin using a product and directly impacts their opinion and decision to stick with it or recommend it to others. Companies that focus on creating a positive and seamless experience for developers will reap the benefits of increased retention, attracting more users, and a strong community.</p>
<p>When I worked at ExactTarget, the Journey Builder application was first released to the public. This opened my eyes to the world of <a target="_blank" href="https://www.hotjar.com/blog/customer-centric-marketing/">customer-centric marketing</a> and how thinking about the users from their perspective can have an incredible impact. It taught me to see things as a journey and has translated into many aspects of my career.</p>
<h2 id="heading-the-developer-journey">The Developer Journey</h2>
<p>The developer journey is a complex and dynamic process comprising several vital stages: <strong><em>onboarding, technical support, and community building</em></strong>. To ensure a positive developer experience, companies must focus on enhancing the onboarding process, providing timely and effective technical support, and fostering a strong community of developers.</p>
<h3 id="heading-onboarding">Onboarding</h3>
<p>Onboarding is the first stage of the developer journey and sets the tone for the rest of the experience. This is where developers learn about a product and get up to speed on how to use it. To ensure a positive onboarding experience, companies must provide <strong><em>clear, concise documentation, resources, and accessible technical support</em></strong>. A positive onboarding experience can build a positive relationship with developers from the start and impact their view of a product.</p>
<h3 id="heading-technical-support">Technical Support</h3>
<p>Technical support is a critical touchpoint in the developer journey. Developers may encounter challenges or need additional help along the way. Having a team available to answer questions and provide guidance can significantly impact the developer experience. Timely and effective technical support that resolves issues quickly and efficiently is <strong><em>essential</em></strong> to ensuring a positive developer experience.</p>
<h3 id="heading-community-building">Community Building</h3>
<p>Community building is an essential element of the developer journey. A vibrant and supportive community can attract new developers to a product and keep existing users engaged and invested. Building a solid community can lead to more effective and efficient problem-solving, increased innovation, and improved developer satisfaction. Companies can foster a sense of community among developers by <strong><em>hosting meetups, creating forums for discussion, and encouraging collaboration and feedback</em></strong>.</p>
<h2 id="heading-why-is-the-developer-experience-important">Why is the Developer Experience Important?</h2>
<p>When developers are the ones who use a product, their experience directly impacts their decision to continue using it or recommend it to others. By enhancing the developer experience, companies can build stronger relationships with this influential audience and increase their likelihood of continuing to use a product.</p>
<p>The developer experience extends beyond just the technical items of a product. It also encompasses how developers are supported and valued and about creating a culture that prioritizes their needs and feedback. A holistic approach to the developer experience can ensure that companies provide the support and resources developers need to succeed.</p>
<h3 id="heading-empathy-and-communication">Empathy and Communication</h3>
<p>Empathy and communication are two critical components of a positive developer experience. Companies can create a more supportive environment that fosters innovation and collaboration by putting themselves in developers' shoes and understanding their needs and challenges. <strong><em>Clear and concise documentation, prompt technical support responses, and active engagement with the developer community</em></strong> are all important facets of good communication.</p>
<h3 id="heading-the-role-of-developer-relations">The Role of Developer Relations</h3>
<p>Developer Relations' core purpose is to foster strong relationships with developers and understand their needs and challenges. By working closely with developers and supporting them through every stage of their journey, companies can create a positive and seamless experience that leaves a lasting impression.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Companies prioritizing the developer experience will benefit from increased retention, a strong community, and improved relationships with this influential audience. Companies can create a supportive environment that fosters innovation and collaboration by taking a holistic approach to the developer experience and focusing on empathy, communication, and community building.</p>
]]></content:encoded></item><item><title><![CDATA[The Good, The Bad, The Ugly]]></title><description><![CDATA[In the tech world, having a good relationship with developers is vital. That's where developer relations (DevRel) come in. DevRel teams ensure developers are happy and promote a company's products and services. But things can go wrong when you put le...]]></description><link>https://kellyjandrews.dev/the-good-the-bad-the-ugly</link><guid isPermaLink="true">https://kellyjandrews.dev/the-good-the-bad-the-ugly</guid><category><![CDATA[developer relations]]></category><category><![CDATA[leadership]]></category><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Thu, 26 Jan 2023 11:20:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/FueRaNunlUc/upload/76f505245be2783cfdeed26dd5ed3a80.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the tech world, having a good relationship with developers is vital. That's where developer relations (DevRel) come in. DevRel teams ensure developers are happy and promote a company's products and services. But things can go wrong when you put leaders without experience in DevRel, in charge of DevRel teams.</p>
<p>In this post, we'll discuss the good, bad, and ugly of inexperienced leadership in DevRel. We'll cover how new leaders can bring fresh ideas to the table, the problems they might run into, and the consequences of bad leadership. The bottom line is inexperienced leadership in DevRel can be a real problem. But if you're aware of the risks and have a plan, you can avoid disaster.</p>
<h2 id="heading-the-good">The Good</h2>
<p>Okay, so there are some upsides to having inexperienced leaders in DevRel. One of the biggest is that they can bring fresh ideas to the table. They may have a different level of industry knowledge than more experienced leaders, but they can still come up with new solutions to problems.</p>
<p>Another plus is that new leaders can help make the DevRel team more diverse. A mix of experienced and inexperienced leaders can bring a balance of perspectives and lead to a more inclusive and innovative environment. And, if the DevRel team is more diverse, they'll likely be able to understand and serve a diverse group of developers.</p>
<p>In addition, new leaders can bring a lot of energy and enthusiasm to the DevRel team. They may be more willing to take risks and try new things, which can benefit the team and the company.</p>
<p>Generally, inexperienced leaders will face challenges but can also bring value to the DevRel team and the company. And remember, everyone has to start somewhere. With the proper training and support, inexperienced leaders can become influential leaders.</p>
<h2 id="heading-the-bad">The Bad</h2>
<p>While inexperienced leadership in DevRel can have some positive benefits, it can lead to several challenges and obstacles. One of the biggest challenges that inexperienced leaders may face is a need for more industry knowledge and experience. This can make it difficult for them to <a target="_blank" href="https://kellyjandrews.dev/navigating-the-world-of-developer-relations">navigate the complex and ever-changing world of developer relations</a>. They may need to be better familiar with industry best practices to understand the community's needs and pain points.</p>
<p>The need for more communication skills is another challenge. Inexperienced leaders may need to communicate more effectively with the developer community or the rest of the team. Having the proper voice with the developer community is essential - being authentic is vital. Lacking the communication skills to be authentic can ultimately result in poor outcomes for the company.</p>
<p>Along with these challenges, inexperienced leaders may need more technical skills to communicate and promote the company's products and services to developers. This can make it difficult for the DevRel team to effectively promote the company's products and services to developers, resulting in poor product adoption and a loss of revenue for the company.</p>
<p>Inexperienced leadership in DevRel can lead to many challenges and obstacles. It's essential to be aware of these potential pitfalls and have a plan to mitigate them. This may include providing additional training, support, and mentorship to inexperienced leaders to help them overcome these challenges and become influential leaders in the future.</p>
<h2 id="heading-the-ugly">The Ugly</h2>
<p>The consequences of inexperienced leadership in DevRel can be severe, leading to some adverse outcomes for the developer community and the company. One of the most significant issues that can arise is a loss of developer trust.</p>
<p>Developers rely on the DevRel team to provide accurate and reliable information about the company's products and services. If the leader is equipped to handle their role, they may give inaccurate or misleading information, which can erode trust and lead to a loss of developers.</p>
<p>Poor product adoption is another potential outcome of inadequate leadership. If the DevRel team is responsible for product evangelism, then developers are an important target market for the company. If the leader can authentically communicate the value of the products and services, it may lead to better adoption and ultimately result in a loss of revenue for the company.</p>
<p>Leadership can also lead to a lack of alignment within the DevRel team and the broader company. If the leader can't effectively communicate the company's goals and objectives, it can lead to a lack of clarity and direction within the team. This can lead to a lack of productivity and ultimately result in a poor outcome for the company.</p>
<p>Moreover, inexperienced leadership can lead to a lack of motivation, burnout, and a high turnover rate among the team members, further deteriorating the team's overall performance.</p>
<p>It is crucial to have a solid leadership team in place to ensure the success of the DevRel program. Poor leadership can disastrously impact the developer community and the company. It's crucial to have a good leadership training and support program in place to ensure that inexperienced leaders are equipped to handle their roles effectively.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>DevRel is an essential aspect of the tech industry, and it's vital to have a good relationship with developers. However, inexperienced leadership in DevRel can lead to several challenges and negative consequences. It's helpful to be mindful of these issues and prepare yourself for them ahead of time.</p>
<p>While inexperienced leaders can bring fresh ideas, diversity, and energy to the DevRel team, they may also need more industry knowledge, communication, and technical skills. This can lead to poor product adoption, lack of alignment within the team, and loss of trust from the developer community.</p>
<p>To ensure the DevRel program's success, a solid leadership team must be in place. With proper experience, training, and support, inexperienced leaders can become influential leaders and avoid disastrous results.</p>
]]></content:encoded></item><item><title><![CDATA[Navigating the World of Developer Relations]]></title><description><![CDATA[Welcome to the world of developer relations! As a developer relations professional, you play a critical role in building and maintaining relationships between a company and its developer community. Whether you're working for a startup or a large ente...]]></description><link>https://kellyjandrews.dev/navigating-the-world-of-developer-relations</link><guid isPermaLink="true">https://kellyjandrews.dev/navigating-the-world-of-developer-relations</guid><category><![CDATA[developer relations]]></category><category><![CDATA[Career]]></category><category><![CDATA[Developer Advocate]]></category><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Thu, 19 Jan 2023 13:26:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/cckf4TsHAuw/upload/7a113014392be5fbe4253fa7baa18c4d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to the world of developer relations! As a developer relations professional, you play a critical role in building and maintaining relationships between a company and its developer community. Whether you're working for a startup or a large enterprise, the importance of developer relations in the tech industry cannot be overstated.</p>
<p>From technical evangelism to community management, developer relations professionals wear many hats. They act as the voice of the developer community inside the company, providing valuable feedback and insights to help shape product development. They also serve as the company's voice in the developer community, sharing updates, resources, and best practices.</p>
<p>So, whether you're just starting in developer relations or a seasoned pro, this post will give you a better understanding of the field and help you navigate the many roles and responsibilities of a developer relations professional. Let's dive in!</p>
<h2 id="heading-skills">Skills</h2>
<p>When it comes to excelling in developer relations, there are a few essential and critical skills. The first is strong communication skills. As a developer relations professional, you will interact with developers from all backgrounds, colleagues, and stakeholders from other teams. Communicating effectively verbally and in writing is crucial to building trust and establishing solid relationships.</p>
<p>Another essential skill is technical acumen. While you don't need to be a software developer yourself (although it does help a bit), having a solid understanding of the technical side of things is critical to understand developers' needs and concerns and effectively advocating for them within the company. This can be achieved through experience working in a technical role or ongoing learning and education.</p>
<p>In addition to these skills, building relationships and advocating for developers are also vital. Developer relations foster a sense of community and connection between a company and its developer base. Building relationships with developers, understanding their needs and concerns, and advocating for them within the company are critical to the success of a developer relations professional.</p>
<p>Finally, empathizing with developers and understanding their perspectives is crucial to building trust and establishing effective relationships. This skill is often developed through experience and continuous learning of the developer ecosystem and its trends.</p>
<h2 id="heading-roles">Roles</h2>
<p>The role of a developer relations professional can vary depending on the company and the specific needs of the developer community. However, a few key responsibilities are shared across the field.</p>
<p>One of the primary roles of a developer relations professional is to act as the voice of the developer community within the company. This means gathering feedback and insights from developers and then sharing that information with other teams, such as product development, marketing, and customer support. This helps ensure that the needs and concerns of the developer community are taken into account when making decisions about products and services.</p>
<p>Another critical responsibility of a developer relations professional is to act as the company's voice within the developer community. This means sharing updates and resources with developers, answering questions, and addressing concerns. This helps build trust and strong relationships with the developer community, which can lead to increased adoption of products and services.</p>
<p>A developer relations professional also often plays a crucial role in community management. This includes organizing and hosting events, such as meetups and hackathons, and creating and maintaining online communities, such as forums and social media groups. Community management helps to foster a sense of community and connection among developers and can also be a great way to gather feedback and insights.</p>
<p>Finally, a developer relations professional also serves as the company's technical evangelist. This means promoting and showcasing the company's products and services to developers and providing technical support and resources. Technical evangelism helps to increase awareness and adoption of the company's products and services among the developer community.</p>
<h2 id="heading-advice">Advice</h2>
<p>Transitioning into a developer relations role can seem daunting, but it can be a rewarding and fulfilling career path with the right mindset and approach. Here are some tips and advice on how to make the transition.</p>
<p>Networking is one of the most important things you can do when transitioning into a developer relations role. Attend conferences and meetups, join online communities and groups, and connect with other developer relations professionals. Not only will you learn from others in the field, but you'll also have the opportunity to make connections that can lead to job opportunities.</p>
<p>Gaining relevant experience is also crucial when making the transition. Participating in open-source projects, working in a technical role, or volunteering at developer events can help to build your skills and knowledge. It also allows you to showcase your experience and skills to potential employers.</p>
<p>Building a personal brand can also be beneficial when making the transition. Creating a portfolio of your work, writing blog posts or articles, and sharing your knowledge and experience on social media help establish you as a thought leader. This can attract potential employers and showcase your skills and experience.</p>
<p>It's also important to stay current with the latest trends and best practices in the field and to continuously learn and grow your skills. Reading industry publications, following influential people in the area, and taking online courses are great ways to stay up-to-date and continue developing your skills.</p>
<p>Finally, don't be afraid to take risks and try new things. Developer relations is a field that is constantly evolving, and those who are willing to adapt and try new things are often the most successful. Keep an open mind, be ready to learn and grow, and don't hesitate to step out of your comfort zone.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, developer relations is a dynamic and critical field in the tech industry. It is vital in building and maintaining relationships between a company and its developer community. It is essential for the success of any organization that creates and distributes technology.</p>
<p>By understanding a developer relations professional's various roles and responsibilities and the critical skills needed to excel in this field, you can transition into a developer relations role and help bridge the gap between companies and the developer community. We hope this post has provided you with valuable insights and resources to help you pursue a career in developer relations and make an impact in the tech industry.</p>
<p>Remember to stay current with the latest trends and best practices in the field, continuously learn and grow your skills, and don't be afraid to take risks and try new things. Keep building connections and relationships, and always be open to new opportunities to improve your developer relations skills.</p>
]]></content:encoded></item><item><title><![CDATA[My Attempt To Answer How I Lost Weight]]></title><description><![CDATA[Over the last several months, I've been able to reconnect with people again after a couple of years. After their initial shock and surprise that they know who I am, they ask me: "how did you do it?"
I honestly don't know how to answer the question be...]]></description><link>https://kellyjandrews.dev/my-attempt-to-answer-how-i-lost-weight</link><guid isPermaLink="true">https://kellyjandrews.dev/my-attempt-to-answer-how-i-lost-weight</guid><category><![CDATA[weightloss]]></category><category><![CDATA[health]]></category><category><![CDATA[personal]]></category><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Thu, 12 Jan 2023 14:28:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673532216170/1fd75a3d-766c-461b-a1a8-8f0227c999e5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Over the last several months, I've been able to reconnect with people again after a couple of years. After their initial shock and surprise that they know who I am, they ask me: "how did you do it?"</p>
<p>I honestly don't know how to answer the question because, even to me, it still is a mystery. This post will collect my thoughts and hopefully provide some answers to those curious.</p>
<h2 id="heading-the-who">The Who</h2>
<p>I remember during my senior year of high school when I realized I had put on so much weight that my clothes no longer fit me, and I needed to buy larger sizes. What I didn't know at that time was that it would be a 25-year battle of self-image, self-loathing, and motivation.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673532682304/66604125-2b8a-4f32-a7d6-3a859484f895.jpeg" alt class="image--center mx-auto" /></p>
<p>Over that time, I've tried just about <em>everything</em> to lose weight: Atkins, South Beach, Juice cleanses, Weight Watchers, Mediterranean, Cabbage, Paleo, Keto, Low Carb, Body For Life, Low Sugar, Calorie counting, Weight lifting, Cardio, Aerobics, Running, etc.</p>
<p>Sometimes I took those efforts seriously, getting down to 280 pounds twice. 280 does not seem like a good number for a 5'11" male who is supposed to weigh under 190 lbs, but when you previously clocked in at 400 lbs or more, it feels incredible. However, I would always gain the weight back.</p>
<p>I learned that diets are not sustainable and surgery doesn't work for everyone. More importantly, I started to understand that programs are designed to make you temporarily lose weight, are unsustainable, or worse for your health than you think.</p>
<p>You would think that being diagnosed with Type 2 Diabetes would have pushed me to get things under control, but I had convinced myself that no matter what I weighed, I was healthy or that even thin people get sick. I became an expert at evading my efforts.</p>
<p>Anything can be okay in moderation, but not when dieting is so far off course that even moderation is excess. I would eat plateful after plateful, never feeling full or satisfied. I would wake up hungry, despite eating 2500 calories for dinner the night before. I was sick but had convinced myself that I was healthy and that my body was naturally heavy and needed the extra weight.</p>
<h2 id="heading-the-why">The Why</h2>
<p>Before I get to how I lost weight, I want to talk about my love of amusement parks and roller coasters. Around 13, our school went on a field trip to Cedar Point. It wasn't my first time at an amusement park, but this time I finally talked myself into riding a coaster with loops, and after that, I was hooked. Every summer, I would get to a park and ride as many roller coasters as I could. The atmosphere, the heat, the lines, all of it, I loved it.</p>
<p>However, most rides don't cater to larger bodies. I recall countless times feeling dejected after waiting in line for a ride I couldn't fit in. I had to watch my kids enjoy riding coasters from the sidelines. Ultimately, this reality sinking in and watching my wife lose weight the year before finally pushed me towards action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673533596664/ac103f6b-f0de-4e43-aa43-36ce3a170f88.jpeg" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-how">The How</h2>
<p>Most people wish this were where I could say, "I did X," and that was it - end of the post. Typically, my answer to those who ask is, "I ate less." This answer never feels good enough or can even be condescending and rude. So let me try and explain what that means exactly.</p>
<h3 id="heading-eat-less">Eat Less</h3>
<p>I used to love eating at Wendy's. I would order a double (mayo and cheese only), large fries, and a large diet drink (saving calories, of course). Besides the two days' worth of caloric intake in a sitting, the number of addictive chemicals in that meal made me only want it more.</p>
<p>The most important lesson was how addictive food could be. I used to think ice cream and pizza were "bad foods" or salads and veggies were "good." Both things are the same - food. The ingredients make all the difference.</p>
<p>Two brands of peanut butter, with 200 calories/serving, can have completely different ingredients, depending on the amount of sugar and oils added. Removing processed foods from my diet didn't all happen at once. I backed down slowly. I think this is critical - <strong><em>quitting anything this addictive without stepping down is dangerous</em></strong> and fruitless. It tends to lead to severe levels of bounce back.</p>
<p>A triple became a single, and fries became small. I could get the taste without as many calories. Over time, I wanted it less and less. The foods many of us eat, especially diet foods, contain so much stuff that our bodies don't know what to do with them.</p>
<p>The more I eliminated fast foods - frozen meals, dining out, prepackaged foods - the more I lost. Do I still eat junk? Yep. Just so much less often and on a significantly lower amount.</p>
<h3 id="heading-eat-less-often">Eat Less Often</h3>
<p>I found myself eating around the same time every day, regardless. The first habit I had to break was eating on a schedule, not when I was hungry. This behavior would lead me to eat 4000+ calories daily without thinking. I honestly didn't even know what hunger was anymore.</p>
<p>Searching for what hunger felt like took some time, but I'm not sure how long. It took paying close attention to my body and waiting to eat only when I truly felt hungry. Asking the simple question, "am I hungry" I typically answer no.</p>
<p>Eventually, I could turn this new understanding into a more rigorous fasting approach. I am now able to fast and control intake without the initial struggles.</p>
<h3 id="heading-measuring-success">Measuring Success</h3>
<p>My initial measurement of success was stepping on a scale 2-3 times a day. Any loss was a win and felt great. Setting weight goals helped - my first was to get under 300 lbs. After that, I used 20-pound increments to not overwhelm myself with taking on too big of a goal.</p>
<p>Recording my weight was a big eye-opener. There was a pattern of loss/gain that I had never noticed before — seeing the pattern and associating that with how I was eating and feeling at the time helped my brain comprehend what was happening. It was something I could repeat now, with consistent results.</p>
<p>I also used roller coasters at a nearby theme park that year as a measurement - starting the season only fitting on a few to ending the year by riding them all. Clothing sizes also became a gauge. Going from 3XL to L and a 56" waist to 36" was a clear indicator that things were working.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673533475894/2c2e47c8-9cd1-43ae-936d-fea7e53afcf4.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>While it may seem like a brush-off type of answer, "I eat less food" is the only way I can sum up a year of retraining my mind and body to understand how to feed itself again. The problem with asking me how is I am still in disbelief myself and do not believe I have any level of expertise that will ultimately help.</p>
<p>What I can say is - you will have a different journey. You will find the patterns, habits, and foods that work best for you. If I have learned anything, it is this - no one else will do the work for you. No diet, book, video, class, or anything else will change you. It was not easy, but the extra time I may have with my family makes it worth it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673533562478/b9c80fa8-2bbb-45f6-875a-4f9b49ead028.jpeg" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[From Magic Shows to Marching Bands: A Busy 2022]]></title><description><![CDATA[Hey there! It's hard to believe that it's already 2023. It seems like just yesterday we were ringing in 2022, and now here we are at the start of a new year.
Looking back on last year, there's no denying it was great. In April, I joined Zoom. I love ...]]></description><link>https://kellyjandrews.dev/from-magic-shows-to-marching-bands-a-busy-2022</link><guid isPermaLink="true">https://kellyjandrews.dev/from-magic-shows-to-marching-bands-a-busy-2022</guid><category><![CDATA[New year]]></category><category><![CDATA[reflection]]></category><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Thu, 05 Jan 2023 14:33:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672925113258/e053e161-6769-4bb2-9657-4191958c4fca.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey there! It's hard to believe that it's already 2023. It seems like just yesterday we were ringing in 2022, and now here we are at the start of a new year.</p>
<p>Looking back on last year, there's no denying it was great. In April, I joined Zoom. I love being a part of such a fantastic team. I also had the opportunity to watch my son learn the vibraphone and play with the high school marching band, which was a fantastic experience. The family even took a trip to Universal Studios, which was so much fun.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672924855823/215d6893-1099-47c1-b04f-a585da762638.jpeg" alt class="image--center mx-auto" /></p>
<p>But perhaps the year's highlight was my first professional magic performance. I've always been interested in magic and have been practicing for years, but finally, being able to perform for a paying audience was a dream come true. It was a little nerve-wracking at first, but the reactions I got from the crowd made it all worth it.</p>
<p>In addition to that, I was fortunate enough to be invited to speak at two conferences last year. It was an excellent opportunity to share my knowledge and experiences with others, and I hope to have the chance to do more of that in the future.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672924874987/22b1ce7c-2364-42f9-b5ed-6e69f0cd1cc5.jpeg" alt class="image--center mx-auto" /></p>
<p>As great as 2022 was, though, it went by fast, and now we are at the start of 2023. But I'm excited about all that this year has in store.</p>
<p>At Zoom, we've got some inspiring initiatives that will significantly impact developers' lives. I'm also working on a small project that has the potential to turn into something huge in the long term.</p>
<p>And on a personal level, my wife and I are planning a trip to Vegas, which we've wanted to do for a long time, and then later in the year to see Metallica. Who knows what other adventures and opportunities the year will bring?</p>
<p>One thing I'm committed to this year is producing more content. I've always enjoyed blogging and streaming, and I look forward to doing more in 2023. I'm also hoping to have the chance to give more talks and possibly even host some workshops.</p>
<p>Overall, I'm feeling pretty optimistic about the year ahead. Here's to an incredible 2023!</p>
]]></content:encoded></item><item><title><![CDATA[Reflecting on my time at Vonage]]></title><description><![CDATA[It's my last day at Vonage today - no joke. While hard to completely grasp everything that's happened over the last couple of years, I wanted to try and take a moment and sum up everything since starting in August 2018.
Firebase
One of the more forwa...]]></description><link>https://kellyjandrews.dev/reflecting-on-my-time-at-vonage</link><guid isPermaLink="true">https://kellyjandrews.dev/reflecting-on-my-time-at-vonage</guid><category><![CDATA[Inspiration]]></category><category><![CDATA[review]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Fri, 01 Apr 2022 16:09:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/2JIvboGLeho/upload/v1648829295440/eJG4R77yx.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It's my last day at Vonage today - no joke. While hard to completely grasp everything that's happened over the last couple of years, I wanted to try and take a moment and sum up everything since starting in August 2018.</p>
<h2 id="heading-firebase">Firebase</h2>
<p>One of the more forward-looking platforms used during my tenure was Firebase. I was able to spend a ton of time here learning how to use as much of the functionality that Firebase had to offer. Whether it was <a target="_blank" href="https://kellyjandrews.hashnode.dev/real-time-sms-demo-with-react-node-and-google-translate">SMS translation</a>, or <a target="_blank" href="https://kellyjandrews.hashnode.dev/adding-2fa-to-a-react-app-using-firebase-function">adding two-factor authentication</a>, the tool kit that Firebase gives you is extremely powerful, and always a consideration when I create new projects.</p>
<h2 id="heading-sentiment-analysis">Sentiment Analysis</h2>
<p>While easy to implement, Sentiment analysis is hard to get right and is far from perfect. However, as a general guide for intervention, it can do a good job of finding customers that may be more upset and can make that actionable. For call centers, it's a great tool.</p>
<p><a target="_blank" href="https://developer.vonage.com/blog/19/10/17/sentiment-api-analysis-comparison-dr">Read my comparison - although it's a bit dusty now</a></p>
<h2 id="heading-chatbots">Chatbots</h2>
<p>Chatbots can be super fun, or really frustrating. While they can provide easy solutions to simple problems, covering all cases can be tricky, and it's easy to miss flows. Couple that with also trying to use speech transcription to match nouns and verbs programmatically, and now it's a disaster. </p>
<p>With the right amount of effort and planning, however, they can be extremely powerful and handle quite a lot of typical use cases. You can always push the call to a human when the script isn't working as a last resort. </p>
<p>You can read the tutorial for that <a target="_blank" href="https://kellyjandrews.hashnode.dev/using-dialogflow-with-firebase-and-the-vonage-messages-sandbox">here</a>.</p>
<h2 id="heading-transcription">Transcription</h2>
<p>I feel like transcription is really well solved in most cases. Listening to audio and transcribing it word for word is the main function of these APIs, and they all do it efficiently and accurately. Don't be afraid to use them.</p>
<p>[Read another outdated comparison] (https://developer.vonage.com/blog/19/09/10/extending-nexmo-streaming-transcription-dr)</p>
<h2 id="heading-translation">Translation</h2>
<p>Translation, much like transcription, has been fairly well solved. Every translation API has some kind of lexicon customization to cover industry-specific terms and can cost a lot more than the base service, however, the basic level of translation is really good in general. I plan on using these more in the future as well.</p>
<p><a target="_blank" href="https://developer.vonage.com/blog/19/12/10/text-translation-api-comparison-dr">One more ancient review</a></p>
<h2 id="heading-xstate">XState</h2>
<p>One of my favorite tools in JavaScript these days is XState. The ability to define how an application should work as a state machine, and use that in any front or back end to control the flow of states - <em>chef's kiss</em>.  I found it extremely useful with things like video, as I could create states that matched exactly what was happening with the SDK and API. </p>
<p>It's really a lot of fun to use - I recommend you <a target="_blank" href="https://kellyjandrews.hashnode.dev/learn-and-apply-xstate-with-vonage-video">try it out</a>.</p>
<h2 id="heading-oclif-and-cli-design">Oclif and CLI Design</h2>
<p>I built the Vonage CLI using Oclif, and I've really learned to love using oclif, CLI best practices, CLI history, and the command line itself. </p>
<p>Building a CLI isn't always the easiest thing to do, as it can run in so many environments, but solving these types of problems are fun for me. </p>
 <iframe width="560" height="315" src="https://www.youtube.com/embed/5MotpuSPURw"></iframe>


<h2 id="heading-lernamonrepo">Lerna/Monrepo</h2>
<p>While building the CLI and an updated SDK, I decided to work in a monorepo structure, using Lerna to manage the publication of the modules. I plan on writing more on how that is used in the future as well. It's a great resource, easy to understand, but hard to get started.</p>
<h2 id="heading-typescript-sorta">Typescript (sorta)</h2>
<p>Last, and well, least, Typescript. I have an appreciation for types now, and while I still feel like I have a long way to go, learning Typescript has been rewarding, if not frustrating. This was one of the harder things to learn for me, but I really am better off for doing it.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Overall - it was a great learning experience.  Being able to learn new things as a developer advocate is one of my favorite aspects of the job. </p>
<p>I'm thankful for the opportunities I've had here, and look forward to the future. </p>
]]></content:encoded></item><item><title><![CDATA[Learn and Apply XState with Vonage Video]]></title><description><![CDATA[Over the last few months, I have heard more about state machines used for front-end development. The idea of a state machine is that it has only a finite number of states and can only be in one state at any given time. Conceptually this makes perfect...]]></description><link>https://kellyjandrews.dev/learn-and-apply-xstate-with-vonage-video</link><guid isPermaLink="true">https://kellyjandrews.dev/learn-and-apply-xstate-with-vonage-video</guid><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Wed, 01 Jul 2020 04:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/FXFz-sW0uwo/upload/v1648826346752/gugzj067zG.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Over the last few months, I have heard more about <a target="_blank" href="https://en.wikipedia.org/wiki/Finite-state_machine">state machines</a> used for front-end development. The idea of a state machine is that it has only a finite number of states and can only be in one state at any given time. Conceptually this makes perfect sense for app development -  there are only a certain number of states available.</p>
<p>The concept of state machines and state charts is not new, and it isn't rooted in front-end development, either.  It's a mathematical model, and one used in many things around us. As an example, a light can be <code>OFF</code> or <code>ON</code>. You can describe anything with a state machine, even though this is a simple example.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648824795471/d3vAjgmro.png" alt="On and off states of lightbulb" /></p>
<h2 id="heading-introduction-to-xstate">Introduction to XState</h2>
<p>Using a state machine in front-end development has become much less complicated with the creation of the package <a target="_blank" href="https://xstate.js.org/docs/">XState</a>. XState helps us define state machines, create events and effects, and control the entire application flow. XState uses JavaScript methods and objects to describe the state machine.</p>
<p>The light bulb example from above would be written out like the following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> lightBulb = Machine({
  <span class="hljs-attr">id</span>: <span class="hljs-string">'lightBulb'</span>,
  <span class="hljs-attr">initial</span>: <span class="hljs-string">'off'</span>,
  <span class="hljs-attr">states</span>: {
    <span class="hljs-attr">off</span>: {
      <span class="hljs-attr">on</span>: {
        <span class="hljs-attr">TURN_ON</span>: <span class="hljs-string">'on'</span>
      }
    },
    <span class="hljs-attr">on</span>: {
      <span class="hljs-attr">on</span>: {
        <span class="hljs-attr">TURN_OFF</span>: <span class="hljs-string">'off'</span>,
      }
    }
  }
});
</code></pre>
<p>The state machine defined here shows the two states for the light bulb, <code>OFF</code> and <code>ON</code>, and the transitions from the events <code>TURN_ON</code> and <code>TURN_OFF</code>.</p>
<p>The object itself isn't very complex to read, but as the state machine grows in complexity, it can be harder to understand.  XState has create a tool to help out with this - the <a target="_blank" href="https://xstate.js.org/viz/">XState visualizer</a>.</p>
<iframe src="https://xstate.js.org/viz/?gist=a8a33932a88951692a8c94de647db40b&amp;embed=1" width="100%" height="150px"></iframe>

<p>Using the XState visualizer helps to see how the state machines work and are interactive, so playing with them is fun. If you want to check out the code for the machine, you can also click the code button to take a peek.</p>
<h2 id="heading-building-a-vonage-video-state-chart">Building a Vonage Video State Chart</h2>
<p>When I set out to learn state machines, and XState, my overall goal was to build an application similar to Google Meet using <a target="_blank" href="https://tokbox.com/developer/">Vonage Video</a>. The app would allow a user to create a meeting room, share the URL, and have a meeting with multiple streams. To get to that point, I had to learn some of the various concepts for state charts and how to represent those in XState.</p>
<p>Thinking through the possible application states is not an easy task, I found. There are many possibilities to explore, and ultimately finding the right solution takes some trial and error.  </p>
<p>The rest of this article will cover some basic concepts and build a state chart visualization that will mimic the eventual state machine. I will also provide some additional links and resources throughout so you can explore on your own.</p>
<h2 id="heading-states-and-state-nodes">States and State Nodes</h2>
<p>A <a target="_blank" href="https://xstate.js.org/docs/guides/states.html">state</a> is a representation of a machine at any given time. This moment can be defined and then made into a <a target="_blank" href="https://xstate.js.org/docs/guides/statenodes.html">state node</a> in XState, captured as a configuration.</p>
<p>In my Vonage Video app, there are a couple of different possible solutions to this, but I've found that describing the states in as simple of terms as possible is the best way to get to a useful result.</p>
<p>Creating a <a target="_blank" href="https://xstate.js.org/docs/guides/machines.html">machine</a> uses the following pattern:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> machine = Machine(state_nodes, options)
</code></pre>
<p>With a video state machine in mind, there are two compound states - <code>connected</code> and <code>disconnected</code>.</p>
<iframe src="https://xstate.js.org/viz/?gist=2d260c1f9c7b51ed47c4cf7bbc61b4df&amp;embed=1" width="100%" height="200px"></iframe>

<p>Two state nodes may seem overly simplified, but there are only two states after some trial and error. Each of these states, however, are more complex than an atomic (no children) node. Instead of creating every possible state at the top level, XState helps us organize with hierarchical and parallel state nodes.</p>
<h3 id="heading-hierarchical-state-nodes">Hierarchical State Nodes</h3>
<p>XState provides the option to create nested states called <a target="_blank" href="https://xstate.js.org/docs/guides/hierarchical.html"><code>hierarchical</code> state nodes</a>.  When we first start the machine, we can set it to <code>idle</code> first, as the machine will be ready but doing nothing. Why not just make another top-level atomic state node?</p>
<p>Adding states to the top level is called "state explosion" and is a typical side-effect of finite state machines. Since Vonage Video is still technically <code>disconnected</code>, nesting <code>idle</code> makes sense as the video is both disconnected and idle.  Another <code>disconnected</code> substate should be <code>ready</code>. The <code>disconnected.ready</code> state would happen just before moving to the <code>connected</code> state. The state machine also has a state node in-between <code>idle</code> and <code>ready</code> to get everything set up. This middle state can be called the <code>init</code> phase.</p>
<p>The state machine now would look like this:</p>
<iframe src="https://xstate.js.org/viz/?gist=da00b68575ceb12f88b1ad7ad0219020&amp;embed=1" width="100%" height="400px"></iframe>

<p>You should notice that we don't currently have a way to move in between the two nodes. We will cover events and actions in a moment.</p>
<h3 id="heading-parallel-state-nodes">Parallel State Nodes</h3>
<p>A <a target="_blank" href="https://xstate.js.org/docs/guides/parallel.html"><code>parallel</code> state node</a> allows the application to be in all of its substates at the same time. The Vonage Video state machine is highly event-driven, so we need to manage multiple states at once.</p>
<p>To specify that a state node is parallel, we use <code>type:parallel</code> in the configuration. After the transition to <code>connected</code>, three parallel states will occur - <code>session</code>, <code>publisher</code>, and <code>subscribers</code>. Each of these states will set up events and event listeners to control the Vonage Video service's responses.  </p>
<p>The resulting visualization looks like this:</p>
<iframe src="https://xstate.js.org/viz/?gist=bf9c3dd275206505c12bb0bb88df5d58&amp;embed=1" width="100%" height="400px"></iframe>

<p>With these primary states, we can control what our application shows at particular times.  Currently, however, we are unable to move between states. Let's have a look at events and transitions.</p>
<h2 id="heading-events-and-transitions">Events and Transitions</h2>
<p>Since a state node is just the configuration of an individual state, their inherently is not a way to move from state to state without declaring that in the state node.</p>
<p>Each node listens for a sent <a target="_blank" href="https://xstate.js.org/docs/guides/events.html">event</a> to <a target="_blank" href="https://xstate.js.org/docs/guides/transitions.html">transition</a> to the next state. In the light bulb example, the <code>TURN_ON</code> event sent tells the machine to transition to <code>on</code>.</p>
<p>Transitions only occur between top-level nodes and within hierarchical nodes. Parallel nodes are not allowed to transition between each other.  For our Vonage Video app, this means the following:</p>
<ol>
<li>When the page is ready, we can send a <code>START</code> event. This event will transition the state to <code>disconnected.init</code>.</li>
<li>The <code>disconnected.init</code> state will transition out once the <code>VIDEO_ELEMENT_CREATED</code> event has fired.</li>
<li>Once we reach <code>disconnected.ready</code>, we can allow the user to connect, sending the <code>CONNECT</code> event, and transition to <code>connected</code>.</li>
<li>If the application passed the <code>DISCONNECT</code> event, the state machine would disconnect.</li>
</ol>
<iframe src="https://xstate.js.org/viz/?gist=53c4772dc742ce1b84c40e3fa6669c6e&amp;embed=1" width="100%" height="400px"></iframe>

<p>Declaring an event and transition in XState uses the following pattern:</p>
<pre><code class="lang-js">on: {
  <span class="hljs-attr">EVENT_DESCRIPTOR</span>: <span class="hljs-string">'nextState'</span>
}
</code></pre>
<p>You can add specific actions to the transition as well. I would recommend you read the sections on <a target="_blank" href="https://xstate.js.org/docs/guides/transitions.html#internal-transitions">internal and external transitions</a> in the documentation.  They cover in great detail the various types of transitions possible.</p>
<h3 id="heading-guarded-transitions">Guarded Transitions</h3>
<p>You may have noticed that one of the transitions has a <code>cond</code> node in the transition.  This conditional is what's called a <a target="_blank" href="https://xstate.js.org/docs/guides/guards.html#guards-condition-functions"><code>guarded</code> transition</a>. Guarded transitions help protect the machine from moving to a state that is not allowed based on certain conditions.  In this case, I don't want to transition to <code>ready</code> until the token and video element are both created.</p>
<pre><code class="lang-js">on: {
  <span class="hljs-string">'VIDEO_ELEMENT_CREATED'</span>: {
    <span class="hljs-attr">target</span>: <span class="hljs-string">'ready'</span>,
    <span class="hljs-attr">cond</span>: <span class="hljs-string">'checkToken'</span>
  }
}
</code></pre>
<p>The guard condition <code>checkToken</code> is a named reference to the guards object in the options paramater sent to machine:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> video = Machine(
  state_nodes,{
  <span class="hljs-attr">guards</span>: {
    <span class="hljs-attr">checkToken</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-literal">true</span>
  }
});
</code></pre>
<h2 id="heading-context">Context</h2>
<p>In order to be more useful to an application, our state machine will need a longer living state, called an <a target="_blank" href="https://xstate.js.org/docs/guides/context.html#initial-context"><code>extended state</code>, or <code>context</code></a>. The context object updates using various effects with the <a target="_blank" href="https://xstate.js.org/docs/guides/context.html#assign-action"><code>assign()</code> method</a>.</p>
<p>Speaking of actions, let's get to these now and finish out the rest of the skeleton.</p>
<h3 id="heading-actions-and-services">Actions and Services</h3>
<p>There are what's known as "side-effects" in state machines that XState puts into one of two categories:</p>
<ol>
<li>"Fire-and-forget" - where the effect doesn't send events</li>
<li>Invoked - where sending events are required</li>
</ol>
<h3 id="heading-actions">Actions</h3>
<p><a target="_blank" href="https://xstate.js.org/docs/guides/actions.html">Actions</a> are single effects and tend to be one of the more common effects in the video state machine. You can use actions when you enter or exit a node, or during a transition. Understanding the order of actions is incredibly important.</p>
<p>An excellent resource for learning the action order (and all of the topics on XState) is a video published by <a target="_blank" href="https://twitter.com/kyleshevlin">@kyleshvlin</a> over at <a target="_blank" href="https://egghead.io/lessons/xstate-how-action-order-affects-assigns-to-context-in-a-xstate-machine">Egghead.io</a>. It helped me understand how actions fired.</p>
<p>The bulk of the actions for this machine revolve around updating the context as a transition. When events are called, we can use the action to run the <code>assign()</code> method:</p>
<pre><code class="lang-js">on: {
  <span class="hljs-string">'SOME_EVENT'</span>: {
      <span class="hljs-attr">actions</span>: assign({<span class="hljs-string">'someContext'</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> e.someValue})
  } 
}
</code></pre>
<h3 id="heading-services">Services</h3>
<p><a target="_blank" href="https://xstate.js.org/docs/guides/communication.html#the-invoke-property">Invoked Services</a> are the other main effect in the video state machine. To use promises and event listeners, you need to invoke a service.  This concept was, by far, the hardest for me to grasp. The main difficulty I had to work through was understanding that an invoked service stops when the state exits.  If you transition too quickly, your promise or callback will disappear.</p>
<p>There are two primary services that I'm using in the video state machine, promises, and callbacks. The <a target="_blank" href="https://xstate.js.org/docs/guides/communication.html#invoking-promises"><code>invoke promises</code> service</a> allows the state machine to use a promise to either resolve or reject and then act accordingly. I used this to interact with the server asynchronously and then update the context when complete.</p>
<p>The function signature of invoked promises looks like this:</p>
<pre><code class="lang-js">src: <span class="hljs-function">(<span class="hljs-params">context, event</span>) =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (event.error) reject(<span class="hljs-string">'Rejected'</span>)
  resolve(<span class="hljs-string">'Resolved'</span>)
}),
<span class="hljs-attr">onDone</span>: {<span class="hljs-comment">/*success transition*/</span>}
<span class="hljs-attr">onError</span>: {<span class="hljs-comment">/*error transition*/</span>}
</code></pre>
<p>The second, and probably the most important part of this machine, is the <a target="_blank" href="https://xstate.js.org/docs/guides/communication.html#invoking-callbacks"><code>invoked callbacks</code></a>. The Vonage Video architecture relies heavily on events and event listeners.  In a state machine, those are set up through a callback. Let me show you an example:</p>
<pre><code class="lang-js">invoke: {
  <span class="hljs-attr">id</span>: <span class="hljs-string">'initPublisher'</span>,
  <span class="hljs-attr">src</span>: <span class="hljs-function">(<span class="hljs-params">ctx</span>) =&gt;</span> <span class="hljs-function">(<span class="hljs-params">cb</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> publisher = initPublisher(pubOptions);
    publisher.on(<span class="hljs-string">'videoElementCreated'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
      cb({ <span class="hljs-attr">type</span>: <span class="hljs-string">'VIDEO_ELEMENT_CREATED'</span>, <span class="hljs-attr">publisher</span>: publisher })
    })
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> publisher.off(<span class="hljs-string">'videoElementCreated'</span>);
  }
}
</code></pre>
<p>The function signature of an invoked callback looks like this:</p>
<pre><code class="lang-js">src: <span class="hljs-function">(<span class="hljs-params">context, event</span>) =&gt;</span> <span class="hljs-function">(<span class="hljs-params">callback, onReceive</span>) =&gt;</span> {

  callback(<span class="hljs-string">'EVENT'</span>);
  onReceive(event) =&gt; { callback(<span class="hljs-string">'OTHER_EVENT'</span>) };

  <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> cleanup()
};
</code></pre>
<p>Using actions and services, our Vonage Video state machine now looks like the following:</p>
<iframe src="https://xstate.js.org/viz/?gist=826cd253fd9eac80c2daa6bfa0924f50&amp;embed=1" width="100%" height="400px"></iframe>

<p>Don't forget to click around, and click the <code>code</code> tab to see what the state machine looks like as a configuration. </p>
<h2 id="heading-resources-and-wrap-up">Resources and Wrap-up</h2>
<p>Ok - so you've made it this far.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648824750085/v1m_swoun.gif" alt="I'm proud of you!" /></p>
<p>If this is your first time looking at XState, it's a lot to take in all at once.  I'm still exploring and learning new ways to do things. This post is just a small portion of what's out there. As you are learning - here are a few great resources you will want to check out</p>
<ul>
<li><a target="_blank" href="https://egghead.io/courses/introduction-to-state-machines-using-xstate">Kyle Shevlin's Intro to State Machines Using XState videos</a></li>
<li><a target="_blank" href="https://xstate.js.org/docs/guides/start.html">XState Docs</a></li>
<li><a target="_blank" href="https://flaviocopes.com/xstate/">Introduction to XState at flaviocopes.com</a></li>
<li><a target="_blank" href="https://www.smashingmagazine.com/2018/01/rise-state-machines/">The Rist of State Machines - Smashing Magazine</a></li>
</ul>
<p>Feel free to reach out if you have questions about XState, and we can learn something new together!</p>
]]></content:encoded></item><item><title><![CDATA[Using Dialogflow with Firebase and the Vonage Messages Sandbox]]></title><description><![CDATA[Using Dialogflow to build a chatbot to interact with your customer is a great way to handle incoming requests like reservations, bank inquiries, FAQs, and initial support. When you combine Dialogflow with Firebase and Vonage Messages API, things star...]]></description><link>https://kellyjandrews.dev/using-dialogflow-with-firebase-and-the-vonage-messages-sandbox</link><guid isPermaLink="true">https://kellyjandrews.dev/using-dialogflow-with-firebase-and-the-vonage-messages-sandbox</guid><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Mon, 01 Jun 2020 18:11:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/Q1p7bh3SHj8/upload/v1648826212166/CP-IjG2O0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Using <a target="_blank" href="https://Dialogflow.com/">Dialogflow</a> to build a chatbot to interact with your customer is a great way to handle incoming requests like reservations, bank inquiries, FAQs, and initial support. When you combine Dialogflow with <a target="_blank" href="https://firebase.google.com/">Firebase</a> and <a target="_blank" href="https://developer.nexmo.com/messages/overview">Vonage Messages API</a>, things start to heat up. </p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>For this tutorial, you are connecting a Dialogflow agent with the Vonage Messages API using Firebase. When complete, you can send a message in Facebook Messenger to the Dialogflow agent and get a response based on the agent's rules.</p>
<p>You'll want to make sure you have:</p>
<ul>
<li><a target="_blank" href="https://cloud.google.com/">Google Cloud Account</a></li>
<li><a target="_blank" href="https://firebase.google.com/docs/cli">Firebase CLI</a></li>
<li><a target="_blank" href="https://developer.nexmo.com/messages/concepts/messages-api-sandbox">Vonage Messages API Sandbox</a></li>
</ul>
<p></p>
<h2 id="heading-create-a-firebase-application">Create a Firebase Application</h2>
<p>If this is your first time setting up a Firebase project, I would recommend you work through Google's <a target="_blank" href="https://firebase.google.com/docs/functions/get-started">Firebase getting started tutorial</a> first to get yourself acclimated to the environment. If you are accustomed to Firebase or feeling adventurous, the first step is to create a new Firebase project. Feel free to name this anything memorable.</p>
<p><img src="/content/blog/using-dialogflow-with-firebase-and-the-vonage-messages-sandbox/firebase-project-setup.png" alt="Create Firebase Project" /></p>
<p>Once through the initial creation process (less than 5 minutes), set up these two items in the dashboard of your project:</p>
<ol>
<li><strong>Upgrade to Blaze Pay-As-You-Go</strong> You can locate this under Settings -&gt; Usage and Billing -&gt; Details &amp; Settings -&gt; Modify Plan. Firebase requires Blaze for any 3rd party integrations to work.</li>
<li><strong>Pick a Resource Location</strong> Find this in Settings -&gt; Project Settings -&gt; General</li>
</ol>
<p>Once those are all set, you can set up the Dialogflow agent and associate it with the new Firebase/GCP project.</p>
<h2 id="heading-set-up-dialogflow-agent">Set Up Dialogflow Agent</h2>
<p>Now you are ready to create a new Dialogflow agent—the chatbot that you will be interacting with later.  Dialogflow uses Natural Language Processing to determine the user's intents based on what they type or speak and return an action based on that intent.</p>
<p>Next, import a "Prebuilt Agent" provided by Google to help get you started. If you are new to DialogFlow, you need to create a blank agent on the landing page.</p>
<p><img src="/content/blog/using-dialogflow-with-firebase-and-the-vonage-messages-sandbox/create-blank-dialogflow-agent.png" alt="Create Blank Dialogflow Agent" /></p>
<p>Once you have an agent, you can select the "Prebuilt Agents" from the left menu. You are presented with many different types of agents that might help you start learning how to build your chatbot. For this example, I picked "Easter Eggs."</p>
<p><img src="/content/blog/using-dialogflow-with-firebase-and-the-vonage-messages-sandbox/prebuilt-agents.png" alt="Prebuilt Agents" /></p>
<p>Import an agent and wait for it to complete the build process. Once finished, get the credentials for the service account.</p>
<h3 id="heading-download-service-account-keys">Download Service Account Keys</h3>
<p>Navigate to the GCP IAM &amp; Admin service for <a target="_blank" href="https://console.cloud.google.com/iam-admin/serviceaccounts">service accounts</a>. Make sure you select the correct project that you have been using already.</p>
<p>You should see a service account that looks similar to <code>dialogflow@myexampleproject.iam.gserviceaccount.com</code>.  Click the dots on the right and select <code>Create Key</code> and then pick <code>JSON</code> from the options. This process generates and downloads a <code>JSON</code> file.  Save this file for later.</p>
<h2 id="heading-build-functions-locally">Build Functions Locally</h2>
<p>You now have a Firebase project and a Dialogflow agent ready to go. The two systems need to communicate with each other and with Vonage APIs. To do that, you need a little code to make things work.  </p>
<p>The Firebase CLI has some helpful tools to get you started. Make sure you have the latest version and run the following: </p>
<pre><code class="lang-shell">firebase init functions
</code></pre>
<p>This command creates the project inside your root folder and asks you to connect it to an already existing project. After a few prompts, the command runs <code>npm install</code> to install all of the requirements. Once complete, navigate to the functions folder and use this command to install the other packages you need:</p>
<pre><code class="lang-shell">npm i @google-cloud/Dialogflow axios
</code></pre>
<p>At this time, you should also move the service account <code>JSON</code> file over to the <code>functions</code> directory. Once you've done that, make sure to add it to your <code>.gitignore</code> file as well.</p>
<h3 id="heading-the-code">The Code</h3>
<p>Inside the <code>functions</code> directory is <code>index.js</code>. They provide some sample code to get you started, but you can delete and replace it with the following code to send a message to the Vonage Messages Sandbox. Make sure to have your Vonage API key and secret handy for this.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> functions = <span class="hljs-built_in">require</span>(<span class="hljs-string">'firebase-functions'</span>);
<span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">'axios'</span>);
<span class="hljs-comment">// the service account JSON file downloaded earlier - make sure this is named properly</span>
<span class="hljs-keyword">const</span> serviceAccount = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./service_account.json'</span>);
<span class="hljs-keyword">const</span> Dialogflow = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@google-cloud/Dialogflow'</span>);
<span class="hljs-comment">// This method takes the TO_ID, FROM_ID, MSG from the webhook defined later</span>
<span class="hljs-comment">// DialogFlow responses will be sent using this function</span>
<span class="hljs-comment">// You will need to get your API Key and Secret from the Vonage Dashboard.</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendMessage</span>(<span class="hljs-params">TO_ID, FROM_ID, MSG</span>) </span>{
  <span class="hljs-keyword">return</span> axios.post(<span class="hljs-string">'https://messages-sandbox.nexmo.com/v0.1/messages'</span>, {
    <span class="hljs-string">"from"</span>: { <span class="hljs-string">"type"</span>: <span class="hljs-string">'messenger'</span>, <span class="hljs-string">"id"</span>: FROM_ID },
    <span class="hljs-string">"to"</span>: { <span class="hljs-string">"type"</span>: <span class="hljs-string">'messenger'</span>, <span class="hljs-string">"id"</span>: TO_ID },
    <span class="hljs-string">"message"</span>: {
      <span class="hljs-string">'content'</span>: {
        <span class="hljs-string">'type'</span>: <span class="hljs-string">'text'</span>,
        <span class="hljs-string">'text'</span>: MSG
      }
    }
  }, {
    <span class="hljs-attr">auth</span>: {
      <span class="hljs-attr">username</span>: <span class="hljs-string">'API_KEY'</span>,
      <span class="hljs-attr">password</span>: <span class="hljs-string">'API_SECRET'</span>
    }
  })
}
</code></pre>
<p>The next function is the gateway between Firebase and Dialogflow.  The incoming message from Vonage is sent to the Dialogflow agent to determine the intent of the message. Once the intent is determined, Dialogflow passes back a message to forward on to the client.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">DialogflowGateway</span>(<span class="hljs-params">text, sessionId</span>) </span>{
  <span class="hljs-keyword">const</span> sessionClient = <span class="hljs-keyword">new</span> Dialogflow.SessionsClient({ <span class="hljs-attr">credentials</span>: serviceAccount });
  <span class="hljs-keyword">const</span> sessionPath = sessionClient.projectAgentSessionPath(<span class="hljs-string">'YOUR-GOOGLE-PROJECT'</span>, sessionId);
  <span class="hljs-keyword">const</span> request = {
    <span class="hljs-attr">session</span>: sessionPath,
    <span class="hljs-attr">queryInput</span>: {
      <span class="hljs-attr">text</span>: {
        <span class="hljs-attr">text</span>: text,
        <span class="hljs-attr">languageCode</span>: <span class="hljs-string">'en-US'</span>,
      },
    },
  };
  <span class="hljs-built_in">console</span>.log(request)
  <span class="hljs-keyword">return</span> sessionClient.detectIntent(request);
}
</code></pre>
<p>The Vonage Messages Sandbox uses an inbound webhook to receive the messages using a POST method to Firebase.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">exports</span>.webhook = functions.https.onRequest(<span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { message, <span class="hljs-keyword">from</span>, to } = req.body;
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// the message from the user is sent to Dialogflow, and a response is returned</span>
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> DialogflowGateway(message.content.text, <span class="hljs-keyword">from</span>.id);
    <span class="hljs-comment">// the response from Dialogflow is sent back to the user through Vonage</span>
    <span class="hljs-keyword">await</span> sendMessage(<span class="hljs-keyword">from</span>.id, to.id, response[<span class="hljs-number">0</span>].queryResult.fulfillmentText);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(error);
  }
  res.sendStatus(<span class="hljs-number">200</span>);
});
</code></pre>
<p>These three functions should be inside the <code>index.js</code> file and saved. All that is left is to deploy these functions.  </p>
<h3 id="heading-deploy-functions">Deploy Functions</h3>
<p>Using the Firebase CLI, deploy the functions to Firebase using this command:</p>
<pre><code class="lang-shell">firebase deploy --only functions
</code></pre>
<p>On successful deployment, you get the webhook required for the <a target="_blank" href="https://dashboard.nexmo.com/messages/sandbox">Vonage Messages Sandbox <code>Inbound</code> webhook</a>.</p>
<pre><code class="lang-shell">✔  functions: Finished running predeploy script.
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
✔  functions: required API cloudfunctions.googleapis.com is enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (47.86 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: creating Node.js 8 function webhook(us-central1)...
✔  functions[status(us-central1)]: Successful create operation.
✔  functions[webhook(us-central1)]: Successful create operation.
Function URL (webhook): https://us-central1-myexampleproject.cloudfunctions.net/webhook
✔  Deploy complete!
</code></pre>
<p>Once you have the webhook updated, you can test it out on Facebook Messenger.</p>
<p><img src="/content/blog/using-dialogflow-with-firebase-and-the-vonage-messages-sandbox/messenger-example.png" alt="Facebook Messenger Example" /></p>
<p>Feel free to change intents and actions in Dialogflow to start to see how it can work in your communication strategy.  </p>
<p>If you'd like to see a full version of this and other Google Cloud examples, you can find it here - https://github.com/nexmo-community/google-cloud-sample-code.  If you have any questions or requests for other examples with Google Cloud, Dialogflow or Firebase, open up a pull request or shoot me a message <a target="_blank" href="https://twitter.com/kellyjandrews">on Twitter</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Adding 2FA to a React App Using Firebase Function]]></title><description><![CDATA[If you're like me, you probably have a few "smart" devices around your home. There are multiple ways to interact and control these devices, but I wanted to be able to control them with text messages and eventually voice as well.  
So I set out to bui...]]></description><link>https://kellyjandrews.dev/adding-2fa-to-a-react-app-using-firebase-function</link><guid isPermaLink="true">https://kellyjandrews.dev/adding-2fa-to-a-react-app-using-firebase-function</guid><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Wed, 01 Apr 2020 04:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/6pflEeSzGUo/upload/v1648826411949/_vSv5xOT3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you're like me, you probably have a few "smart" devices around your home. There are multiple ways to interact and control these devices, but I wanted to be able to control them with text messages and eventually voice as well.  </p>
<p>So I set out to build some tooling in Firebase to get me going.  The first step I wanted to take, however, was securing the phone numbers that have access, and I thought it would be a perfect time to try out the Verify API. It's admittedly a bit over-the-top since this isn't a distributed app, but for safety, a phone number must go through the verification process to access my devices.  </p>
<h2 id="heading-verify-api">Verify API</h2>
<p>The <a target="_blank" href="https://developer.nexmo.com/verify/overview">Verify API</a> is a way to confirm that the phone belongs to the user.  Performing the verification helps protect against spam and suspicious activity, as well as validating ownership.  </p>
<p>The API itself has quite a lot packed into it. Its <a target="_blank" href="https://developer.nexmo.com/verify/guides/changing-default-timings">configuration options</a> let you build the <a target="_blank" href="https://developer.nexmo.com/verify/guides/workflows-and-events">exact workflow</a> that works for your system. As an example, the default workflow sends an SMS with a PIN code, waits 125 seconds, then calls with a Text-to-Speech event, waits 3 additional minutes, then calls again, and waits 5 minutes before expiring the request altogether.</p>
<p>I like having this level of control over something like this as it allows me to be very specific about how I can interact with my users. In my particular instance, I kept it very simple and did just one SMS message that expired in two minutes, since I wanted this mostly for my own purposes.</p>
<pre><code class="lang-js">    <span class="hljs-keyword">let</span> opts = {
      <span class="hljs-attr">number</span>: context.params.phoneNumber,
      <span class="hljs-attr">brand</span>: <span class="hljs-string">"Total Home Control"</span>,
      <span class="hljs-attr">workflow_id</span>: <span class="hljs-number">6</span>,
      <span class="hljs-attr">pin_expiry</span>: <span class="hljs-number">120</span>
    };
</code></pre>
<p>If you want to get started with the Verify API, you can sign up for a <a target="_blank" href="https://dashboard.nexmo.com/sign-up">Vonage</a> account today to get going.</p>
<h2 id="heading-firebase-functions">Firebase Functions</h2>
<p>Since I decided on using <a target="_blank" href="https://firebase.google.com/">Firebase</a> and <a target="_blank" href="https://firebase.google.com/products/firestore">Firestore</a>, setting up some <a target="_blank" href="https://firebase.google.com/products/functions">Cloud Functions</a> to interact with the data and the Verify API was my next step. Each time a new phone number was created, I wanted to send it a verification code and then have a function to check the code.  </p>
<h3 id="heading-promises-promises">Promises, Promises</h3>
<p>When you first learn Cloud Functions, you may try some <a target="_blank" href="https://firebase.google.com/docs/functions/get-started">simple operations</a> and build your confidence,  which is what I did. After going through some of the simple functions first, I figured I'd be able to build this out fairly quickly.  </p>
<p>And I was wrong.  One detail I completely overlooked is that callback methods do not evaluate in the Cloud Function environment the way they do in other environments. Once there is a returned value or promise, the CPU stops. Since the Nexmo JavaScript SDK is running on callback methods, it stops processing.</p>
<p>Not knowing this had to be one of the more frustrating problems I've run into in a long time.  The timing of everything was weird because the call back would run when I tried again, causing me to think I wasn't waiting long enough or the latency was terrible.  </p>
<p>Once I sorted that out, I realized I needed to <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises">create Promise wrappers</a> for the SDK methods, and everything worked perfectly. If you want some useful tips &amp; tricks, I recommend reading <a target="_blank" href="https://firebase.google.com/docs/functions/tips">this Firebase documentation guide.</a></p>
<h3 id="heading-requesting-the-verify-code">Requesting the Verify Code</h3>
<p>The Verify request method in the <a target="_blank" href="https://developer.vonage.com/sdk/stitch/javascript/">Vonage JavaScript SDK</a> is quite minimal code, as the framework there makes it simple to do most everything. The first thing I had to do was wrap it in a promise.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyRequest</span>(<span class="hljs-params">opts</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    nexmo.verify.request(opts, <span class="hljs-function">(<span class="hljs-params">err, res</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (err) reject(err);
      resolve(res);
    })
  });
}
</code></pre>
<p>Creating this wrapper allows the callback method to run and return as a Promise resolution, instead of being ignored.</p>
<p>With this method, I could now create a Firebase function to run when the app added a new number to Firestore.</p>
<pre><code class="lang-js"><span class="hljs-built_in">exports</span>.requestVerify = functions.firestore.document(<span class="hljs-string">'/phoneNumbers/{phoneNumber}'</span>)
  .onCreate(<span class="hljs-function">(<span class="hljs-params">entry, context</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> opts = {
      <span class="hljs-attr">number</span>: context.params.phoneNumber,
      <span class="hljs-attr">brand</span>: <span class="hljs-string">"Total Home Control"</span>,
      <span class="hljs-attr">workflow_id</span>: <span class="hljs-number">6</span>,
      <span class="hljs-attr">pin_expiry</span>: <span class="hljs-number">120</span>
    };

    <span class="hljs-keyword">return</span> verifyRequest(opts)
      .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(res);
        <span class="hljs-keyword">return</span> admin.firestore().doc(<span class="hljs-string">`/phoneNumbers/<span class="hljs-subst">${context.params.phoneNumber}</span>`</span>).update({ <span class="hljs-attr">req_id</span>: res.request_id })
      })
      .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(res))
      .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> <span class="hljs-built_in">console</span>.error(err));
  });
</code></pre>
<p>With the Verify API, we need to keep track of the <code>request_id</code> to use in the check process. I use this to indicate that the verification process started but has not yet been completed.  </p>
<h3 id="heading-checking-the-verify-code">Checking the Verify Code</h3>
<p>Same as the previous example, the SDK method needs to first be wrapped as a Promise. </p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyCheck</span>(<span class="hljs-params">opts</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    nexmo.verify.check(opts, <span class="hljs-function">(<span class="hljs-params">err, res</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (err) reject(err);
      resolve(res);
    })
  });
}
</code></pre>
<p>Once the user receives it, the React application asks for the code and then calls the function directly from the application, passing the <code>request_id</code>, and the <code>code</code>. </p>
<pre><code class="lang-js"><span class="hljs-built_in">exports</span>.checkVerify = functions.https.onCall(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
  <span class="hljs-keyword">let</span> opts = {
    <span class="hljs-attr">request_id</span>: data.req_id,
    <span class="hljs-attr">code</span>: data.code
  };

  <span class="hljs-keyword">return</span> verifyCheck(opts)
    .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (res.status === <span class="hljs-string">"0"</span>) {
        <span class="hljs-keyword">return</span> admin.firestore().doc(<span class="hljs-string">`/phoneNumbers/<span class="hljs-subst">${data.phoneNumber}</span>`</span>).update({ <span class="hljs-attr">req_id</span>: <span class="hljs-literal">null</span>, <span class="hljs-attr">verified</span>: <span class="hljs-literal">true</span> });
      }
    })
    .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(res))
    .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> <span class="hljs-built_in">console</span>.error(err));
});
</code></pre>
<p>As long as the code checks out, the document updates to include a <code>verified</code> flag, and the process is over.  There are error status responses to check for and respond to accordingly—for example, if the code has timed out. My app currently assumes it passes.</p>
<h3 id="heading-react-app">React App</h3>
<p>I won't spend too much time explaining all the code I wrote for my app, but the highlights are adding the data, and then calling the Firebase function from the front end.</p>
<p>In my app, I have a form to add a new number, consisting of just the phone number field. On submission, it merely adds it to the database. I've also set up a Firebase context file that sets the connections between my app and Firebase, so I can easily import everything I need.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { db, fb } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../context/firebase'</span>;

<span class="hljs-comment">//-----//</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_handleSubmit</span>(<span class="hljs-params">e</span>) </span>{
  e.preventDefault();

  <span class="hljs-keyword">let</span> data = {
    <span class="hljs-attr">owner</span>: fb.auth().currentUser.uid,
    <span class="hljs-attr">verified</span>: <span class="hljs-literal">false</span>,
  };

  <span class="hljs-keyword">return</span> db.collection(<span class="hljs-string">'phoneNumbers'</span>).doc(phoneNumber).set(data);
}

<span class="hljs-comment">//-----//</span>
</code></pre>
<p>The verification is nearly the same form with a similar submit method.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { functions } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../context/firebase'</span>;

<span class="hljs-comment">//-----//</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_handleSubmit</span>(<span class="hljs-params">e</span>) </span>{
  e.preventDefault();
  <span class="hljs-keyword">var</span> checkVerify = functions.httpsCallable(<span class="hljs-string">'checkVerify'</span>);
  checkVerify({ <span class="hljs-attr">code</span>: code, <span class="hljs-attr">req_id</span>: value[<span class="hljs-number">0</span>]?.data().req_id, <span class="hljs-attr">phoneNumber</span>: value[<span class="hljs-number">0</span>]?.id }).then(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">result</span>) </span>{
    <span class="hljs-comment">//close the form</span>
  });
}

<span class="hljs-comment">//-----//</span>
</code></pre>
<p>The Firebase SDK provides a <code>functions</code> export to let you use <code>httpsCallable()</code> and name the function. Instead of needing to write any HTTP requests and waiting on those, this simplifies the process.</p>
<h2 id="heading-sample-code">Sample Code</h2>
<p>You can find my <a target="_blank" href="https://github.com/kellyjandrews/smart-home-app">sample application code here</a>. The app I built is more of a personal app for me, but feel free to have a look and use anything you might find useful.  </p>
]]></content:encoded></item><item><title><![CDATA[Real-time SMS Demo with React, Node, and Google Translate]]></title><description><![CDATA[Last year I worked with the [Google Translate API (https://www.nexmo.com/blog/2019/10/24/extending-nexmo-google-cloud-translation-api-dr) to translate SMS messages. After showing the rest of the team, they wanted a demo they could show off to other d...]]></description><link>https://kellyjandrews.dev/real-time-sms-demo-with-react-node-and-google-translate</link><guid isPermaLink="true">https://kellyjandrews.dev/real-time-sms-demo-with-react-node-and-google-translate</guid><dc:creator><![CDATA[Kelly Andrews]]></dc:creator><pubDate>Wed, 11 Mar 2020 14:43:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/w7ZyuGYNpRQ/upload/v1648826312145/29ybfBAFe.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last year I worked with the [Google Translate API (https://www.nexmo.com/blog/2019/10/24/extending-nexmo-google-cloud-translation-api-dr) to translate SMS messages. After showing the rest of the team, they wanted a demo they could show off to other developers at conferences we attended. Based on that, I set out to create a frontend with React that could display the translations in real-time.</p>
<h2 id="heading-building-the-websocket">Building the WebSocket</h2>
<h3 id="heading-whats-a-websocket">What's a WebSocket?</h3>
<p>For this demo, I decided that using a WebSocket would be a great solution. If you haven't used a WebSocket before, it's a protocol that allows a client and server to communicate in real-time. WebSockets are bi-directional, meaning the client and server can both send and receive messages. When you first connect to a WebSocket, the connection is made by upgrading an HTTP protocol to the WebSocket protocol and is kept alive as long as it goes uninterrupted. Once established, it provides a continuous stream of content. Exactly what we need to receive incoming, translated SMS messages.</p>
<h3 id="heading-create-the-websocket-server-in-node">Create the WebSocket Server in Node</h3>
<p>As an initial step to creating the WebSockets, the server requires a path to allow for client connections. Starting with the original server file from my <a target="_blank" href="https://github.com/nexmo-community/sms-google-translate-js">previous post</a>, we can make a few minor changes to create the WebSocket server and the events and listeners required by the client.</p>
<p>Using the <a target="_blank" href="https://github.com/websockets/ws"><code>ws</code></a> package on NPM, we can quickly create what we need to get this working.  </p>
<pre><code class="lang-bash">npm install ws
</code></pre>
<p>Once installed, include the package in your server file, and create the WebSocket server. <code>WS</code> allows a <code>path</code> option to set the route the client uses to connect.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> WebSocket = <span class="hljs-built_in">require</span>(<span class="hljs-string">'ws'</span>);

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> server = http.createServer(app);
<span class="hljs-keyword">const</span> wss = <span class="hljs-keyword">new</span> WebSocket.Server({ server, <span class="hljs-attr">path</span>: <span class="hljs-string">"/socket"</span> });
</code></pre>
<p>With this bit of code, the client now has a place to connect to the WebSocket route <code>/socket</code>. With the server ready to go, you need to now listen for a <code>connection</code> event. When the client connects, the server uses the following to set up the other listeners we need:</p>
<pre><code class="lang-javascript">wss.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function">(<span class="hljs-params">ws</span>) =&gt;</span> {
  ws.isAlive = <span class="hljs-literal">true</span>;
  ws.translateTo = <span class="hljs-string">'en'</span>;

  ws.on(<span class="hljs-string">'pong'</span>, <span class="hljs-function">() =&gt;</span> {
    ws.isAlive = <span class="hljs-literal">true</span>;
  });

  ws.on(<span class="hljs-string">'message'</span>, <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> {
    translateTo = message;
  });

});
</code></pre>
<p>There are two main points to call out:</p>
<ol>
<li>On connection, we set the property <code>isAlive</code> to <code>true</code>, and listen for the <code>pong</code> event. This event is for the server to check and maintain a connection with the client. The server sends a <code>ping</code> and responds with <code>pong</code> to verify it's still a live connection.</li>
<li>Here I set up <code>translateTo</code> as a property to store. <code>translateTo</code> is set through each client using a dropdown. When someone using our booth demo app selects a different language, that action sets this to translate the SMS texts into the requested language.</li>
</ol>
<h3 id="heading-keeping-the-connection-alive">Keeping the Connection Alive</h3>
<p>One essential item to be concerned with is checking for clients that disconnect. It's possible that during the disconnection process, the server may not be aware, and problems may occur. With a good friend <code>setInterval()</code>, we can check if our clients are still there and reconnect them if needed.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
  wss.clients.forEach(<span class="hljs-function">(<span class="hljs-params">ws</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (!ws.isAlive) <span class="hljs-keyword">return</span> ws.terminate();
    ws.isAlive = <span class="hljs-literal">false</span>;
    ws.ping(<span class="hljs-literal">null</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>);
  });
}, <span class="hljs-number">10000</span>);
</code></pre>
<h3 id="heading-sending-messages-to-the-client">Sending Messages to the Client</h3>
<p>Now that the WebSocket is connected and monitored, we can handle the inbound messages from Nexmo, the translation, and the response to the client. The method <code>handleRoute</code> needs to be updated from its original state to add the response for each client.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> handleRoute = <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {

  <span class="hljs-keyword">let</span> params = req.body;

  <span class="hljs-keyword">if</span> (req.method === <span class="hljs-string">"GET"</span>) {
    params = req.query
  }

  <span class="hljs-keyword">if</span> (!params.to || !params.msisdn) {
    res.status(<span class="hljs-number">400</span>).send({ <span class="hljs-string">'error'</span>: <span class="hljs-string">'This is not a valid inbound SMS message!'</span> });
  } <span class="hljs-keyword">else</span> {
    wss.clients.forEach(<span class="hljs-keyword">async</span> (client) =&gt; {
      <span class="hljs-keyword">let</span> translation = <span class="hljs-keyword">await</span> translateText(params, client.translateTo);
      <span class="hljs-keyword">let</span> response = {
        <span class="hljs-attr">from</span>: obfuscateNumber(req.body.msisdn),
        <span class="hljs-attr">translation</span>: translation.translatedText,
        <span class="hljs-attr">originalLanguage</span>: translation.detectedSourceLanguage,
        <span class="hljs-attr">originalMessage</span>: params.text,
        <span class="hljs-attr">translatedTo</span>: client.translateTo
      }

      client.send(<span class="hljs-built_in">JSON</span>.stringify(response));
    });

    res.status(<span class="hljs-number">200</span>).end();
  }

};
</code></pre>
<p>The <code>wss.clients.forEach</code> method iterates through each connection and sends off the SMS parameters from Nexmo to the Google Translate API. Once the translation comes back, we can decide what data the front-end should have, and pass it back as a string as I've done here with <code>client.send(JSON.stringify(response))</code>.</p>
<p>To recap what has happened here: Each client connects to the WebSocket server by calling the <code>/socket</code> route and establishing a connection. An SMS message goes from the sender's phone to Nexmo, which then calls the <code>/inboundSMS</code> route. The app passes the text message to Google Translate API for each connected client, and then finally sends it back to the client UI.</p>
<p><img src="/content/blog/real-time-sms-demo-with-react-node-and-google-translate/flow-diagram.png" alt="Diagram of WebSocket Flow" /></p>
<p>Next, let's build the UI parts to display it on the screen.</p>
<h2 id="heading-websockets-with-react">WebSockets with React</h2>
<p>With the WebSocket server running, we can move on to the display of the messages on screen. Since I enjoy using <a target="_blank" href="https://reactjs.org/">React</a>, and more importantly, <a target="_blank" href="https://reactjs.org/docs/hooks-intro.html">React Hooks</a>, I set out to locate something to help with connecting to WebSockets. Sure enough, I found one that fit my exact need.</p>
<p>The demo app UI is built with <a target="_blank" href="https://github.com/facebook/create-react-app"><code>create-react-app</code></a>, and I used the <a target="_blank" href="https://v2.grommet.io/">Grommet</a> framework. These topics are out of scope for this post, but you can grab my <a target="_blank" href="https://github.com/nexmo-community/sms-translation-demo-app">source code</a> and follow along.</p>
<h3 id="heading-connecting-to-the-websocket">Connecting to the WebSocket</h3>
<p>The first step here is to establish a connection and begin two-way communication. The module I found is <a target="_blank" href="https://github.com/robtaussig/react-use-websocket"><code>react-use-websocket</code></a>, and it made setting this up super simple.</p>
<pre><code class="lang-bash">npm install react-use-websocket
</code></pre>
<p>There are tons of these React hook libraries out there that help you create some impressive functionality in a short amount of time. In this instance, importing the module and setting up a couple of items for the configuration is all it took to get a connection.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> useWebSocket <span class="hljs-keyword">from</span> <span class="hljs-string">'react-use-websocket'</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> STATIC_OPTIONS = useMemo(<span class="hljs-function">() =&gt;</span> ({
    <span class="hljs-attr">shouldReconnect</span>: <span class="hljs-function">(<span class="hljs-params">closeEvent</span>) =&gt;</span> <span class="hljs-literal">true</span>,
  }), []);

  <span class="hljs-keyword">const</span> protocolPrefix = <span class="hljs-built_in">window</span>.location.protocol === <span class="hljs-string">'https:'</span> ? <span class="hljs-string">'wss:'</span> : <span class="hljs-string">'ws:'</span>;
  <span class="hljs-keyword">let</span> { host } = <span class="hljs-built_in">window</span>.location;
  <span class="hljs-keyword">const</span> [sendMessage, lastMessage, readyState] = useWebSocket(<span class="hljs-string">`<span class="hljs-subst">${protocolPrefix}</span>//<span class="hljs-subst">${host}</span>/socket`</span>, STATIC_OPTIONS);

  <span class="hljs-comment">//...</span>
}
</code></pre>
<p>In the component, we import the <code>useWebSocket</code> method to pass the WebSocket URL and the object <code>STATIC_OPTIONS</code> as the second argument. The <code>useWebSocket</code> method is a custom hook that returns the <code>sendMessage</code> method, <code>lastMessage</code> object from the server (which is our translated messages), and the <code>readyState</code> which is an integer to give us the status of the connection.</p>
<h3 id="heading-receiving-incoming-messages">Receiving Incoming Messages</h3>
<p>Once <code>react-use-websocket</code> makes the connection to the server, we can now start listening for messages from the <code>lastMessage</code> property. When receiving incoming messages from the server, they populate here and update the component. If your server has multiple message types, you discern that information here. Since we only have one, it's an easier implementation.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [messageHistory, setMessageHistory] = useState([]);

useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">if</span> (lastMessage !== <span class="hljs-literal">null</span>) {
    setMessageHistory(<span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> prev.concat(lastMessage))
  }
}, [lastMessage]);

<span class="hljs-keyword">return</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Main</span>&gt;</span>
    {messageHistory.map((message, idx) =&gt; {
      let msg = JSON.parse(message.data);
      return (
        <span class="hljs-tag">&lt;<span class="hljs-name">Box</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>From: {msg.from}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Heading</span> <span class="hljs-attr">level</span>=<span class="hljs-string">{2}</span>&gt;</span>{msg.translation}<span class="hljs-tag">&lt;/<span class="hljs-name">Heading</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Box</span>&gt;</span>
      )
    })}
  <span class="hljs-tag">&lt;/<span class="hljs-name">Main</span>&gt;</span></span>
)
</code></pre>
<p>The built-in hook <code>useEffect</code> runs every time the state is updated. When <code>lastMessage</code> is not null, it adds the new message to the end of the previous message state array, and the UI updates using the <code>map</code> function to render all of the messages. It is in the <code>messageHistory</code> where all of the JSON strings we passed from the server are stored. The main functionality of our WebSocket is complete, but I still want to add a few more items.</p>
<h3 id="heading-sending-messages-to-the-server">Sending Messages to the Server</h3>
<p>Since this is a translation demo, having more than one language is an excellent way to show the power of the Google Translate API in conjunction with Nexmo SMS messages. I created a dropdown with languages to pick. This dropdown is where bi-directional communication happens with the server, and the app sends the selected language from the client.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> languages = [
  { <span class="hljs-attr">label</span>: <span class="hljs-string">"English"</span>, <span class="hljs-attr">value</span>: <span class="hljs-string">"en"</span>},
  { <span class="hljs-attr">label</span>: <span class="hljs-string">"French"</span>, <span class="hljs-attr">value</span>: <span class="hljs-string">"fr"</span>},
  { <span class="hljs-attr">label</span>: <span class="hljs-string">"German"</span>, <span class="hljs-attr">value</span>: <span class="hljs-string">"de"</span>},
  { <span class="hljs-attr">label</span>: <span class="hljs-string">"Spanish"</span>, <span class="hljs-attr">value</span>: <span class="hljs-string">"es"</span>}
];

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Select</span>
  <span class="hljs-attr">labelKey</span>=<span class="hljs-string">"label"</span>
  <span class="hljs-attr">onChange</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">option</span> }) =&gt;</span> {
    sendMessage(option.value)
    setTranslateValue(option.label)
  }}
  options={languages}
  value={translateValue}
  valueKey="value"
/&gt;</span>
</code></pre>
<p>Here, the <code>sendMessage</code> function from <code>react-use-websocket</code> is how we can send information back to our server and consume it. This process is where the event handler we set up comes in handy from earlier. It is this dropdown that determines what language the Google Translate API translates the message into and displays on the screen.</p>
<h3 id="heading-connection-status-display">Connection Status Display</h3>
<p>Since this is a demo in a conference environment, I thought having a connectivity indicator would be a good idea. As long as the front-end remains connected to the WebSocket, the light displays green.  </p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> CONNECTION_STATUS_CONNECTING = <span class="hljs-number">0</span>;
<span class="hljs-keyword">const</span> CONNECTION_STATUS_OPEN = <span class="hljs-number">1</span>;
<span class="hljs-keyword">const</span> CONNECTION_STATUS_CLOSING = <span class="hljs-number">2</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Status</span>(<span class="hljs-params">{ status }</span>) </span>{
  <span class="hljs-keyword">switch</span> (status) {
    <span class="hljs-keyword">case</span> CONNECTION_STATUS_OPEN:
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span>Connected<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"led green"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;/&gt;</span></span>;
    <span class="hljs-keyword">case</span> CONNECTION_STATUS_CONNECTING:
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span>Connecting<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"led yellow"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;/&gt;</span></span>;
    <span class="hljs-keyword">case</span> CONNECTION_STATUS_CLOSING:
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span>Closing<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"led yellow"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;/&gt;</span></span>;
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span>Disconnected<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"led grey"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;/&gt;</span></span>;;
  }
}

<span class="hljs-comment">//....</span>
&lt;Status status={readyState} /&gt;
<span class="hljs-comment">//...</span>
</code></pre>
<p>The <code>Status</code> component uses the <code>readyState</code> to switch between the various statuses and indicates that to the user. If it turns red, you know something is wrong with the WebSocket server, and you should check into it.  </p>
<p>Once everything is up and running, it looks something like this:</p>
<p><img src="/content/blog/real-time-sms-demo-with-react-node-and-google-translate/using_translation_app.gif" alt="Animation of Working Demo App" /></p>
<h2 id="heading-try-it-out">Try It Out</h2>
<p>The <a target="_blank" href="https://github.com/kellyjandrews/sms-translation-demo-app">demo application code</a> is on our community GitHub organization, and you can try it out for yourself as well. I've created a README that should help you get through the setup and run it locally on your server or deploy it to Heroku. I've also provided a Dockerfile, if you'd prefer to go that route. Let me know what you think of it, and if you have any trouble, feel free to reach out and submit an issue on the repo.</p>
]]></content:encoded></item></channel></rss>