<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Timothy Olanrewaju</title>
    <description>The latest articles on DEV Community by Timothy Olanrewaju (@timothyolanrewaju).</description>
    <link>https://dev.to/timothyolanrewaju</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1119725%2Ff4906d35-2ed1-4299-bc5e-def9291b8b62.jpeg</url>
      <title>DEV Community: Timothy Olanrewaju</title>
      <link>https://dev.to/timothyolanrewaju</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/timothyolanrewaju"/>
    <language>en</language>
    <item>
      <title>The Case for OAuth</title>
      <dc:creator>Timothy Olanrewaju</dc:creator>
      <pubDate>Fri, 17 Oct 2025 14:16:07 +0000</pubDate>
      <link>https://dev.to/timothyolanrewaju/the-case-for-oauth-33l7</link>
      <guid>https://dev.to/timothyolanrewaju/the-case-for-oauth-33l7</guid>
      <description>&lt;p&gt;Every modern application that uses third-party services struggles with one key issue: sharing resources without compromising security. On one hand, functionality demands collaboration, but on the other hand, security models demand isolation. Users need to grant applications access to their resources across multiple services.&lt;/p&gt;

&lt;p&gt;However, traditional authentication models force developers to make a tough choice between security and functionality. The core problem is granting access to resources in a way that maintains security boundaries, limits privilege scope, and preserves user control.&lt;/p&gt;

&lt;p&gt;Picture this: Application A wants to access data in service B. Application A stores the credentials and gains full access equivalent to the user, with no limit on scope or access. Now, service B cannot distinguish whether the user is acting directly or Application A is acting on their behalf. This makes audit trails or user-specific rate limiting impossible.&lt;/p&gt;

&lt;p&gt;From this point, every additional service integration only increases these risks exponentially. With micro-services, API-first design, and sophisticated attacks, this model quickly became untenable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Look at Early Delegation Models
&lt;/h2&gt;

&lt;p&gt;As the industry evolved, some approaches were introduced to solve the challenge of authorization delegation. These early models successfully established token-based access and federated identity, which would later pave the way for a standardized framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Keys and Shared Secrets&lt;/strong&gt;: Token tied to a user improved over passwords, but still carried a persistent security risk due to the longevity and broad-scoped nature of the API keys. The only way to revoke access in this approach is by manual deletion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signed Requests and HMAC Authentication&lt;/strong&gt;: Cryptographic signatures could tell who was in possession of a secret key without transmitting it. However, they still operate on shared secrets that must be distributed and stored by client applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SAML and Enterprise SSO&lt;/strong&gt;: Focused on federated identity, Security Assertion Markup Language (SAML) enables single sign-on (SSO) by allowing users to access multiple applications with a single set of credentials. It successfully separated authentication from authorization and introduced token-based delegation, but it introduced layers of complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the Solution
&lt;/h3&gt;

&lt;p&gt;All of the early adopted models solved subsets of the problem. There was a need for a general-purpose delegation protocol that could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Work across security domains without sharing credentials.&lt;/li&gt;
&lt;li&gt;Support both interactive user flows and machine-to-machine communication.&lt;/li&gt;
&lt;li&gt;Enable ecosystem tooling with flexibility for different security requirements.&lt;/li&gt;
&lt;li&gt;Separate authentication from authorization.&lt;/li&gt;
&lt;li&gt;Provide granular scoping, enable revocation, and work across device types.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  OAuth: The Modern Authorization Protocol
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://auth0.com/intro-to-iam/what-is-oauth-2" rel="noopener noreferrer"&gt;OAuth&lt;/a&gt; is a standard designed to allow a website or application to access resources hosted by other web apps on behalf of a user. It provides consented access and restricts actions of what the client app can perform on resources on behalf of the user, without ever sharing the user’s credentials. Its strength lies in its ability to adapt to different security contexts while maintaining interoperability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Architecture Flow
&lt;/h3&gt;

&lt;p&gt;In the OAuth architecture, there are four roles that separate concerns unlike traditional authentication models.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Resource Owner&lt;/strong&gt;: Is the end user who owns resources. This role controls the authorization process by having the power to grant or deny access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt;: Is the application requesting access to resources on behalf of the resource owner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization Server&lt;/strong&gt;: Handles the authentication of the resource owner and provides tokens after gaining authorization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Server&lt;/strong&gt;: Serves as a host for the resources and validates access tokens to determine whether to fulfill requests or not.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This separation of roles brings about a crucial architectural shift where clients never handle user credentials. Instead, the authorization server authenticates the user, obtains their consent, and provides short-lived tokens that clients can use to access resources.​&lt;/p&gt;

&lt;h3&gt;
  
  
  OAuth In Full Flow
&lt;/h3&gt;

&lt;p&gt;Imagine you want to schedule an online meeting. The go-to choice is Calendly. But Calendly is just a meeting scheduler. For you to stay in the loop and get a reminder when the meeting is approaching, it needs to be added to your personal calendar (say, Google Calendar).&lt;/p&gt;

&lt;p&gt;Technically, Calendly needs controlled access to your Google Calendar, as it doesn’t own or manage it directly. It must be able to write new events and read free/busy slots on your personal calendar.&lt;/p&gt;

&lt;p&gt;With OAuth, instead of handing over your Google password to Calendly, it provides an efficient delegation mechanism:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inside Calendly, you connect your Google Calendar. The app redirects you to Google’s Authorization Server.&lt;/li&gt;
&lt;li&gt;Google presents you with a consent screen while authenticating directly using your existing account.&lt;/li&gt;
&lt;li&gt;If you approve, Google sends an authorization code to Calendly.&lt;/li&gt;
&lt;li&gt;Calendly then securely exchanges the authorization code with Google’s Token Endpoint using &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;client_secret&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Google responds with an Access Token.&lt;/li&gt;
&lt;li&gt;Now, when Calendly needs to fetch your calendar events, it appends the Access Token sent into the Authorization Bearer header to Google Calendar API requests.&lt;/li&gt;
&lt;li&gt;Finally, Google validates the token, checks if the scopes match and returns the requested data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuf8ju3fl5uxpu9652iz2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuf8ju3fl5uxpu9652iz2.png" alt="Diagram illustrating the OAuth authentication flow between Calendly and Google Calendar" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This exchange means Calendly only uses tokens with limited scope and life without ever accessing your Google Password.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of OAuth
&lt;/h3&gt;

&lt;p&gt;The success of OAuth stems from solving problems that follow modern systems architecture. It effectively solved the foundational problems of delegated authorization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Credential Isolation&lt;/strong&gt;: Users never share credentials with third-party applications. This reduces the attack surface drastically and limits the risk of client vital information being compromised. When there is a breach, attackers only gain access tokens with a short life span.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Granular Authorization&lt;/strong&gt;: Applications only receive the permissions they need. Users can grant read-only access or limit access. Individual clients are not affected when access to others is revoked. It follows the Principle of Least Privilege (PoLP).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Revocation Without Disruption&lt;/strong&gt;: When a user suspects their account is compromised, they can review all authorized applications and revoke suspicious ones. This can be done without changing passwords or affecting legitimate applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audit and Visibility&lt;/strong&gt;: Service operators can track activities carried out by a user, such as API usage, abuse patterns etc. This is made possible because every token-based request carries information about the client.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third-Party Ecosystem Boom&lt;/strong&gt;: Startups can integrate with major platforms without going through custom authentication schemes. Libraries and frameworks can now provide general-purpose OAuth clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations and Challenges of OAuth
&lt;/h3&gt;

&lt;p&gt;OAuth’s flexibility and general-purpose model come with limitations that you must understand. Implementation complexity stands as the biggest limitation. With the amount of work needed to implement multiple grant types, manage token lifecycle, handle consent flows etc, very few organizations can invest in such an architecture.&lt;/p&gt;

&lt;p&gt;OAuth, being an authorization framework not an authentication protocol, brings up concerns about identifying the user. Receiving access tokens only tells you what the client is allowed to do. To identify the user, other layers have to be added, like using OpenID Connect and examining user info endpoints.&lt;/p&gt;

&lt;p&gt;Refresh tokens are also a point of concern. Refresh tokens must be long-lived to provide a good user experience, but such longevity leaves it prone to attacks. Shortening or rotation of the token would increase implementation complexity if that route is taken.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical Considerations for Implementation
&lt;/h3&gt;

&lt;p&gt;We’ve covered the numerous benefits of the OAuth framework in applications. Adoption is a different ball game, and for it to be successful, you must pay attention to several key factors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Proven Libraries&lt;/strong&gt;: Building an OAuth system from scratch is complex, making custom implementations is risky too. Therefore, opting for commercial solutions like Auth0, Okta, or KeyCloak is a smarter choice. For clients, use well-maintained libraries like AppAuth for mobile, Passport.js for Node, or Spring Security for Java.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design Good Scopes&lt;/strong&gt;: Your application scope should be from a user perspective. Each scope should represent a clear permission that users can understand and evaluate. “Read your profile” is more user-centric than “api.read”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token Lifetime Balance&lt;/strong&gt;: You should ensure your access tokens are short-lived, preferably 15 minutes to 1 hour. Refresh tokens can live longer but should expire or rotate at intervals. For high-security applications like bank apps, refresh tokens should be rotated on every use. The decision is up to you. Evaluate your risk tolerance and UX needs and tweak accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor Activites&lt;/strong&gt;: It is essential to keep track of failed authorization attempts, unusual token usage patterns, token validation failures etc. Build dashboards that display an overview of which clients are consuming which API with what permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test, Test and Test&lt;/strong&gt;: As a custodian of your application, you are responsible for authorization code interception, token replay, scope escalation, and CSRF attacks against your implementation. Use tools like OAuth 2.0 Playground or Postman to simulate various attack scenarios. Also, remember to include security testing in your CI/CD pipeline.&lt;/p&gt;

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

&lt;p&gt;OAuth represents a pivotal shift in how we think and implement authorization in software systems. By aligning with modern ways of app communication, you can tap into the security and convenience that OAuth provides. With a secured authorization architectural flow across services, this model fits naturally.&lt;/p&gt;

&lt;p&gt;OAuth should be viewed as an infrastructure rather than as a feature to implement. It is a foundational layer that other security properties depend on. The question isn’t whether to use OAuth, but how to deploy it appropriately within your organization.&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>security</category>
    </item>
    <item>
      <title>Build a Chat to Slack Integration for Real-Time Feedback</title>
      <dc:creator>Timothy Olanrewaju</dc:creator>
      <pubDate>Fri, 29 Aug 2025 08:11:23 +0000</pubDate>
      <link>https://dev.to/getstreamhq/build-a-chat-to-slack-integration-for-real-time-feedback-27eg</link>
      <guid>https://dev.to/getstreamhq/build-a-chat-to-slack-integration-for-real-time-feedback-27eg</guid>
      <description>&lt;p&gt;To build great products, start by listening. Everything matters. Every bug report, feature request, or glowing review. These are the signals that fuel your &lt;a href="https://getstream.io/blog/product-roadmap-2025/" rel="noopener noreferrer"&gt;product roadmap&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;However, standing between success and failure is the pain of managing and routing that feedback to the right teams.&lt;/p&gt;

&lt;p&gt;According to a Zendesk &lt;a href="https://cxtrends.zendesk.com/" rel="noopener noreferrer"&gt;report&lt;/a&gt;, 59% of support teams are overwhelmed. Feedback pours in from multiple channels, often causing delays and leaving users feeling ignored. Support teams want to help; they try to, but the fragmented channels jeopardize their efforts. When this happens, nobody wins.&lt;/p&gt;

&lt;p&gt;What if feedback didn’t float around in limbo or reside in silos but instead went straight to the people who can fix the problem—right inside Slack. Slack is a well-known and widely recognized workspace tool for developers, project managers, and support teams. And when feedback is sent to a dedicated Slack channel, it’s more likely to be seen, fast.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will implement a feedback system that uses &lt;a href="https://getstream.io/chat/" rel="noopener noreferrer"&gt;Stream Chat&lt;/a&gt; to gather user thoughts and sends them directly into Slack. &lt;/p&gt;

&lt;p&gt;Here’s a quick look at what we’ll build: &lt;a href="https://drive.google.com/file/d/1Yo3B3lefY8r0BWtUr8qGkzI-Em8dI3UK/view?usp=sharing" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of the code for this tutorial can be found in this &lt;a href="https://github.com/TimothyOlanrewaju/StreamChat-Slack" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://getstream.io/accounts/login/" rel="noopener noreferrer"&gt;free Stream account&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://slack.com/" rel="noopener noreferrer"&gt;Slack account&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://Node.js" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; and npm installed.&lt;/li&gt;
&lt;li&gt;Basic knowledge of React.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create Your Slack App
&lt;/h2&gt;

&lt;p&gt;We need to create a Slack app to send messages from Stream Chat to Slack. This app lets your code securely talk to Slack’s API and send feedback to a specific channel. Consider it the connection point between your Stream Chat messages and your team’s Slack workspace.&lt;/p&gt;

&lt;p&gt;A Slack app enables you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure an API token.&lt;/li&gt;
&lt;li&gt;Have your application post messages.&lt;/li&gt;
&lt;li&gt;Control which channels or data your app can access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s walk through the steps to set up your Slack app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Slack’s API &lt;a href="https://api.slack.com/apps/" rel="noopener noreferrer"&gt;portal&lt;/a&gt; and click the “Create New App” button.&lt;/li&gt;
&lt;li&gt;From the resulting pop-up, click on the “From Scratch” option.&lt;/li&gt;
&lt;li&gt;Choose an app name and the workspace in which you’d like to develop the app.&lt;/li&gt;
&lt;li&gt;Click on “Create App”.&lt;/li&gt;
&lt;li&gt;Once your app is created, go to the &lt;strong&gt;OAuth &amp;amp; Permissions&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Scopes&lt;/strong&gt;, add the following &lt;strong&gt;Bot Token Scopes&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;chat:write&lt;/strong&gt; - to allow the app to send messages to channels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;channels:read&lt;/strong&gt; - if you want to post to public channels&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;OAuth Tokens&lt;/strong&gt; section, click on “Install to {{workspace_name}}”.&lt;/li&gt;
&lt;li&gt;On the next auth page, click on “Allow”.&lt;/li&gt;
&lt;li&gt;You’ll get a &lt;strong&gt;Bot User OAuth Token&lt;/strong&gt;. Copy the token and save it somewhere safe.&lt;/li&gt;
&lt;li&gt;Enter the workspace you integrated with your Slack app and create a channel.&lt;/li&gt;
&lt;li&gt;Click on the channel. Copy the last part on your browser’s URL, usually prefixed with &lt;strong&gt;C&lt;/strong&gt; (e.g, C092WJFRJGZ). That is your channel ID.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;We create and set up our React project with the &lt;a href="https://getstream.io/chat/sdk/react/" rel="noopener noreferrer"&gt;Stream React Chat SDK&lt;/a&gt;. We'll use Vite with the TypeScript template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite user-feedback-collector -- --template react-ts
cd user-feedback-collector
npm i stream-chat stream-chat-react axios
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we set up a Node.js backend by creating a &lt;strong&gt;server&lt;/strong&gt; directory in the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir server
cd server
npm init -y
npm install express @slack/web-api dotenv cors 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we set up our environment variables: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;server&lt;/strong&gt; directory, create a &lt;strong&gt;.env&lt;/strong&gt; file and provide the necessary credentials:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLACK_BOT_TOKEN=your_slack-bot_token
SLACK_CHANNEL_ID=your_channel_id
STREAM_API_KEY=your_stream_api_key
STREAM_API_SECRET=your_stream_api_secret
PORT=5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In the project’s root, create a &lt;strong&gt;.env&lt;/strong&gt; file and provide your Stream API key:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; VITE_STREAM_API_KEY=your_stream_api_key
 VITE_API_URL=http://localhost:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The Node.js and Express stack handles server-side logic and handles HTTP requests within this application for real-time feedback processing. &lt;/p&gt;

&lt;h3&gt;
  
  
  Server Initialization and Configuration
&lt;/h3&gt;

&lt;p&gt;At startup, the server checks for critical environmental variables. These variables are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SLACK_BOT_TOKEN&lt;/li&gt;
&lt;li&gt;STREAM_API_KEY&lt;/li&gt;
&lt;li&gt;STREAM_API_SECRET &lt;/li&gt;
&lt;li&gt;SLACK_CHANNEL_ID. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The check is important as it ensures a secure and reliable operation. If any are missing, the server terminates to prevent incorrect configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// server/index.js

const requiredEnvVars = [
  'SLACK_BOT_TOKEN',
  'STREAM_API_KEY',
  'STREAM_API_SECRET',
  'SLACK_CHANNEL_ID'
];

const missingEnvVars = requiredEnvVars.filter(varName =&amp;gt; !process.env[varName]);
if (missingEnvVars.length &amp;gt; 0) {
  console.error('Missing required environment variables:', missingEnvVars.join(', '));
  process.exit(1);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, the backend establishes critical integrations with Stream Chat and Slack. &lt;/p&gt;

&lt;h3&gt;
  
  
  Slack and Stream Client Initialization
&lt;/h3&gt;

&lt;p&gt;A Slack Web API Client authenticates with a bot token to enable real-time feedback to a designated Slack Channel. We must also create a Stream Chat client using Stream’s API credentials to manage users, channels, and messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// server/index.js

const { WebClient } = require('@slack/web-api');
const { StreamChat } = require('stream-chat');

const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
const streamClient = StreamChat.getInstance(process.env.STREAM_API_KEY, process.env.STREAM_API_SECRET);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stream Chat Integration
&lt;/h3&gt;

&lt;p&gt;The integral aspect of this system relies on &lt;a href="https://getstream.io/chat/" rel="noopener noreferrer"&gt;Stream Chat&lt;/a&gt;. Each customer is given a unique ID (e.g., &lt;code&gt;customer-&amp;lt;id&amp;gt;&lt;/code&gt;) and a dedicated messaging channel (e.g., &lt;code&gt;feedback-&amp;lt;customerId&amp;gt;&lt;/code&gt;). We’ll also have a support-bot user, initialized as an admin, and participating in every channel to send automated responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// server/index.js

async function createStreamUser(userId, name, role = 'user') {
  try {
    await streamClient.upsertUser({
      id: userId,
      name: name || `Customer ${userId}`,
      role: role,
      image: role === 'admin' ? 'https://via.placeholder.com/40x40.png?text=BOT' : 'https://via.placeholder.com/40x40.png?text=USER'
    });
    return true;
  } catch (error) {
    return false;
  }
}


// Initialize support bot user
async function initializeSupportBot() {
  try {
    await streamClient.upsertUser({
      id: 'support-bot',
      name: 'Support Team',
      role: 'admin',
      image: 'https://via.placeholder.com/40x40.png?text=BOT'
    });
    return true;
  } catch (error) {
    return false;
  }
}


// Get or create customer channel
async function getOrCreateCustomerChannel(customerId) {
  try {
    const channelId = `feedback-${customerId}`;

    const channel = streamClient.channel('messaging', channelId, {
      name: `Feedback from ${customerId}`,
      custom: {
        type: 'feedback',
        customerId: customerId
      },
      members: [customerId, 'support-bot']
    });


    await channel.create();
    return channel;
  } catch (error) {
    return null;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Feedback Submission
&lt;/h3&gt;

&lt;p&gt;The feedback submission process is the central workflow, orchestrated by the &lt;code&gt;/api/feedback/submit&lt;/code&gt; endpoint. When a customer submits feedback, the backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validates the input (&lt;code&gt;customerId&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Creates a Stream Chat user for the customer if they don’t exist.&lt;/li&gt;
&lt;li&gt;Establishes or retrieves a dedicated feedback channel for the customer.&lt;/li&gt;
&lt;li&gt;Send the customer’s message to the channel.&lt;/li&gt;
&lt;li&gt;Follows up with an automated support-bot response.&lt;/li&gt;
&lt;li&gt;Posts a notification to Slack with the feedback details.
This flow ensures real-time communication and enables visibility for both parties.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/api/feedback/submit', async (req, res) =&amp;gt; {
  const { customerId, message } = req.body;

  try {
    if (!customerId || !message) {
      return res.status(400).json({
        error: 'Missing required fields',
        required: ['customerId', 'message']
      });
    }


    const messageText = typeof message === 'string' ? message : String(message);
    const formattedCustomerId = customerId.startsWith('customer-')
      ? customerId
      : `customer-${customerId}`;


    // Create customer user
    const customerCreated = await createStreamUser(
      formattedCustomerId,
      `Customer ${customerId}`,
      'user'
    );


    if (!customerCreated) {
      return res.status(500).json({ error: 'Failed to create customer user' });
    }


    // Get or create channel
    const channel = await getOrCreateCustomerChannel(formattedCustomerId);
    if (!channel) {
      return res.status(500).json({ error: 'Failed to create channel' });
    }


    // Send customer message
    await channel.sendMessage({
      text: messageText,
      user_id: formattedCustomerId,
      custom: {
        type: 'customer_message',
        timestamp: new Date().toISOString()
      }
    });


    // Send bot response
    await channel.sendMessage({
      text: 'Thank you for your feedback! Our team will review and respond soon.',
      user_id: 'support-bot',
      custom: {
        type: 'bot_response',
        timestamp: new Date().toISOString()
      }
    });


    // Send to Slack
    try {
      await slack.chat.postMessage({
        channel: process.env.SLACK_CHANNEL_ID,
        text: `💬 *New Chat Message*\n*From:* ${formattedCustomerId}\n*Message:* ${messageText}`
      });
    } catch (slackError) {
      // Don't fail the request if Slack fails
    }


    res.status(200).json({
      success: true,
      message: 'Message sent successfully',
      customerId: formattedCustomerId,
      channelId: `feedback-${formattedCustomerId}`
    });


  } catch (error) {
    res.status(500).json({
      error: 'Failed to submit message',
      details: error.message
    });
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend provides additional endpoints to support the application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;/api/health&lt;/strong&gt;: Checks connectivity with Stream Chat and Slack, ensuring system health.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;/api/stream-token&lt;/strong&gt;: Generates authentication tokens for Stream Chat clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Run the Server
&lt;/h3&gt;

&lt;p&gt;The application runs on port 5000 by default. Run the server by entering this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The frontend of this application aims to deliver a responsive, real-time chat interface for users to submit feedback using Stream Chat’s React SDK. It  works hand-in-hand with the backend to authenticate users, manage sessions, and display a toggleable chat widget located at the bottom right of the web page.&lt;/p&gt;

&lt;h3&gt;
  
  
  The App Component
&lt;/h3&gt;

&lt;p&gt;Like all React applications, the App component serves as the root. It is where we initialize the Stream Chat React SDK using the &lt;code&gt;StreamChat.getInstance&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const client = StreamChat.getInstance(import.meta.env.VITE_STREAM_API_KEY);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  State Initialization and User Session
&lt;/h3&gt;

&lt;p&gt;The App component manages state using the &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useEffect&lt;/code&gt; hooks to handle connection status, loading states, and widget visibility. A unique &lt;code&gt;currentUserId&lt;/code&gt; is generated for each chat session with a combination of timestamp and random string, ensuring distinct user identification. &lt;br&gt;
We use the &lt;code&gt;connectionError&lt;/code&gt;, &lt;code&gt;isLoading&lt;/code&gt;, and &lt;code&gt;isWidgetOpen&lt;/code&gt; states to track the chat connection, initialization, and widget visibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/App.tsx

import { useState, useEffect, useCallback } from 'react';
import { useCreateChatClient, Chat } from 'stream-chat-react';
import './App.css';
import ChatWidget from './components/ChatWidget';


function App() {
  const [connectionError, setConnectionError] = useState&amp;lt;string | null&amp;gt;(null);
  const [isLoading, setIsLoading] = useState(true);
  const [isWidgetOpen, setIsWidgetOpen] = useState(false);

  const [currentUserId] = useState(() =&amp;gt; {
    const timestamp = Date.now().toString(36);
    const randomStr = Math.random().toString(36).substr(2, 5);
    return `customer-${timestamp}-${randomStr}`;
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connecting to Stream Chat
&lt;/h3&gt;

&lt;p&gt;The connection to Stream Chat is established using the &lt;code&gt;useCreateChatClient&lt;/code&gt; hook with a token provider function. The connection process performs the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defines the &lt;code&gt;tokenProvider&lt;/code&gt; function that checks the backend health via &lt;code&gt;/api/health/&lt;/code&gt; endpoint.&lt;/li&gt;
&lt;li&gt;Requests a Stream Chat token from the backend’s &lt;code&gt;/api/stream-token&lt;/code&gt; endpoint using the generated &lt;code&gt;currentUserId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Initializes the Stream Chat client using the &lt;code&gt;useCreateChatClient&lt;/code&gt; hook with API key, token, provider, and user data.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/App.tsx

const tokenProvider = useCallback(async () =&amp;gt; {
    try {
      const apiUrl = getApiUrl();
      const healthResponse = await fetch(`${apiUrl}/api/health`);
      if (!healthResponse.ok) {
        throw new Error(`Backend health check failed: ${healthResponse.status}`);
      }


      const response = await fetch(`${apiUrl}/api/stream-token`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: JSON.stringify({ userId: currentUserId }),
      });


      if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`Failed to get Stream token: ${response.status} - ${errorText}`);
      }


      const data = await response.json();

      if (!data.token) {
        throw new Error('No token received from backend');
      }


      return data.token;
    } catch (error) {
      console.error('Token provider error:', error);
      throw error;
    }
  }, [currentUserId]);


  const user = {
    id: currentUserId,
    name: `Customer ${currentUserId.split('-')[1]}`,
  };


  const client = useCreateChatClient({
    apiKey: import.meta.env.VITE_STREAM_API_KEY,
    tokenOrProvider: tokenProvider,
    userData: user,
  });


  useEffect(() =&amp;gt; {
    if (client) {
      setIsLoading(false);
      setConnectionError(null);


      const handleConnectionError = (error: any) =&amp;gt; {
        console.error('Stream connection error:', error);
        setConnectionError(`Connection failed: ${error.message}`);
      };


      client.on('connection.error', handleConnectionError);

      return () =&amp;gt; {
        client.off('connection.error', handleConnectionError);
      };
    }
  }, [client]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rendering the User Interface
&lt;/h3&gt;

&lt;p&gt;Our UI would include a toggle button that controls the visibility of the &lt;strong&gt;ChatWidget&lt;/strong&gt; component, which renders the Stream Chat interface. During initialization, a loading screen displays the &lt;code&gt;currentUserId&lt;/code&gt; and &lt;code&gt;API URL&lt;/code&gt;. Once the user is connected to Stream Chat, the ChatWidget is conditionally rendered when &lt;code&gt;isWidgetOpen&lt;/code&gt; is true, ensuring a clean and non-intrusive user experience.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  if (isLoading) {
    return (
      &amp;lt;div className="loading-container"&amp;gt;
        &amp;lt;h3&amp;gt;Connecting to chat service...&amp;lt;/h3&amp;gt;
        &amp;lt;p&amp;gt;User ID: {currentUserId}&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;API: {getApiUrl()}&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }


  return (
    &amp;lt;Chat client={client} theme="messaging light"&amp;gt;
      &amp;lt;div className="app-container"&amp;gt;
        &amp;lt;header className="app-header"&amp;gt;
          &amp;lt;h1&amp;gt;Welcome to User Feedback Collector&amp;lt;/h1&amp;gt;
          &amp;lt;h3 className="session-id"&amp;gt;Your session ID: {currentUserId}&amp;lt;/h3&amp;gt;
          &amp;lt;h3 className="connection-status"&amp;gt;✅ Connected to chat service&amp;lt;/h3&amp;gt;
        &amp;lt;/header&amp;gt;


        &amp;lt;button
          className="chat-toggle-button"
          onClick={toggleWidget}
          data-state={isWidgetOpen ? 'open' : 'closed'}
          aria-label={isWidgetOpen ? 'Close chat widget' : 'Open chat widget'}
        &amp;gt;&amp;lt;/button&amp;gt;


        {isWidgetOpen &amp;amp;&amp;amp; (
          &amp;lt;div className="chat-widget-container"&amp;gt;
            &amp;lt;ChatWidget customerId={currentUserId} /&amp;gt;
          &amp;lt;/div&amp;gt;
        )}
      &amp;lt;/div&amp;gt;
    &amp;lt;/Chat&amp;gt;
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The ChatWidget Component
&lt;/h3&gt;

&lt;p&gt;The ChatWidget is at the centre of our front-end real-time chat interface. It serves as the user-facing component working with Stream Chat and Slack. It renders a fully interactive chat window where customers can submit feedback and receive automated responses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Component Setup and Context
&lt;/h3&gt;

&lt;p&gt;This ChatWidget component receives a &lt;code&gt;customerId&lt;/code&gt; prop, identifying the user interacting with the chat. It leverages Stream Chat's &lt;code&gt;useChatContext&lt;/code&gt; hook and automated channel management to handle the chat interface and message routing. The component performs the following operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieves the Stream Chat client from the chat context using &lt;code&gt;useChatContext&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Creates a channel instance using &lt;code&gt;useMemo&lt;/code&gt; with a formatted customer ID.&lt;/li&gt;
&lt;li&gt;Defines channel members as the customer and a support-bot for automated responses.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/components/ChatWidget.tsx

const ChatWidget: React.FC&amp;lt;ChatWidgetProps&amp;gt; = ({ customerId }) =&amp;gt; {
  const { client } = useChatContext();


  const channel = useMemo(() =&amp;gt; {
    if (!client || !customerId) return null;


    const formattedCustomerId = customerId.startsWith('customer-')
      ? customerId
      : `customer-${customerId}`;


    const channelId = `feedback-${formattedCustomerId}`;

    return client.channel('messaging', channelId, {
      members: [formattedCustomerId, 'support-bot'],
    });
  }, [client, customerId]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Message Submission and Backend Integration
&lt;/h3&gt;

&lt;p&gt;The ChatWidget overrides the default message submission behavior of Stream Chat’s &lt;strong&gt;MessageInput&lt;/strong&gt; component with a custom &lt;code&gt;handleMessageSubmit&lt;/code&gt; function. &lt;br&gt;
This function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensures the message the user enters is a non-empty string, preventing invalid submissions.&lt;/li&gt;
&lt;li&gt;Constructs a payload with the &lt;code&gt;formattedCustomerId&lt;/code&gt; and message text.&lt;/li&gt;
&lt;li&gt;Logs API or network errors without disrupting the UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This integration is important as it ensures messages are processed through the backend, enabling features like Slack notifications and automated bot responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const handleMessageSubmit = async (params: {
    cid: string;
    localMessage: any;
    message: any;
    sendOptions: any;
  }) =&amp;gt; {    
    const messageText = params.message.text;


    if (!messageText || typeof messageText !== 'string' || !messageText.trim()) {
      return;
    }


    const formattedCustomerId = customerId.startsWith('customer-')
      ? customerId
      : `customer-${customerId}`;


    const payload = {
      customerId: formattedCustomerId,
      message: messageText.trim(),
    };


    try {
      const response = await fetch(`${import.meta.env.VITE_API_URL}/api/feedback/submit`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(payload),
      });


      if (!response.ok) {
        console.error('API Error:', response.status);
      }
    } catch (error) {
      console.error('Network error:', error);
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rendering User Interface and Stream Chat Components
&lt;/h3&gt;

&lt;p&gt;The ChatWidget renders a fully functional chat interface using Stream Chat’s pre-built components: &lt;strong&gt;Channel&lt;/strong&gt;, &lt;strong&gt;Window&lt;/strong&gt;, &lt;strong&gt;ChannelHeader&lt;/strong&gt;, &lt;strong&gt;MessageList&lt;/strong&gt;​​, and &lt;strong&gt;MessageInput&lt;/strong&gt;. &lt;br&gt;
The MessageInput component has the &lt;code&gt;handleMessageSubmit&lt;/code&gt; function passed to its &lt;code&gt;overrideSubmitHandler&lt;/code&gt; prop; enabling the MessageInput component to send the user-entered message to the chat and also to the backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; if (!channel) {
    return (
      &amp;lt;div className="chat-widget chat-widget--error"&amp;gt;
        Failed to load chat
      &amp;lt;/div&amp;gt;
    );
  }


  return (
    &amp;lt;div className="chat-widget"&amp;gt;
      &amp;lt;Channel channel={channel}&amp;gt;
        &amp;lt;Window&amp;gt;
          &amp;lt;ChannelHeader /&amp;gt;
          &amp;lt;MessageList messageLimit={100} /&amp;gt;
          &amp;lt;MessageInput
            overrideSubmitHandler={handleMessageSubmit}
          /&amp;gt;
        &amp;lt;/Window&amp;gt;
      &amp;lt;/Channel&amp;gt;
    &amp;lt;/div&amp;gt;
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run the Frontend
&lt;/h3&gt;

&lt;p&gt;Enter this command to run the frontend:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Having completed the implementation of the feedback system with Stream React Chat SDK and Slack, you now have a streamlined system that collects user feedback in real-time and sends it directly to your team. This system makes feedback management easy and channels it in a single hub, ensuring it reaches decision-makers quickly.&lt;/p&gt;

&lt;p&gt;What’s next? Keep building with these recommended tutorials: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://getstream.io/blog/job-app-interview-platform/" rel="noopener noreferrer"&gt;Build a Job Application and Interview Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://getstream.io/blog/react-assistant/" rel="noopener noreferrer"&gt;Build an AI Assistant with React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://getstream.io/blog/build-facebook-messenger-clone/" rel="noopener noreferrer"&gt;Build a Facebook Messenger Clone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding. 🎉&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Developers' Missing Piece for Boosting Productivity</title>
      <dc:creator>Timothy Olanrewaju</dc:creator>
      <pubDate>Mon, 17 Jun 2024 14:22:32 +0000</pubDate>
      <link>https://dev.to/timothyolanrewaju/the-developers-missing-piece-for-boosting-productivity-1kh3</link>
      <guid>https://dev.to/timothyolanrewaju/the-developers-missing-piece-for-boosting-productivity-1kh3</guid>
      <description>&lt;p&gt;As a front-end engineer and technical writer, my work life revolves around crafting interactive, responsive and dynamic User Interfaces and writing about tech. I am always eager to learn new things and share my views about them through writing. About two weeks ago, I came across two different posts talking about Pieces while scrolling on X, my curiosity kicked in and I decided to take a look at what it was all about. Since that day, I can say that my development productivity levels have sky-rocketed 🚀. &lt;/p&gt;

&lt;p&gt;Productivity is crucial for every developer as we want to complete tasks within a set timeframe to either beat a deadline, complete a project or have time to do some other things. Helping my fellow developers out is the reason why I decided to write this piece 😉&lt;/p&gt;

&lt;p&gt;In this article, I will be reviewing the Pieces for Developers tool and how it can boost your output levels just like it did mine. Walk with me!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Pieces&lt;/strong&gt;&lt;br&gt;
Pieces is an AI-powered productivity tool built to streamline developers' workflow activities. I like to call it a Hub of streamlined workflow, seamlessly integrating essential aspects of a programmer's development process. &lt;br&gt;
Before I started using Pieces, my code snippets, screenshots and general workflow activities tracking were all over the place. I used Notion, sometimes Notepad and I always leave many tabs open on my browser, so I can visit those specific webpages housing code snippets when I need them. You can imagine how long it takes to trace a particular code snippet and how hectic it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Awesome Pieces  functionalities
&lt;/h2&gt;

&lt;p&gt;One important thing to note as we dive into the incredible world of Pieces is that it is a developer-focused tool - tailored specifically to assist developers. &lt;br&gt;
Pieces has many cool and useful functionalities. However, in this section, I will focus on the aspects that blow my mind and I use quite often.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Robust Snippet Management&lt;/strong&gt;&lt;br&gt;
Pieces offers a snippet management functionality that enables you to save a bunch of code snippets from both your browser and IDE to the Desktop App.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ud823ntcdklio4rul2f.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ud823ntcdklio4rul2f.PNG" alt="Full snippet view" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
 The Desktop app stores all saved snippets which can be accessed at any time and also keeps track of workflow activities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; After saving some code snippets to Pieces, I decided to edit by adding a comment to it and later on, deleted the snippet. When you navigate to Workflow Activity, you will be able to view all actions performed on your snippets. This enables you to keep track of your snippets. Imagine, a snippet was deleted by mistake, all you need to do is to view your Workflow Activity and get an idea of what happened.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5dfe1ddr11o2y0btuur.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5dfe1ddr11o2y0btuur.PNG" alt="Workflow Activity view" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shareable Snippet Link&lt;/strong&gt;&lt;br&gt;
One cool thing about how Pieces manages snippets is, you can generate a link that can be shared with colleagues or anyone on communication channels like GChat, Teams, Slack and emails to access your code. &lt;/p&gt;

&lt;p&gt;To generate a shareable link on Pieces, &lt;/p&gt;

&lt;p&gt;First, click on Generate Shareable Link icon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib66qxnz277g6vlckr71.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib66qxnz277g6vlckr71.PNG" alt="Image showing shareable link icon" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, copy your personal link after generation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd9ftd1u1xlwtzg2mq18h.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd9ftd1u1xlwtzg2mq18h.PNG" alt="Personal shareable link generation" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now share this link with whoever you wish to.&lt;br&gt;
&lt;strong&gt;NB:&lt;/strong&gt; You only generate a snippet link once. The next time you want to have access to the link, it would be attached to the Context Preview of your saved snippet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Context&lt;/strong&gt;&lt;br&gt;
This is a powerful and versatile copilot feature that intrigues me the most and I know you would find it interesting too. Pieces Live Context can scan across your system and be aware of what is going on. It helps you remember anything and interacts with everything. It captures all context gathered from your system, processes and stores them entirely on-device. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; I want to get my work day started and I lost track of where I stopped the previous day, that is where  Live Context can come into play.&lt;br&gt;
To use Live Context, you need to switch it on before you proceed to prompt the copilot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8namsptn30k6dy4zvo9g.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8namsptn30k6dy4zvo9g.PNG" alt="Live contexting" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Already, you get some Suggested Prompt options including a prompt of where we left off. Just a click and it lists the last actions you performed!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwgn4kkbdwgg0c9lpiszh.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwgn4kkbdwgg0c9lpiszh.PNG" alt="Suggested Live contexting prompt" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;br&gt;
You can also manually set Context for the copilot to read files, folders, websites, snippets or messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; I was given an assignment to revamp the web pages of a particular project so, I cloned the Github repository to my local machine. It was a huge Laravel project with many folders and I did not have a clue as to where the user view was located. Aha! I do have a tool that can help me out in that regard. Up steps Pieces.&lt;br&gt;
All I need to do is to set the Context to Folders and add the folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro0inhpgfem6tx39mkfd.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro0inhpgfem6tx39mkfd.PNG" alt="Image showing both ways of adding folder to context" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, I proceeded to ask the copilot for the user view in the project folder. It took some time but it came back with an answer correctly pointing to the location of the file which is &lt;strong&gt;resources &amp;gt; views &amp;gt; user&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsnbrklrxnbyyvmtd0y1.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsnbrklrxnbyyvmtd0y1.PNG" alt="Context showing location of user in the folder" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Synchronized Workflow&lt;/strong&gt;&lt;br&gt;
My complete Pieces suite consists of the Desktop app,  Visual Studio Code extension and a Chromium Browser extension (Google Chrome). When saving snippets in my Chrome browser, they get added to your Saved Materials in the Desktop app in real time which keeps your work flowing nicely and smoothly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On-Device Large Language Models (LLMs)&lt;/strong&gt;&lt;br&gt;
On the 4th of June, the famous ChatGPT was down and the whole world was talking about it. People who heavily relied on the Generative AI tool were left stranded, but with an LLM locally deployed in your machine, you will not have to worry about such occurrences. Pieces offer LLMs that can reside on your computer system which enables you to go about your development activities without having to be online.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuaxviwv62vy1tyh00npw.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuaxviwv62vy1tyh00npw.PNG" alt="List of on-device LLMs" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, I love the fact that Pieces copilot is readily available on my Visual Studio Code where I do all my coding and also on my Chrome browser where I carry out my research. This makes it possible for me to carry the same copilot conversation across all these tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where Pieces Can Improve&lt;/strong&gt;&lt;br&gt;
The response time of the copilot when using Live Context.  When using Live Context for huge folders, the delay is understandable as it is going through a lot of files/folders. But, for more straightforward Live Context prompting, the response should be delivered much faster.&lt;br&gt;
The related links attached to saved snippets ironically links some unrelated web pages.&lt;/p&gt;

&lt;p&gt;Overall, I am impressed by the idea behind the development of this software. The Pieces team took into consideration the complexity of a programmer's development process and built a software that streamlined the workflow.&lt;/p&gt;

&lt;p&gt;And can I also say that the team at Pieces are amazing. Always willing to answer questions and help you resolve any issues you encounter. Kudos to the Pieces Team!&lt;/p&gt;

&lt;p&gt;Having had early access to the stable and soon-to-be-released features, I can say that Pieces is here to stay for a long time and will grow as developers seek workflow organization.&lt;/p&gt;

&lt;p&gt;Want to try it out?&lt;br&gt;
You can download Pieces for free &lt;a href="https://pieces.app/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have any questions about Pieces, leave them in the comment section.&lt;/p&gt;

&lt;p&gt;You can also connect with me on &lt;a href="https://www.linkedin.com/in/timothy-olanrewaju750/"&gt;LinkedIn&lt;/a&gt;for software-related posts and articles.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>ai</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>How to generate AI images using ChatGPT-3.5 (free)</title>
      <dc:creator>Timothy Olanrewaju</dc:creator>
      <pubDate>Tue, 21 May 2024 10:32:32 +0000</pubDate>
      <link>https://dev.to/timothyolanrewaju/how-to-generate-ai-images-in-chatgpt-35-free-4fng</link>
      <guid>https://dev.to/timothyolanrewaju/how-to-generate-ai-images-in-chatgpt-35-free-4fng</guid>
      <description>&lt;p&gt;With the emergence and popularity of Artificial Intelligence, various tools have sprung up making certain tasks easier to accomplish. ChatGPT is one of the most widely used tool used for various reasons from research to coding purposes.&lt;/p&gt;

&lt;p&gt;AI now has the capability of generating images. There are tools that render AI images from a photo and also generate from a user's specified input. The downside to these AI image generative tools are they are not free or limited. &lt;/p&gt;

&lt;p&gt;In this article, I will be showing you how to use ChatGPT-3.5 and &lt;a href="https://pollinations.ai/" rel="noopener noreferrer"&gt;Pollinations AI&lt;/a&gt; to generate AI images for free.&lt;/p&gt;

&lt;p&gt;Pollinations Ai are a team of machine-learning specialists, artists &lt;br&gt;
that are deeply engaged in the open source AI ecosystem.&lt;/p&gt;

&lt;p&gt;I found this hack and it has been super helpful for me in generating article cover images. Saves me the stress of looking up stock-free websites for images or having to design on Canva.&lt;/p&gt;

&lt;p&gt;Enough of the intro, let's start generating some cool images 😎&lt;/p&gt;

&lt;p&gt;To start the process, you will need to input this prompt into ChatGPT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INPUT = {focus}
OUTPUT = {description} \n ![IMG](https://image.pollinations.ai/prompt/{description})
{description} = {focusDetailed},%20{adjective1},%20{adjective2},%20{visualStyle1},%20{visualStyle2},%20{visualStyle3},%20{artistReference}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we add our &lt;code&gt;INPUT&lt;/code&gt; and &lt;code&gt;OUTPUT&lt;/code&gt; - which are like parameters of what we want our image to look like. The &lt;code&gt;INPUT&lt;/code&gt; will be the main character of your expected image. &lt;/p&gt;

&lt;p&gt;I want my main character to be a Cat(I am a huge cat lover, they are lovely creatures). So, I put cat as my &lt;code&gt;INPUT&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INPUT = a cat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;OUTPUT&lt;/code&gt; provides more image details. You can detail every single thing you want your image to show. There are seven available criteria you can specify but, it is not mandatory to fill all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{focusDetailed},%20{adjective1},%20{adjective2},%20{visualStyle1},%20{visualStyle2},%20{visualStyle3},%20{artistReference}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are the criteria we want for our soon-to-be generated image:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The cat to be outside in the night (focusDetailed).&lt;/li&gt;
&lt;li&gt;The cat should be orange in color (adjective1).&lt;/li&gt;
&lt;li&gt;The cat should be happy (adjective2).&lt;/li&gt;
&lt;li&gt;It should be raining (visualStyle1).&lt;/li&gt;
&lt;li&gt;It should be warm (visualStyle2).&lt;/li&gt;
&lt;li&gt;The background should have lots of flowers (visualStyle3)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;NB: I ignored the artistReference aspect, you can input it if you have an artist in mind.&lt;/p&gt;

&lt;p&gt;This is what our &lt;code&gt;OUTPUT&lt;/code&gt; would look like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OUTPUT = a cat outside in the night, orange cat, happy cat, rainy, warm, lots of flowers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have all the necessary parts for our image. The whole prompt to generate the image is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INPUT = {focus}
OUTPUT = {description} \n ![IMG](https://image.pollinations.ai/prompt/{description})
{description} = {focusDetailed},%20{adjective1},%20{adjective2},%20{visualStyle1},%20{visualStyle2},%20{visualStyle3},%20{artistReference}

INPUT = a cat
OUTPUT = a cat outside in the night, orange cat, happy cat, rainy, warm, lots of flowers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is all we need to have a cat image generated by Pollinations AI!&lt;/p&gt;

&lt;p&gt;Press enter and below, it would generate an image link that looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;![IMG](https://image.pollinations.ai/prompt/a%20cat%20outside%20in%20the%20night,%20orange%20cat,%20happy%20cat,%20rainy,%20warm,%20lots%20of%20flowers)

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

&lt;/div&gt;



&lt;p&gt;Having a closer look at the link, you can see all the criteria we set for the image embedded in the link.&lt;/p&gt;

&lt;p&gt;You can follow the link or copy and paste it in your favorite browser depending on how the link appears in your ChatGPT.&lt;/p&gt;

&lt;p&gt;Here is the image generated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjojwsdexqaw1s5keyiy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjojwsdexqaw1s5keyiy.jpg" alt="Generated Image of an orange cat in the rain" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How cool is that!&lt;/p&gt;

&lt;p&gt;Let's try another image, this time it would be two people in the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INPUT = {focus}

OUTPUT = {description} \n ![IMG](https://image.pollinations.ai/prompt/{description})

{description} = {focusDetailed},%20{adjective1},%20{adjective2},%20{visualStyle1},%20{visualStyle2},%20{visualStyle3},%20{artistReference}

INPUT = a photo of two men

OUTPUT = A photo of two men shaking hands, cool, calm, green color, standing on the road
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, is our generated image of two gentle men shaking hands on the road (looks dangerous though).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48xu64cruf8rlau99u29.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48xu64cruf8rlau99u29.jpg" alt="Generated Image of two men shaking hands on the road" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try it out and let me know if it works for you in the comments.&lt;br&gt;
Follow me on &lt;a href="https://www.linkedin.com/in/timothy-olanrewaju750/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for interesting contents and tech-related articles.&lt;/p&gt;

</description>
      <category>howto</category>
      <category>ai</category>
      <category>opensource</category>
      <category>design</category>
    </item>
    <item>
      <title>Harnessing the Potential of AbortController with FetchAPI for Optimal Performance</title>
      <dc:creator>Timothy Olanrewaju</dc:creator>
      <pubDate>Sun, 31 Mar 2024 07:36:18 +0000</pubDate>
      <link>https://dev.to/timothyolanrewaju/harnessing-the-potential-of-abortcontroller-with-fetchapi-for-optimal-performance-4je0</link>
      <guid>https://dev.to/timothyolanrewaju/harnessing-the-potential-of-abortcontroller-with-fetchapi-for-optimal-performance-4je0</guid>
      <description>&lt;p&gt;The Fetch API is a modern JavaScript interface for fetching resources like data or files asynchronously from the web. The Fetch API is known for its asynchronous nature which means, the fetch requests does not in any way block the execution of other code while awaiting response from the Fetch API. It simplifies the process of fetching data, handling responses and plays an important role in allowing developers to build efficient and interactive web experiences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Good knowledge of JavaScript and Fetch APIs.&lt;/li&gt;
&lt;li&gt;Intermediate experience in React.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Consuming the Fetch API
&lt;/h3&gt;

&lt;p&gt;In React applications, any activity that involves external sourcing of data is handled using the useEffect hook. The useEffect hook houses the asynchronous function of the FetchAPI. In this article, we will make use of the &lt;a href="https://openlibrary.org/dev/docs/api/search"&gt;Open Library Search API&lt;/a&gt; , a simple app to retrieve book data on Open Library. We would like to narrow our search to the authors and just like most Fetch APIs, we can attach a query parameter to the endpoint to fetch specific data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function App() {
useEffect(
    function () {
      async function FetchBooks() {
        const res = await fetch(
          `https://openlibrary.org/search/authors.json?`
        );
        const data = await res.json();
}
FetchBooks();
},[dependency array]
)
return (
    &amp;lt;&amp;gt;
      Enter Author's Name:{" "}
      &amp;lt;input
        type="text"/&amp;gt;
      &amp;lt;h2&amp;gt;Search Results&amp;lt;/h2&amp;gt;
      &amp;lt;ol&amp;gt;

      &amp;lt;/ol&amp;gt;
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the basic structure of our React App component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;useEffect Hook:&lt;/strong&gt; Responsible for handling side effects of data fetching and will cause the App component to render when the application mounts. It also houses a dependency array which is the state and it causes the component to re-render anytime there is a change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FetchBooks:&lt;/strong&gt; Is our asynchronous function that awaits a response in json format from the Open Library Search API and takes in a query parameter, which will be determined by an input state.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;input&lt;/strong&gt; type that will take in the &lt;strong&gt;searchTerm&lt;/strong&gt; state and dynamically add it to the fetch request.&lt;/li&gt;
&lt;li&gt;An ordered list that will be rendered to display results of our search query.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Handling States and Events
&lt;/h3&gt;

&lt;p&gt;States are referred to as any data that will likely change in a React application. When declaring states, two parameters are specified - the state value and the state updater function, that sets the state. In our Book Search application’s case, we need two states which are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State that will take in the search term from the input, called the searchTerm.&lt;/li&gt;
&lt;li&gt;State that will store the search results, called the searchResults.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState([]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, is to attach an onChange event handler to the input to take in the event and pass it as a search query to the fetch API’s query parameter. The setSearchTerm handles the updating of every key pressed as a state value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding the onChange event handler to the input&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input
        type="text"
        onChange={(e) =&amp;gt; setSearchTerm(e.target.value)}
        value={searchTerm}
      /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Appending the searchTerm state to the fetch API endpoint query parameter&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const res = await fetch(
          `https://openlibrary.org/search/authors.json?q=${searchTerm}`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rendering the List
&lt;/h3&gt;

&lt;p&gt;After a successful response has been gotten from the API and the data gotten, we want to display it in the ordered list returned by our JSX. We achieve this by mapping through the data present in the &lt;strong&gt;searchResults&lt;/strong&gt; array state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ol&amp;gt;
        {searchResults.map((books) =&amp;gt; (
          &amp;lt;li key={books.key}&amp;gt;
            {books.name}
          &amp;lt;/li&amp;gt;
        ))}
&amp;lt;/ol&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have all our essential pieces in place to be able make a Fetch request and display the response(data).&lt;/p&gt;

&lt;h3&gt;
  
  
  A look at the Browser Network Tab
&lt;/h3&gt;

&lt;p&gt;When performing fetch requests, it is good practice to keep an eye on the network tab of your browser. A quick look at that shows a request for every query parameter entered. This means that for every searchTerm entered or individual key pressed, there is a request sent to the Fetch API - some may come with data and some without any data. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcf9l1igotdtwet1ugxu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcf9l1igotdtwet1ugxu.png" alt="Browser Network Tab" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Food for thought
&lt;/h3&gt;

&lt;p&gt;As a good front-end developer,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you think that it is efficient and good work to allow so many requests to get sent to the API?&lt;/li&gt;
&lt;li&gt;Imagine a situation whereby the search term is lengthy and the user’s internet connection is very slow. In fact, you can mimic such a situation by choosing slow 3G from the Throttling dropdown options above - still in the Network tab section. You will notice how terrible of an experience that can be. However, there is a fascinating solution to this problem which is the Abort Controller!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The AbortController JavaScript Web API
&lt;/h3&gt;

&lt;p&gt;This Web API represents a controller object that has the capability to abort one or more web requests. It cancels many web requests leading up to a ‘final’ web request that is allowed to fetch data from the API. &lt;br&gt;
Firstly, we create a new AbortController object using the AbortController() constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const controller = new AbortController();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secondly, we make use of the signal property of the AbortController object which we call controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { signal } = controller;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thirdly, the fetch API allows us to attach options. So, we attach the signal as an option object to the fetch request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const res = await fetch(
          `https://openlibrary.org/search/authors.json?q=${searchTerm}`,
          { signal }
        );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we return a CleanUp function which calls the abort() method of the controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return function () {
        controller.abort();
      };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The clean up function should be returned immediately after calling the asynchronous function.&lt;/p&gt;

&lt;h3&gt;
  
  
  AbortController Error Handling
&lt;/h3&gt;

&lt;p&gt;The AbortController generates an AbortError message on the Console anytime an operation is being aborted. This allows you to know when an operation is aborted and is different from other forms of errors you might encounter while performing asynchronous operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5e0maa57nuoagzckonyx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5e0maa57nuoagzckonyx.png" alt="Browser Console logging an AbortError message" width="800" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final full code piece
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useEffect, useState } from "react";
import "./App.css";
function App() {
  const [searchTerm, setSearchTerm] = useState("");
  const [searchResults, setSearchResults] = useState([]);
  useEffect(
    function () {
      const controller = new AbortController();
      const { signal } = controller;
      async function FetchBooks() {
        const res = await fetch(
          `https://openlibrary.org/search/authors.json?q=${searchTerm}`,
          { signal }
        );
        const data = await res.json();
        setSearchResults(data.docs);
      }
      FetchBooks();
      return function () {
        controller.abort();
      };
    },
    [searchTerm]
  );
  return (
    &amp;lt;&amp;gt;
      Enter Author's Name:{" "}
      &amp;lt;input
        type="text"
        onChange={(e) =&amp;gt; setSearchTerm(e.target.value)}
        value={searchTerm}
      /&amp;gt;
      &amp;lt;h2&amp;gt;Search Results&amp;lt;/h2&amp;gt;
      &amp;lt;ol className="olStyle"&amp;gt;
        {searchResults.map((books) =&amp;gt; (
          &amp;lt;li style={{ padding: "3px" }} key={books.key}&amp;gt;
            {books.name}
          &amp;lt;/li&amp;gt;
        ))}
      &amp;lt;/ol&amp;gt;
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Results
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feq6j6oip1cnyx45m6i94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feq6j6oip1cnyx45m6i94.png" alt="Final browser network" width="800" height="222"&gt;&lt;/a&gt;&lt;br&gt;
Now, the abortController cancels all fetch requests leading up to the last. The act of canceling these requests has prevented the network from executing numerous fetch operations, thereby enhancing the overall efficiency of the fetch process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; A successful fetch request largely depends on the speed of keystroke as the abortController will permit the sending of query parameter requests if the keys are being pressed a bit slower.&lt;br&gt;
The idea behind the abortController is pretty cool and is highly recommended to build efficient applications. &lt;br&gt;
Connect with me on &lt;a href="http://www.linkedin.com/in/timothy-olanrewaju750"&gt;LinkedIn&lt;/a&gt; for more useful front-end related posts.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Frontend Development 101: Essential Skills and Tools</title>
      <dc:creator>Timothy Olanrewaju</dc:creator>
      <pubDate>Tue, 25 Jul 2023 19:38:32 +0000</pubDate>
      <link>https://dev.to/timothyolanrewaju/frontend-development-101-essential-skills-and-tools-38mo</link>
      <guid>https://dev.to/timothyolanrewaju/frontend-development-101-essential-skills-and-tools-38mo</guid>
      <description>&lt;p&gt;Frontend just as the name implies, is the end or interface that the user sees in front of them and they interact with. The act of developing softwares that renders an elegant interface while also maintaining engagements with users and ensures the experience of the user is seamless. There is also the backend which is the opposite end, that supports the frontend with resources. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1xy8xuqpbxcvz49i0py.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1xy8xuqpbxcvz49i0py.jpeg" alt="Forms Example" width="800" height="555"&gt;&lt;/a&gt;&lt;br&gt;
In the above image, a user fills the form’s input fields for registration on a webpage and submits it. The form is the frontend resource he/she interacts with. The process of manipulating, storing the data is handled in the backend part of the application which is invisible to the user but is responsible for all the logical aspects.&lt;br&gt;
Frontend development mainly focuses on the functional aspects of creating user interfaces. The demand for frontend developers is on the rise as the need to have an efficient interface that users can be comfortable navigating through is crucial in the world today. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SKILLS&lt;/strong&gt;&lt;br&gt;
There are numerous skills that are needed in frontend development. So, in this section we will be taking a sneak peek into these important skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML&lt;/strong&gt;&lt;br&gt;
Known as HyperText Markup Language. This is the main structure that serves as a building block for all webpages. It can also be referred to as the skeletal backbone of frontend web development. Html elements consist of resources visible on a web page such as buttons, forms, links etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSS&lt;/strong&gt;&lt;br&gt;
Known as Cascading Style Sheet. It is used to design the layout of a webpage for elegant presentation and gives the developer the ability to customize as he/she wishes. CSS gives the Html elements styles and improves the interactivity with users. Animations are also achievable with CSS. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAVASCRIPT&lt;/strong&gt;&lt;br&gt;
Is a frontend programming language that adds dynamism to the interactivity of a web page to the users. Javascript can make  Html elements behave in a way that is customizable by the developer. For example, a button can be given a particular function or behavior when a certain event is triggered which can be a click, double click etc. With Javascript embedded in a webpage, it can be refreshed dynamically while responding to users actions. &lt;br&gt;
Also, Javascript is capable of handling operations that takes time to execute without blocking the other tasks from executing. This comes in handy if you want an operation to carry on and not wait for some tasks to reach completion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FRONTEND DEVELOPMENT FRAMEWORKS&lt;/strong&gt;&lt;br&gt;
In recent times, there has been an increase of highly functional collections of pre-written codes and tools for web development that improves efficiency and increases flexibility. They also come with features such as state management, responsive design capabilities and components.  Popular frontend frameworks are React, Svelte, Angular, Vue.js etc&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VERSION CONTROL&lt;/strong&gt;&lt;br&gt;
This is an extremely important skill in software development as it helps developers track and manage changes to codes and files. The most used version control system is Git. It is necessary to be able to use git efficiently in today’s Tech world and it also helps in collaboration on projects. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RESPONSIVE WEB DESIGN&lt;/strong&gt;&lt;br&gt;
This is a very important part of modern web development that takes into consideration various devices and screen sizes. Websites and applications are meant to adjust dynamically to corresponding devices and screen sizes to allow users to access contents and navigate seamlessly. Optimal user experience is the goal in making a responsive web design. Zooming and struggling with elements that are unresponsive can lead to frustrating and bad user experience which doesn't bode well for the developers of the application or website. To achieve a good responsive site, developers should use the following: flexbox, media queries, flexible layouts and grids. Fixed and layouts set to arbitrary values should not be integrated to achieve a fluid and responsive layout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BASIC DESIGN PRINCIPLES&lt;/strong&gt;&lt;br&gt;
To build maintainable and effective frontend code, it is important to follow some fundamental design principles guiding frontend development. They are:&lt;br&gt;
&lt;strong&gt;Responsive web design&lt;/strong&gt;:Which is about embedding adaptation to different devices and screen sizes to keep consistent user experience across different platforms.&lt;br&gt;
&lt;strong&gt;Mobile first approach&lt;/strong&gt;:Is also essential due to the ever increasing use of mobile devices. Hence, priority must be given to mobile devices.&lt;br&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: It is good practice to keep the interface simple so as to avoid overwhelming users with needless complexities.&lt;br&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: keeping a consistent layout, interaction pattern and design throughout the creation of an application is good practice.&lt;br&gt;
&lt;strong&gt;Performance optimization&lt;/strong&gt;: To get the best performance in the frontend, it is advisable to minimize file sizes and use lazy image loading. When applied, an application or website’s frontend loads faster which improves user experience and SEO ranking.&lt;br&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Is all about organizing the codebase into modules and using scalable ways, that involves using components and modules to break down complex user interfaces.&lt;br&gt;
&lt;strong&gt;Cross-browser compatibility&lt;/strong&gt;: Testing the frontend across different web browsers to check if it maintains a good layout across web browsers is also very important.&lt;br&gt;
&lt;strong&gt;Performance testing&lt;/strong&gt;: Regularly testing the frontend to check for loopholes that should be improved on helps in upscaling overall application performance. Tools like Pagespeed Insights and Lighthouse perform these tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TOOLS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are various tools used in building interactive frontend and they are:&lt;br&gt;
&lt;strong&gt;Code Editors&lt;/strong&gt;: These play significant roles in software development as they provide an environment for writing of Html, CSS and Javascript codes. They provide other features such as syntax highlighting, code completion, code formatting, error checking and integration with version control. Most code editors allow the integration of extensions that perform specific tasks on codes. Examples of Code editors are Visual Studio Code, Sublime Text, Atoms etc.&lt;br&gt;
&lt;strong&gt;Package Managers&lt;/strong&gt;: Due to the use of external libraries, frameworks and dependencies employed in an application, there is a need to use tools that help manage and organize them and that is where package managers come in handy. They help ease the process of installing, updating and removing these external packages. Examples are NPM known as Node Package Manager and Yarn.&lt;br&gt;
&lt;strong&gt;Version Control Clients&lt;/strong&gt;: These are software tools that provide a user an interface to interact with version control systems. They make the operation of version control easier for developers and manage repositories without the use of command line interfaces. Examples of version control clients are: GitKraken, Github Desktop, SmartGit etc.&lt;br&gt;
&lt;strong&gt;Design Collaboration Tools&lt;/strong&gt;: These tools play a vital role as it bridges the gap between designers and developers in streamlining the entire development process. Designers create prototypes and design mockups of the frontend with developers. The interaction can be mocked up and demonstrated on how the frontend will behave.&lt;/p&gt;

&lt;p&gt;In conclusion, frontend development is a crucial part of software development which is also constantly evolving and plays a vital role in users’ interactivity with websites and applications. In this article, we have explored some essential skills and tools that every aspiring frontend developer should be familiar with to succeed in this field.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>career</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
