<?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: Keyhole Software</title>
    <description>The latest articles on DEV Community by Keyhole Software (@keyholesoftware).</description>
    <link>https://dev.to/keyholesoftware</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%2Forganization%2Fprofile_image%2F4408%2F9feca361-1fd4-45ee-a0f9-200f9d26e379.jpg</url>
      <title>DEV Community: Keyhole Software</title>
      <link>https://dev.to/keyholesoftware</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/keyholesoftware"/>
    <language>en</language>
    <item>
      <title>Apollo Client with GraphQL: State Management for GraphQL Made Easy</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Wed, 08 Feb 2023 21:43:49 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/apollo-client-with-graphql-state-management-for-graphql-made-easy-4mkj</link>
      <guid>https://dev.to/keyholesoftware/apollo-client-with-graphql-state-management-for-graphql-made-easy-4mkj</guid>
      <description>&lt;p&gt;In this post, we will explore Apollo Client integrated with a React application and how it provides an abstraction layer between a JavaScript application and a GraphQL server. The features Apollo Client offers make it a prime choice for devs also using GraphQL.&lt;/p&gt;

&lt;p&gt;We’ll begin by discussing what Apollo Client is and how it can be advantageous over other state management libraries. And finally, we will look at some example React queries for retrieving and mutating data.&lt;/p&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="http://keyholesoftware.com" rel="noopener noreferrer"&gt;Keyholesoftware.com&lt;/a&gt; by Braden Niswonger on 1/16/23.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is Apollo Client?
&lt;/h2&gt;

&lt;p&gt;Apollo Client is a popular state management library built for JavaScript applications that interact with a GraphQL server. It allows easy solutions for handling GraphQL data including data fetching, caching, real-time subscriptions, and error handling. Additionally, Apollo Client has a large and active community with many resources and support for developers.&lt;/p&gt;

&lt;p&gt;Apollo Client can be used in many different JavaScript libraries including Angular, Vue.JS, and React. This allows for easy integration into new and existing projects without having to rewrite code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Apollo Client?
&lt;/h2&gt;

&lt;p&gt;When using a GraphQL server, Apollo Client is a great choice for a front-end state management tool. The advantage of Apollo Client over other libraries such as Redux or Recoil is that it provides features specifically designed for GraphQL. Some of these features include:&lt;br&gt;
Data Management: Apollo Client provides API for querying and modifying data, allowing for easy state management of an application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Built-In Caching:&lt;/strong&gt; By caching query results, Apollo Client can reduce the refetching of data that has already been retrieved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling:&lt;/strong&gt; Apollo Client has built-in error handling when querying or updating data. This helps simplify both error handling and error display to users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimistic UI:&lt;/strong&gt; Apollo Client will temporarily display the result of a mutation before the server has confirmed the change. Once the operation is complete, it will replace the optimistic data. This can make an application feel more responsive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Tooling:&lt;/strong&gt; Apollo has a suite of developer tools that can be utilized for debugging and application support.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Set Up
&lt;/h2&gt;

&lt;p&gt;The following command will install the Apollo Client and GraphQL dependencies needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @apollo/client graphql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we will define a function to return an Apollo Client with reference to a GraphQL server. This will also set up an in-memory cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
    uri: 'https://path-to-graphql-server.com/',
    cache: new InMemoryCache(),
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To finish setup, we have to wrap the React app with an ApolloProvider. This will allow any subcomponent access to the GraphQL data.&lt;/p&gt;

&lt;p&gt;Wrapping can be done at any level, but I recommend putting it somewhere high in the component tree, above any component that will need to utilize it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ApolloProvider } from '@apollo/client';
⋮
&amp;lt;ApolloProvider client={client}&amp;gt;
    &amp;lt;App /&amp;gt;
&amp;lt;/ApolloProvider&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Queries
&lt;/h2&gt;

&lt;p&gt;Now that dependencies have been installed and the application is properly set up, the application is equipped to start querying.&lt;/p&gt;

&lt;p&gt;Apollo Client provides several custom &lt;a href="https://keyholesoftware.com/2021/04/08/using-environment-variables-with-react-hooks/" rel="noopener noreferrer"&gt;React hooks&lt;/a&gt; for querying GraphQL data. For the sake of brevity in this post, we will only cover &lt;code&gt;useQuery&lt;/code&gt; (used for retrieving data) and &lt;code&gt;useMutation&lt;/code&gt; (used for modifying data).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useQuery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;useQuery&lt;/code&gt; hook allows a GraphQL query to be executed inside a functional component. When using the hook, it will provide an object containing the dataset returned from the query, as well as some other metadata such as any loading and error states.&lt;/p&gt;

&lt;p&gt;On component render, the hook will execute the given query. It will return the results in the &lt;code&gt;data&lt;/code&gt; property, the current loading state in the &lt;code&gt;loading&lt;/code&gt; property, and any errors in &lt;code&gt;error&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;    import { useQuery, gql } from '@apollo/client';
    ⋮
const { loading, error, data } = useQuery(gql`
    query {
        user {
            name
            email
        }
    } 
`);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useMutation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;useMutation&lt;/code&gt; hook is used for executing a GraphQL mutation in a functional component. The response is made of two parts.&lt;/p&gt;

&lt;p&gt;The first is the mutation function that executes the mutation against the given GraphQL server. The second part is the object containing the results of the mutation, as well as the loading and error states.&lt;/p&gt;

&lt;p&gt;The mutation function can be called with variables and options, and it returns a &lt;code&gt;Promise&lt;/code&gt; that will resolve the mutation result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useMutation, gql } from '@apollo/client';
    ⋮
const [createUser, { data, loading, error }] = useMutation(gql`
    mutation createUser($name: String!) {
        createUser(name: $name) {
            id
            name
            completed
        }
    }
`);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;For continued reading on Apollo Client or more example code, here are some resources I found helpful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.apollographql.com/docs/react/why-apollo/" rel="noopener noreferrer"&gt;Why Apollo Client?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apollographql.com/docs/react/get-started/" rel="noopener noreferrer"&gt;Get Started with Apollo Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hasura.io/learn/graphql/react/apollo-client/" rel="noopener noreferrer"&gt;Set Up a GraphQL Client with Apollo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>retrogaming</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>Beyond Agile: Dev Methodologies to Fit Your Project</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Thu, 26 Jan 2023 17:03:07 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/beyond-agile-dev-methodologies-to-fit-your-project-2bl6</link>
      <guid>https://dev.to/keyholesoftware/beyond-agile-dev-methodologies-to-fit-your-project-2bl6</guid>
      <description>&lt;p&gt;Throughout my 17-year career, I have worked on projects using several different development methodologies. Most projects I’ve done have utilized the traditional agile approach. Agile is the most popular and widely used, but depending on the project specs, I’ve found other alternative strategies that are better suited for the work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="http://www.keyholesoftware.com"&gt;Keyholesoftware.com&lt;/a&gt; by Brian Jacobs on 1/12/23.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A Real-World Example
&lt;/h2&gt;

&lt;p&gt;Most recently, I worked on a project for an international client by the name of Solera. Our primary contact worked from the UK, so we had to deal with a six-hour time difference, meaning that our daily meetings were in the morning for us and the afternoon for them. The time zone difference did present a challenge with daily schedules, but we found ways to communicate clearly and efficiently.&lt;/p&gt;

&lt;p&gt;With excellent help and guidance from our UK contact, we got our environments set up in Docker, Postgres, VPN, Git repos, and localhost. Once our dev environments were set up, we were ready to begin the project.&lt;/p&gt;

&lt;p&gt;The project was small in scope, and the client wanted the work done as soon as possible due to budgetary concerns. With that in mind, we worked through the project with a different methodology than most traditional tried and true ways (ie Agile). If we had chosen Agile, we would have wasted time and resources.&lt;/p&gt;

&lt;p&gt;I hope this example demonstrates that sometimes, Agile isn’t the best choice, even if it is the most highly touted. Sometimes, &lt;strong&gt;it’s worth thinking critically about the project and its requirements to select a methodology that works better&lt;/strong&gt;. In this post, I will explore a few of those Agile alternatives.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Agile Alternative For This Example
&lt;/h2&gt;

&lt;p&gt;Regarding the example above, instead of a traditional agile approach that involves sprint planning, backlog grooming, sprint retros, etc, we were given a PDF with mockups and info regarding interface behaviors, error handling, data handling, etc.&lt;/p&gt;

&lt;p&gt;I reviewed the PDF and came up with questions for things that needed more explanation, edge cases, and exceptions. Then, we went through the document in a web call, addressed the issues, defined technical aspects and interface behaviors, and added notes with the refined requirements.&lt;/p&gt;

&lt;p&gt;The updated document became the de facto stories and tasks for front-end and back-end development as well as QA. Thus, we were able to proceed quickly with development and testing.&lt;/p&gt;

&lt;p&gt;The advantages of this method are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No time was spent writing out stories, adding tasks, and estimating hours&lt;/li&gt;
&lt;li&gt;No formal sprint retros and backlog grooming&lt;/li&gt;
&lt;li&gt;Discussing questions in the daily standup and getting to a resolution more quickly&lt;/li&gt;
&lt;li&gt;By screen sharing completed work frequently, demos clearly showed what development was done and what was outstanding and allowed design adjustments and refinements to be made&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For small to midsize projects, this approach seems ideal. Formal meetings weren’t needed, as the goals of retros, grooming, writing stories, tasks, and demos are accomplished during daily standups or other arbitrary web calls. While we did not have formal stories written out, we still clearly knew who was working on what, how much development was done, and what development remained.&lt;/p&gt;

&lt;p&gt;When making refinements along the way, we only had to update a single PDF document rather than updating or creating new stories in a tool such as Jira. This allowed the developers to take the project and run with it. Developers are often most productive when able to be heads-down, coding all day instead of spending hours dealing with emails, unnecessary meetings, and other non-dev-related tasks.&lt;/p&gt;

&lt;p&gt;Issues were addressed very quickly when they arose, communication was clear and concise, and development and testing went forward smoothly. I have worked on projects in the past in which the dev team was given mockups with click thru paths of the screens. While not all details are fully conveyed and some issues were discovered later on, having a full overview of the project allowed a good architecture and design to be constructed and built upon.&lt;/p&gt;

&lt;h2&gt;
  
  
  State Driven Development: Another Agile Alternative
&lt;/h2&gt;

&lt;p&gt;Another agile alternative strategy that I find to be easily understood is &lt;a href="https://dev.to/nimmo/state-driven-development-for-user-interfaces-part-1-an-introduction-27f1"&gt;State Driven Development&lt;/a&gt;. This paradigm involves providing and defining all the various states that apply to a screen or component.&lt;/p&gt;

&lt;p&gt;States do not apply for static elements since content is readily available upon page load. However, for screens or components with dynamic content, the following states typically apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading state&lt;/li&gt;
&lt;li&gt;Happy data&lt;/li&gt;
&lt;li&gt;No data&lt;/li&gt;
&lt;li&gt;Not enough data&lt;/li&gt;
&lt;li&gt;Too much data&lt;/li&gt;
&lt;li&gt;Error states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s talk a little more about each of these in depth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loading State&lt;/strong&gt;&lt;br&gt;
Most screens or components in an API-driven application will have a loading state either when retrieving or sending data. A loading state is often needed to indicate that data is being processed. Usually, a spinning animation or simple text message is displayed while this data processing is underway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Data&lt;/strong&gt;&lt;br&gt;
Happy data is the display of data when all the expected fields are present and the result set is manageable. Load times are fast, all the data renders properly and is easily readable, and there is minimal scrolling required to view all the data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Data&lt;/strong&gt;&lt;br&gt;
Usually, a specific message or UI is needed when an API call completes successfully but no results are returned. For example, if data for a table or chart is to be displayed, a message such as, “no results found” is often displayed.&lt;/p&gt;

&lt;p&gt;Depending on the design, the X and Y axis of the chart or the column headers of a table may still render with the “no data” message displayed. Alternatively, a simple “no data” message or image may suffice. Either way, the “no data” state should be considered, so developers, QA, and automated tests all account for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not Enough Data&lt;/strong&gt;&lt;br&gt;
The “not enough data” state has some design and programming considerations to be aware of and should be handled accordingly. “Not enough data” refers to the display of data when not all fields are present.&lt;/p&gt;

&lt;p&gt;In most cases, displaying nothing works fine. In some cases displaying “N/A”, “(empty)”, or some other text is needed to make it more apparent that a data field does not exist. Some designs display a label with no value. Others may omit the display of a label if a corresponding value is not present. Visual aids such as grayed-out text or an icon may be displayed in place of incomplete data.&lt;/p&gt;

&lt;p&gt;There are some things to consider here. If a data table is normally 500px tall when enough data is present, it may be only 75px tall when very little data exists; this could potentially cause layout issues. A slider or carousel that, ideally, has dozens of frames to scroll through may render oddly if only a few frames exist. Also, left and right scroll buttons or table pagination controls may need different handling or displays if not enough data exists to scroll or page through.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Too Much Data&lt;/strong&gt;&lt;br&gt;
Having too much data may cause side effects that should be taken into account. A long word or number may cause text to be cut off or hidden if not enough space is given for it to fully render. Several paragraphs of text may cause unexpected layout issues by pushing other elements away from their desired placement. If scrollbars appear, they may also cause layout issues.&lt;/p&gt;

&lt;p&gt;Too much data can also lead to performance issues if the data is too much for a browser or API request to handle. Most of the time, too many data scenarios only cause minor layout and display issues that can be handled with CSS fixes. Other times more drastic solutions are needed – such as partial loading of data, pagination, load as you scroll, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error State&lt;/strong&gt;&lt;br&gt;
In an API-driven application, error states can occur for a variety of reasons. Scenarios including system errors, API failures, session timeouts, validation errors, and more are all part of most web applications. Error handling is a major effort when doing development, on both the front and back-end. Design is also heavily involved in the display of error messages as there are so many ways to convey error messages to the end user.&lt;/p&gt;

&lt;p&gt;The states listed above apply to most web applications I have worked on. Being aware of them and making sure they are accounted for resolves the vast majority of edge cases. It also makes an application more “bulletproof.” Discussing the above states with your development team often leads to improved design and better code bases, and ensures everything is accounted for and handled properly within a system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Another Nontraditional Agile Alternative
&lt;/h2&gt;

&lt;p&gt;This next agile alternative/programming method is not the most scientific or well-defined, but it is not without practical benefits. This method works best when the design or end goal of a project is not fully known or when the product owner does not fully know what they actually want.&lt;/p&gt;

&lt;p&gt;In that scenario, it is often beneficial to have some examples to look at, click around on, experiment with a real interface or two, and then proceed with that extra trial experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rapid Prototyping&lt;/strong&gt;&lt;br&gt;
Rapid prototyping usually involves two or more developers developing the same thing in different ways. Pitfalls and unknowns are discovered, advantages and disadvantages to each method are found, a consensus can be reached based on comparing notes, and progress is made. This is also a good way to learn a new framework, library, or code package.&lt;/p&gt;

&lt;p&gt;Rapid prototyping creates something tangible that can be seen and demonstrated, allowing a client to play around with it and decide what they like or dislike.&lt;/p&gt;

&lt;p&gt;Clients are often very pleased to have an application that they can view in a browser or mobile device. Even though the product is not fully developed or tested, having a real example is very beneficial when still defining the full end-to-end product. The rapidly developed prototypes can then be iterated upon, keeping desirable features, discarding unwanted features, and further defining the end goal.&lt;/p&gt;

&lt;p&gt;In many instances throughout my career, the client has needed quite a bit of time to come up with requirements, next steps, approval, and/or funding. That downtime can be used by the development team to clean up the prototypes, come up with a solid architecture, refactor and optimize the code, fix bugs, and make other improvements.&lt;/p&gt;

&lt;p&gt;The end result is a more stable code base that can be tested and made production ready much easier. While this is a less defined work path, it is still an effective way to build a system and make use of downtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;While there is no right or wrong way to develop a system, some ways are definitely more efficient than others. In my experience, getting a screen or component developed before expected has worked out very well as it gives the client something tangible at an earlier date, allows more time for testing, and gives a product owner plenty of time to decide if the end result is what they actually want. On most small to midsize projects I have worked on, myself and my team have been more productive when given a workload to just run with and get knocked out.&lt;/p&gt;

&lt;p&gt;The contents of this article are written from my own experience, spanning my 17-year career. These points will not apply to every project out there, but they can be used as insight into alternative strategies for developing a system.&lt;/p&gt;

&lt;p&gt;Each method has different pros and cons, some may be very suitable for a project, while others may not be. Determining which strategy to use becomes more clear with some experience and experimentation.&lt;/p&gt;

&lt;p&gt;I would appreciate hearing your feedback if you have any after reading this post. Let me know what you think about these agile alternatives in the comments below, and if you enjoyed reading, go subscribe to the &lt;a href="https://keyholesoftware.com/blog/"&gt;Keyhole Dev Blog&lt;/a&gt; for more.&lt;/p&gt;

</description>
      <category>agile</category>
      <category>programming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Server-Side Fun with Next.js</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Tue, 24 Jan 2023 17:24:20 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/server-side-fun-with-nextjs-5ac5</link>
      <guid>https://dev.to/keyholesoftware/server-side-fun-with-nextjs-5ac5</guid>
      <description>&lt;p&gt;React has come a long way in terms of giving us JavaScript developers a quick and easy-to-use framework to build web applications without backend code. However, I recently encountered a use case where a full-stack web application was needed: a Jira ticketing system that allows users to create tickets, view ticket status, download reports, upload attachments, etc.&lt;/p&gt;

&lt;p&gt;As it so happens, we really don’t need to stand up an entirely separate back-end to handle these requests server-side. Instead, we can simply utilize &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;. The server side of Next.js can serve as a back-end for accessing and fetching data, which is exactly what we need for this use case.&lt;/p&gt;

&lt;p&gt;Making requests to third-party services like Jira, AWS, etc. can bring out the dreaded and frustrating CORS error, especially if things are handled on the client side only. Therefore, a server side will prove to be essential. SSR or server-side rendering will help us in our example.&lt;/p&gt;

&lt;p&gt;In this post, I will go over some technical details on how to work with server-side rendering in Next.js as well as some cool and helpful utilities to aid in file uploading, all done in React! We’ll take a dive into Static Generation, SSR, and Multi-Part Form Data Uploading using Next-Connect and Mutler.&lt;/p&gt;

&lt;p&gt;Using the example I mentioned above, my goal is to give you an overview of what we can do with the server side of Next.js. Let’s get started!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="http://www.keyholesoftware.com"&gt;Keyholesoftware.com&lt;/a&gt; by Vince Pendergrass on 1/5/23.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Static Generation
&lt;/h2&gt;

&lt;p&gt;Let’s say we need to fetch some Jira ticket data to render on a page. Sure, we can throw in the &lt;code&gt;useEffect&lt;/code&gt; hook, execute a &lt;code&gt;fetch&lt;/code&gt;, and set some state data. However, that’s on the client side, and it’s possible CORS errors might pop up, which is not ideal.&lt;/p&gt;

&lt;p&gt;Here’s where server-side Next.js comes in. Next.js has a &lt;code&gt;getStaticProps&lt;/code&gt; function we can export that will generate the HTML at build time. This executes only on the server, preventing those pesky CORS errors. Here’s how to do it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const getStaticProps = async () =&amp;gt; {
    Const allTickets = await getAllTickets()
    Return {
        Props: {
            allTickets
}
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;getAllTickets()&lt;/code&gt; call will reside in your API and pass ticket results to whatever component you need them in as props.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Tickets = ({allTickets}) =&amp;gt; {
    Return (
&amp;lt;Container spacing={10}&amp;gt;
    {allTickets.issues.map(({key, status, assignee, displayName}) =&amp;gt; (
&amp;lt;Paper key={key} &amp;gt;
    &amp;lt;Ticket {...{name: key, status, assignee, displayName}}/&amp;gt;
&amp;lt;/Paper&amp;gt;
)
)
}
)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your data doesn’t change very often, this may be the way to go. For instance, in our case, we just want a list of open tickets at build time.&lt;/p&gt;

&lt;p&gt;However, if you want access to data that changes often, say for a dashboard or something, you may opt to work with Server-Side Rendering, which brings us to our next topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-Side Rendering
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;getServerSideProps&lt;/code&gt; export function in server-side Next.js will be coded the same as the above &lt;code&gt;getStaticProps&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;export const getServerSideProps = async () =&amp;gt; {
    Const alldata = await getDashboardData()
    Return {
        Props: {
            allData
}
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is all good when we are handling the fetching of data, but what if we need to post data somewhere?&lt;/p&gt;

&lt;p&gt;In general, dealing with requests and passing along uploaded multi-part form data content can be tricky. The good news is that there are good utilities and libraries out there that work inside the Next.js middleware layer to make our lives a little easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next-Connect
&lt;/h2&gt;

&lt;p&gt;Speaking of handy Next.js utilities, let’s talk about &lt;a href="https://www.npmjs.com/package/next-connect"&gt;Next-Connect&lt;/a&gt;. In a life without a utility like Next-Connect, we would need to hardcode switch cases or big &lt;code&gt;if/else&lt;/code&gt; statements in our routing functions to handle all the HTTP verb options.&lt;/p&gt;

&lt;p&gt;Think of Express.js and Node. Next-Connect enables better handling of those standard HTTP verbs (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;), better error control, and just gives us an overall clean approach to our Next.js API routes. It even has support for Generics, making the TypeScript folks happy. Next-Connect acts as a minimal router and middleware layer for Next.js.&lt;/p&gt;

&lt;p&gt;For the processing of file uploads, there are quite a few npm packages out there that can help, but it definitely depends on if your business requirements need to stream files or if a temporary server save and removal is okay.&lt;/p&gt;

&lt;p&gt;The example below uses &lt;a href="https://www.npmjs.com/package/formidable"&gt;Formidable&lt;/a&gt; but requires a disk save since it does not have the option for a memory save.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import formidable from "formidable";
import fs from "fs";

export const config = {
api: {
bodyParser: false
}
};

const post = async (req, res) =&amp;gt; {
const form = new formidable.IncomingForm();
form.parse(req, async function (err, fields, files) {
await saveFile(files.file);
return res.status(200).send("File successfully uploaded!");
});
};

const saveFile = async (file) =&amp;gt; {
const data = fs.readFileSync(file.path);
fs.writeFileSync(`./public/${file.name}`, data);
await fs.unlinkSync(file.path);
return;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same goes for &lt;a href="https://www.npmjs.com/package/multiparty"&gt;Multiparty&lt;/a&gt; and/or &lt;a href="https://www.npmjs.com/package/busboy"&gt;Busboy&lt;/a&gt; (other alternatives) in that they lack the option for a memory save.&lt;/p&gt;

&lt;p&gt;Multer looks to be the only option for in-memory storage and passing along as a stream.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/expressjs/multer#storage"&gt;Multer&lt;/a&gt; works in sync with Next-Connect to handle any multi-part form data uploads in our routes.&lt;/p&gt;

&lt;p&gt;Multer does two things. The first is to send a response based on the user request and to modify the request object before proceeding. In our file upload case, we want to modify the request object. The second is to supply us with two storage types: &lt;code&gt;DiskStorage&lt;/code&gt; and &lt;code&gt;MemoryStorage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DiskStorage&lt;/code&gt;, as the name implies, allows us to utilize the disk for storing files. In this case, all we would need to do is specify the destination and file name. However, if processing the file and sending it to 3rd party storage (say S3 for instance) is the requirement, then &lt;code&gt;MemoryStorage&lt;/code&gt; may be a better option. Multer is neat since it populates the &lt;code&gt;req.files&lt;/code&gt; object for you so they are ready for file consumption automatically.&lt;/p&gt;

&lt;p&gt;Here’s a quick example of using Multer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import nextConnect from 'next-connect';
import multer from 'multer';

const upload = multer({ // Disk Storage option
storage: multer.diskStorage({
destination: './public/uploads',
filename: (req, file, cb) =&amp;gt; cb(null, file.originalname),
}),
});

//const storage = multer.memoryStorage() // Memory Storage option pass along as stream
//const upload = multer({ storage: storage })

const apiRoute = nextConnect({
onError(error, req, res) {
res.status(501).json({ error: `There was an error! ${error.message}` });
},
onNoMatch(req, res) {
res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
},
});

apiRoute.use(upload.array('files'));

apiRoute.post((req, res) =&amp;gt; {
res.status(200).json({ data: 'Success' });
});

export default apiRoute;

export const config = {
api: {
bodyParser: false,
},
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next.js is set up nicely to give us a parsed request body, but only if the content type of the request is application/JSON. In this case, we don’t need to utilize &lt;code&gt;JSON.parse(req.body)&lt;/code&gt; since the parsing has already happened.&lt;/p&gt;

&lt;p&gt;However, if we wanted to override this in our API route, we could just export a config object to modify the default configuration. An applicable use case would be to consume the body as a stream with a raw body. In that case, we can set the &lt;code&gt;bodyParser&lt;/code&gt; to false.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const config = {
api: {
bodyParser: false,
},
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the front-end React code where we are handling the UI for the file upload, we need to ensure we specify the appropriate headers in our case. This value is required when we use forms that have a file input type element like the standard file upload button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const onChange = async () =&amp;gt; {
const formData = new FormData();

Object.entries(data).forEach(([key, value]) =&amp;gt; {
formData.set(key, value);
});
const config = {
headers: { 'content-type': 'multipart/form-data' },
};

const response = await axios.post('/api/upload', formData, config);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that is it! Next.js and Multer work seamlessly and allow us to handle whatever comes our way in terms of API routing and file uploads.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Conclusion...
&lt;/h2&gt;

&lt;p&gt;The power of Next.js and data fetching on the server side is a real nice-to-have in our front-end developer toolkit. As a full stack developer, if you have been reliant on having to stand up an entirely separate backend for simple API, data-fetch-like requests, these features of the server side of Next.js may come in handy and keep your application as lightweight as possible.&lt;/p&gt;

&lt;p&gt;Whether you’re a front-end dev or full-stack, I hope this post has shown you the benefit of server-side Next.js – from Static Generation to SSR to Multi-Part Form Data Uploading. Give it a try sometime, let me know what you think, and be sure to subscribe to the &lt;a href="https://keyholesoftware.com/blog/"&gt;Keyhole Dev Blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>programming</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>KeyholeSoftware.Dev Releases Mobile CFSWater App</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Thu, 19 Jan 2023 22:09:15 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/keyholesoftwaredev-releases-mobile-cfswater-app-55gh</link>
      <guid>https://dev.to/keyholesoftware/keyholesoftwaredev-releases-mobile-cfswater-app-55gh</guid>
      <description>&lt;p&gt;The &lt;a href="https://keyholesoftware.dev/" rel="noopener noreferrer"&gt;KeyholeSoftware.Dev&lt;/a&gt; team is proud to announce the release of the mobile CFSWater application. The cross-platform mobile app is now available for free, in both the app store and the google play store for iOS and Android devices.&lt;/p&gt;

&lt;p&gt;CFS Water is a mobile application that reports scheduled and current water release and power generations for the following Army Corp of Engineer Tailwaters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bull Shoals, AR&lt;/li&gt;
&lt;li&gt;Tablerock, MO&lt;/li&gt;
&lt;li&gt;Norfolk, AR&lt;/li&gt;
&lt;li&gt;Greers Ferry, AR&lt;/li&gt;
&lt;li&gt;Beaver, AR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Please note: These reported water levels are subject to change and may not honor estimated schedules.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/cfswater/id1660268618" rel="noopener noreferrer"&gt;Download for iOS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.keyholesoftware.cfs&amp;amp;hl=en_US" rel="noopener noreferrer"&gt;Download for Android&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CFSWater is also available as a free text message tool to access current conditions for tailwaters and streams using Corps of Engineers and USGS data. For more information, visit the KHS{Convo} &lt;a href="https://keyholesoftware.dev/convo/cfswater/" rel="noopener noreferrer"&gt;website&lt;/a&gt; or check out other Keyhole Creations at &lt;a href="https://keyholesoftware.dev/" rel="noopener noreferrer"&gt;www.keyholesoftware.dev&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Information
&lt;/h2&gt;

&lt;p&gt;The mobile application was developed by a team of Keyhole Software consultants using Flutter. Flutter is an open-source UI development kit that helps to build, test, and deploy cross-platform applications from a single codebase.&lt;/p&gt;

&lt;p&gt;Written in Dart, flutter applications are built using widgets. Similar to React components, widgets describe a piece of application logic and provide element structure and separation.&lt;/p&gt;

&lt;p&gt;The team used an API for retrieving scheduled and current water generation information and scraped data from a Core of Engineer reservoir Dam site. ​​The data is scraped from those websites through our KHS{Convo} application framework, with endpoints hosted on Heroku.&lt;/p&gt;

&lt;p&gt;The KHS{Convo} application framework was built by the Keyhole Dev Team. It is designed as a customizable, Node-based server API, utilizing Twilio for SMS support. As both a conversation programming API and finite state machine algorithm, KHS{Convo} simplifies the conversational experience, making it easy to program mobile, SMS-based engagement with dynamic content.&lt;/p&gt;

&lt;p&gt;As far as functionality, the app reports scheduled and current water release and power generations for Army Corp and Engineer tailwaters and streams in the midwest region. These tailwaters include Beaver Lake, Tablerock Lake, Bull Shoals Lake, Norfolk, and Greers Ferry.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Want to Get Better at Java? Go Old School.</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Wed, 18 Jan 2023 19:49:27 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/want-to-get-better-at-java-go-old-school-43jm</link>
      <guid>https://dev.to/keyholesoftware/want-to-get-better-at-java-go-old-school-43jm</guid>
      <description>&lt;p&gt;So you’re a Java programmer, and you want to take your skills to a higher level. I’m going to suggest you take a project and go old school.&lt;/p&gt;

&lt;p&gt;Over the course of this blog, I’ll explain what “going old school” means as well as give you some tips and tricks to get started. We’re going to step away from most modern tools and go back to the basics, so you gain a deeper understanding of what Java is and how it works. By the end, you should be well on your way to improving your Java skill set.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="http://www.keyholesoftware.com"&gt;Keyholesoftware.com&lt;/a&gt; by Rik Scarborough on 1/3/23.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Don’t do this on a time-critical project. The tools we currently use have been developed to increase our productivity, so we can deliver these projects in a timely manner, which is a great thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However&lt;/strong&gt;, the way they’re designed has isolated us from the compiler and even the build file. We begin to rely too much on the editor to provide us with clues on the best way to code and the environment to set up and control the build process. To truly improve your Java skills, you’re going to need to step away from these.&lt;/p&gt;

&lt;p&gt;So instead, choose a side project, possibly a personal project, that you have control over and can spend some time trying out things.&lt;/p&gt;

&lt;p&gt;I will not suggest truly old-school Java programming. When I started in Java, we built Java classes with the &lt;code&gt;javac&lt;/code&gt; command. This led to writing shell scripts to build complex projects and finally, Makefiles using the Unix and Windows commands &lt;code&gt;make&lt;/code&gt; and &lt;code&gt;nmake&lt;/code&gt; respectively. I remember being thrilled when the &lt;a href="https://ant.apache.org/"&gt;Ant&lt;/a&gt; utility came out and we had a pure Java build tool.&lt;/p&gt;

&lt;p&gt;You certainly can go back that far if you’d like, but it will be more difficult to build anything but a very basic Java project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose a Build File
&lt;/h2&gt;

&lt;p&gt;After Ant was on the scene for a while, the &lt;a href="https://maven.apache.org/"&gt;Maven&lt;/a&gt; utility was introduced. As happy as we were with Ant, we eventually flocked to the easier-to-use Maven. Still later, &lt;a href="https://gradle.org/"&gt;Gradle&lt;/a&gt; was introduced. In my opinion, Gradle is a much more powerful tool. I wrote a &lt;a href="https://keyholesoftware.com/2013/09/09/gradle-do-we-need-another-build-tool/"&gt;blog&lt;/a&gt; about it quite a few years ago. Your &lt;a href="https://en.wikipedia.org/wiki/Integrated_development_environment"&gt;IDE&lt;/a&gt; likely uses one of these under the covers to build your other projects.&lt;/p&gt;

&lt;p&gt;So choose your build tool and use the links above to find the getting started documentation for that tool. Once you have your build file (and possibly a stubbed main class), you can turn your attention to editing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editing
&lt;/h2&gt;

&lt;p&gt;You certainly can use your IDE of choice at this point to edit both the build file and Java source files. This provides several nice-to-have features, including code completion, creating any new Java source files in the correct directory for the package, and informing you of code errors before running the compiler.&lt;/p&gt;

&lt;p&gt;But it also isolates you from the reasons why these are important, and without full understanding, it’s almost impossible to truly improve your Java skills.&lt;/p&gt;

&lt;p&gt;Even if you use your IDE for this project, I would suggest having a good editor on your system. There are several good programmer editors for your operating system of choice, but I’d like to take a moment to promote the &lt;a href="https://www.vim.org/"&gt;Vim&lt;/a&gt; editor.&lt;/p&gt;

&lt;p&gt;The first thing I’ll say about Vim is to get out of it, hit the escape key a few times, then press the &lt;code&gt;:&lt;/code&gt; key and type &lt;code&gt;q!&lt;/code&gt; followed by the &lt;code&gt;enter&lt;/code&gt; key.&lt;/p&gt;

&lt;p&gt;The learning curve for Vim is steep, but I find that as a programmer editor, it is invaluable, especially for refactoring code. As an aside, I wrote this article initially in Vim using the markdown language.&lt;/p&gt;

&lt;p&gt;The biggest complaint I hear about Vim is that people launch it and then can’t figure out how to get out of it. Because of that, they never want to use it again. And that is why I started out with the key sequence to get out of it; now you know.&lt;/p&gt;

&lt;p&gt;When creating Java source files outside of an IDE, you have to be very careful about the directory structure you are placing the files. They must first conform to the location the build file is expecting them. The startup documentation for the build tool will show you that.&lt;/p&gt;

&lt;p&gt;Second, you have to follow the package structure you are using. Also, be aware that even in Windows, upper and lowercase are important. In Windows, it will allow you to create the file with letters that are a different case than you use in the source code for the package and class names, but it will create problems for you. In any Unix system, the compiler will have a much harder time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Completion
&lt;/h2&gt;

&lt;p&gt;We, or at least I, have gotten very used to being able to use the IDE to look up method names for the class I am using at any particular time. Before this very helpful innovation, I would have the &lt;a href="https://docs.oracle.com/en/java/javase/18/docs/api/index.html"&gt;JDK documentation&lt;/a&gt; in a browser window at all times.&lt;/p&gt;

&lt;p&gt;We had to write our own frameworks (uphill, both ways) but most current frameworks will have similar documentation pages as well. Both &lt;a href="https://apache.org/"&gt;Apache&lt;/a&gt; and &lt;a href="https://spring.io/projects/spring-framework"&gt;Spring&lt;/a&gt; are especially good at that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Control
&lt;/h2&gt;

&lt;p&gt;Whether you are using Git or Subversion or some other source or version control software, try using it from the command line. This will force you to become a little more familiar with how it works and the different options it has.&lt;/p&gt;

&lt;p&gt;If this is a personal project, you may not have a remote repository to sync this to. However, source control is still helpful; it will allow you to track the changes you make to the project as you work on it.&lt;/p&gt;

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

&lt;p&gt;Some of the most frustrating coding sessions seem to come from not fully understanding the tools we are using. I have had discussions with young developers that are frustrated because the environment is not working the way they think it should. Once I explain the reasons things are happening, the frustrations disappear and they are able to move on with the project.&lt;/p&gt;

&lt;p&gt;Taking a step back from modern tools will improve your Java skills by growing your understanding. Understanding is a foundation every technician in any skills-based career should strive for.&lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
    </item>
    <item>
      <title>Jamstack: Azure Serverless Function App With React</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Wed, 18 Jan 2023 18:47:21 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/jamstack-azure-serverless-function-app-with-react-24na</link>
      <guid>https://dev.to/keyholesoftware/jamstack-azure-serverless-function-app-with-react-24na</guid>
      <description>&lt;p&gt;A new trend of creating applications is emerging called &lt;a href="https://jamstack.org/what-is-jamstack/"&gt;Jamstack&lt;/a&gt;. No, this isn’t slapping together your favorite flavor of jelly (grape is the best) with peanut butter and two pieces of bread. The intent is an architecture that is faster, more secure, and easier to scale. It focuses on pre-rending and decoupling. This way, the solutions created are more reliable and resilient than before.&lt;/p&gt;

&lt;p&gt;Pre-rendering comes by the way of using a static website via a CDN for high availability and security. No more serving your React app via web server like we’ve become accustomed to. It reduces cost and complexity by eliminating the regular maintenance and configuration of traditional servers.&lt;/p&gt;

&lt;p&gt;Also, the idea of APIs and the ability to move them to things like Serverless functions creates more cost savings, elimination of traditional servers, and use of features only when they are requested. For more information, check out the &lt;a href="https://www.serverless.com/"&gt;Serverless website&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="http://keyholesoftware.com"&gt;Keyholesoftware.com&lt;/a&gt; by Matt McCandless on 12/20/22.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Making a lovely PB&amp;amp;J
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bread&lt;/strong&gt;&lt;br&gt;
Let’s consider bread to be the cloud provider of your choice. For today’s example, we will be using Azure. There are many flavors of bread: AWS, Azure, GCP, and others (all with their own tasty bits and options), but really, it’s just the bread. The good stuff is what’s in the middle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Peanut Butter&lt;/strong&gt;&lt;br&gt;
Here, we are going to look at the Serverless function apps in Azure as our peanut butter. Peanut butter can be complex. Whether it be creamy, crunchy, whipped, or natural – they all serve a purpose, so it can be complicated to choose what we want.&lt;/p&gt;

&lt;p&gt;Personally, I think crunchy peanut butter gives the sandwich some texture and is the way to go. At any rate, our peanut butter Serverless function app will provide the more complex part of our code. We will set up our backend code to provide our endpoints – login, user listing, and updating the role.&lt;/p&gt;

&lt;p&gt;So, how do we get this all working? We create a regular Node.js application. Time to code!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coding the Peanut Butter&lt;/strong&gt;&lt;br&gt;
First, we will set up Serverless on our local machine. Using the link mentioned above, we can follow the steps to install Serverless on our local machine. This will allow us to create, edit, and run the code locally but in an “offline” mode. This way, we can debug things locally before we ever push anything up to Azure.&lt;/p&gt;

&lt;p&gt;First, we need to install the serverless tools locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -g serverless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a new function app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;servless create -t azure-nodejs -p &amp;lt;appName&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After it is done, change into that directory and run.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Once you have done this, let’s take a closer look at the &lt;code&gt;serverless.yml&lt;/code&gt;file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service: &amp;lt;serviceName&amp;gt;

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
frameworkVersion: '3'

provider:
  name: azure
  region: Central US
  runtime: nodejs12
  resourceGroup: &amp;lt;resourceGroup&amp;gt;
  # os: windows  # windows is default, linux is available
  # prefix: "sample"  # prefix of generated resource name
  # subscriptionId:
  # stage: dev
  # type: premium  # premium azure functions
  WEBSITE_RUN_FROM_PACKAGE: 1

  environment: # these will be created as application settings
    VARIABLE_FOO: 'foo'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service name is whatever you’d like to name it, but you will leave the rest the same. Name a resource group or use one that you already have defined. By default, Windows is the OS of choice.&lt;/p&gt;

&lt;p&gt;Further down, let’s take a look at the function mapping. All it’s really doing is mapping where your function lives, the methods accepted, the authorization level to use it, and the type of event it is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;functions:
  login:
    handler: src/handlers/login.login
    events:
      - http: true
        methods:
          - POST
        authLevel: anonymous # can also be `function` or `admin`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, you would write your code to authenticate your user. This article won’t get into specifics, as there are many ways in which you can do so. What we will focus is on how to handle logins from a Serverless setting.&lt;/p&gt;

&lt;p&gt;Create your login handler like the example below, and import anything that you need to. The key is to return the response in the context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const login = require("../login");

module.exports.login = async function (context, req) {
  context.log('JavaScript HTTP trigger function login request.');
  context.res.headers = { "Content-Type": "application/json" };
  context.res = await login(req, context.res);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the original login code that we used on a traditional implementation was left intact. In this approach, we can, if need be, revert to a traditional setup with minimal effort.&lt;/p&gt;

&lt;p&gt;To test locally now, use the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless offline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run the Serverless functions locally and list out the available endpoints and the URLs to call them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Functions:        login: [POST] http://localhost:7071/api/login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your function is available to test via Postman or other tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy the Peanut Butter&lt;/strong&gt;&lt;br&gt;
This is where things get sticky. Deploying to Azure isn’t extremely clear using the Serverless tools. This article assumes you have an Azure account already. Be sure that you have the proper permissions to the subscription and write permissions.&lt;/p&gt;

&lt;p&gt;Avoid getting peanut butter in your hair. Make sure these are all in place before running any of this via the CLI tool. Instead of regurgitating the steps, I advise you to visit this &lt;a href="https://www.serverless.com/framework/docs/providers/azure/guide/credentials"&gt;link&lt;/a&gt; and follow the steps.&lt;/p&gt;

&lt;p&gt;Once all of that is set up, then run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all goes well, it will zip up your app and deploy it to Azure. Then, you can pop over to Azure Function Apps and look at it there. Once it is running, you can use Postman to try your login or even use the test/run option available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jelly Time: Grape, of course!&lt;/strong&gt;&lt;br&gt;
It’s jelly time, and in my opinion, there’s only one flavor worth using: grape. By grape, I mean a React-based web application. A lot of times, people forget that React builds a static output that can actually just be hosted on any web server. This allows us to take our application and host it via a CDN.&lt;/p&gt;

&lt;p&gt;We won’t get into specifics of how to host in this article. Azure, AWS, and GCP all have different flavors of hosting a CDN where we can add our static files to be hosted. This allows for a scalable and highly available static website with minimal setup and configuration.&lt;/p&gt;

&lt;p&gt;All you need to do is create your frontend application like you normally do with a login, lists, forms, etc. The only difference is that now you point your API calls to the Serverless calls that were listed above.&lt;/p&gt;

&lt;h2&gt;
  
  
  PB&amp;amp;J Perfection
&lt;/h2&gt;

&lt;p&gt;Hopefully, you can see that this approach is fairly simple to carry out. You don’t need to change how you develop a whole lot. The key is how you are going to host and deploy your functions.&lt;/p&gt;

&lt;p&gt;Your backend APIs aren’t eating up computing time and are much more shareable now. There are some studies that suggest that around 80% of computing time is idle. In a Serverless environment, you only use compute time when needed.&lt;/p&gt;

&lt;p&gt;A CDN-hosted website is a prime way to create a highly scalable and available website application with little to no overhead. It is almost like stealing the jelly for your sandwich.&lt;/p&gt;

&lt;p&gt;Hopefully, you enjoyed this tasty treat and are ready to start making your own Jamstack solutions! If you enjoyed this snack, make sure you check out the others on the &lt;a href="https://keyholesoftware.com/blog/"&gt;Keyhole Dev Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>azure</category>
      <category>react</category>
      <category>node</category>
    </item>
    <item>
      <title>Monitoring SQL Server Agent Jobs with Spring Batch</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Mon, 12 Dec 2022 21:11:25 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/monitoring-sql-server-agent-jobs-with-spring-batch-2pf7</link>
      <guid>https://dev.to/keyholesoftware/monitoring-sql-server-agent-jobs-with-spring-batch-2pf7</guid>
      <description>&lt;p&gt;In this blog post, I will demonstrate a technique to query the status of a SQL Server Agent job that executes the SSIS package. SQL Server Agent scripts are asynchronous in nature. This can make it difficult to make code decisions based on the execution status of a script of this type.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="http://keyholesoftware.com"&gt;Keyholesoftware.com&lt;/a&gt; by Josh Green on 12/8/22.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;In this example, we’ll be using Java. I’ve been assisting a recent client in renovating their legacy Model 204 (M204) IBM mainframe codebase, building a more elegant distributed package instead. One relic of the mainframe days is a function named Fast Unloads, ironically nicknamed “Funloads.”&lt;/p&gt;

&lt;p&gt;These Funloads can best be described as ETL transactions. There may be a file with millions of lines of data that needs to be uploaded to a SQL table or a massive SQL result set that needs to be written to a file. These downstream files may be used by an M204 or COBOL program as input for processing.&lt;/p&gt;

&lt;p&gt;In the distributed world, these Funloads have been translated to SQL and wrapped within an SQL Server Agent call to an SSIS package. This process has been baked into a Spring Batch job as a job step to mimic mainframe JCL or job code.&lt;/p&gt;

&lt;p&gt;The only drawback to this plan is the asynchronous nature of the SQL Server Agent job. The Java/Spring Batch code could start the process, but the Batch job is not inherently smart enough to wait for the output file before moving on to subsequent steps that may need that file as a dependency.&lt;/p&gt;

&lt;p&gt;Let’s begin with the call to execute the SSIS package.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL Server Agent Job Call
&lt;/h2&gt;

&lt;p&gt;In the code below we pass in the name of the SQL Server Agent job to the function as &lt;code&gt;funloadName&lt;/code&gt;. This kicks off the SQL Server Agent job to then execute an SSIS package. The SSIS package runs a query and then writes a dataset out to a file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected void executeSsisFunload(String funloadName) {

        LOGGER.info("Funload name [" + funloadName + "]");

        try (Connection con = DriverManager.getConnection(establishSsisConnection()); Statement stmt = con.createStatement();) {

            String sql = "EXEC msdb.dbo.sp_start_job N'" + funloadName + "'";
            LOGGER.info("Expected SQL [" + sql + "]");

            stmt.execute(sql);
        }
        // Handle any errors that may have occurred.
        catch (SQLException e) {
            LOGGER.info("Funload [" + funloadName + "] failed", e);
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Checking The Status Of The SSIS Package
&lt;/h2&gt;

&lt;p&gt;The next portion of the code is arguably the most important. This code runs an SQL query against an internal SSIS database that contains information related to each package execution.&lt;/p&gt;

&lt;p&gt;Using this query, we ask the job control tables for the status of our specific running package. Based on the results of this query we either (a) wait 20 seconds and try again, or (b) continue on to the next step in our Spring Batch job.&lt;/p&gt;

&lt;p&gt;The looping mechanism is essential because of the unexpected duration of the SSIS package to run to completion. It was discovered early on that a downstream process was beginning to FTP the output file from our SSIS package before it was finished. This query and looping mechanism prevents any FTP actions before the file is completely written.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected void checkForFunloadFile(String funloadName) {
        StringBuilder ssisStatusString = new StringBuilder();
        ssisStatusString.append("SELECT ");
        ssisStatusString.append("EXECUTION_ID ");
        ssisStatusString.append(", FOLDER_NAME ");
        ssisStatusString.append(", PROJECT_NAME ");
        ssisStatusString.append(", PACKAGE_NAME ");
        ssisStatusString.append(", ENVIRONMENT_NAME ");
        ssisStatusString.append(", EXECUTED_AS_NAME ");
        ssisStatusString.append(", START_TIME ");
        ssisStatusString.append(", END_TIME ");
        ssisStatusString.append(", CASE STATUS WHEN 1 THEN 'CREATED'");
        ssisStatusString.append("WHEN 2 THEN 'RUNNING' ");
        ssisStatusString.append("WHEN 3 THEN 'CANCELLED' ");
        ssisStatusString.append("WHEN 4 THEN 'FAILED' ");
        ssisStatusString.append("WHEN 5 THEN 'PENDING' ");
        ssisStatusString.append("WHEN 6 THEN 'ENDED UNEXPECTEDLY' ");
        ssisStatusString.append("WHEN 7 THEN 'SUCCEEDED' ");
        ssisStatusString.append("WHEN 8 THEN 'STOPPING' ");
        ssisStatusString.append("ELSE 'COMPLETED' END AS STATUS ");
        ssisStatusString.append(", CALLER_NAME ");
        ssisStatusString.append(", PROCESS_ID ");
        ssisStatusString.append(", SERVER_NAME ");
        ssisStatusString.append("FROM INTERNAL.EXECUTION_INFO ");
        ssisStatusString.append("WHERE PACKAGE_NAME = '" + funloadName + ".DTSX' ");
        ssisStatusString.append("AND STATUS = 7");

        // Declare integer to
        int ssisResultInteger = 1;
        if (ssisResultInteger == 1) {
            try (Connection con = DriverManager.getConnection(establishSsisConnection()); Statement stmt = con.createStatement();) {

                LOGGER.info("Expected SQL [" + ssisStatusString.toString() + "]");
                ResultSet ssisStatusResult = stmt.executeQuery(ssisStatusString.toString());

                ssisResultInteger = ssisStatusResult.getRow();

                LOGGER.info("Result Integer = [" + ssisResultInteger + "]");

                if (ssisResultInteger &amp;gt;= 1) {

                    LOGGER.info("The current funload [" + funloadName + " has not finished. Looping again...");

                    try {
                        // Wait 20 seconds
                        Thread.sleep(20000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }


            // Handle any errors that may have occurred.
            catch (SQLException e) {
                LOGGER.info("Funload [" + funloadName + "] failed", e);
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Steps
&lt;/h2&gt;

&lt;p&gt;The output file from the SSIS package is created in a mounted NFS drop location. Once the query returns no results, we can move to the next step of our job with a fully written file. Nice work!&lt;/p&gt;

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

&lt;p&gt;The asynchronous nature of some processes can be difficult to wrestle with in a code structure dependent on return codes and exit statuses such as Spring Batch. Above is the process that worked best for me!&lt;/p&gt;

&lt;p&gt;I’m sure there are many ways to tackle this problem, so feel free to drop a comment with your thoughts! If you like what you read, be sure to check out the &lt;a href="https://keyholesoftware.com/blog/"&gt;Keyhole Dev Blog&lt;/a&gt; for tons of other great articles.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springbatch</category>
      <category>sql</category>
      <category>modernization</category>
    </item>
    <item>
      <title>[Video] Deep Dive Into Vue.js</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Mon, 12 Dec 2022 19:21:16 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/video-deep-dive-into-vuejs-26nl</link>
      <guid>https://dev.to/keyholesoftware/video-deep-dive-into-vuejs-26nl</guid>
      <description>&lt;p&gt;&lt;em&gt;A 45-minute deep dive into the Vue.js framework, including its technical philosophies, how it differs from the React library, and how it should be applied to new applications at an enterprise scale.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Recording&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The video was recorded at a Keyhole Software Public Education Event in December 2022. Speaker Zach Gardner is a Senior Consultant and Architect at Keyhole Software with over 15 years of experience helping clients exceed their goals with custom software solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video Overview
&lt;/h2&gt;

&lt;p&gt;​This presentation dives into the Vue.js framework, including its technical philosophies, how it differs from the React library, and how it should be applied to new applications at an enterprise scale. ​&lt;/p&gt;

&lt;p&gt;The top two current ​contenders for greenfield UI development are React and Vue.js. Most JavaScript developers are familiar with React, though few have worked on Vue. Both frameworks have a similar heritage and are more straightforward than competitors (e.g., Angular), and this presentation will explore their differences and similarities. ​This includes the pros/cons and parallels of how Vue approaches events, conditional rendering, computed, component lifecycle events, watchers, and events compared to React.&lt;/p&gt;

&lt;p&gt;The presentation also includes a common sense perspective into when our clients might best consider Vue.js versus React. If you want to expand your knowledge of popular JavaScript libraries, this presentation is for you.&lt;/p&gt;

</description>
      <category>challenge</category>
      <category>testing</category>
    </item>
    <item>
      <title>MockOla v3 Released: Free Design Canvas For Developers</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Thu, 17 Nov 2022 16:48:00 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/mockola-v3-released-free-design-canvas-for-developers-2bpe</link>
      <guid>https://dev.to/keyholesoftware/mockola-v3-released-free-design-canvas-for-developers-2bpe</guid>
      <description>&lt;p&gt;Keyhole Software is proud to announce the v3.0 release of &lt;a href="https://mockola.com"&gt;MockOla&lt;/a&gt;, a free web-based design software for software developers. This release provides significant feature and usability updates, including a formal template gallery that simplifies the illustration of complex software design concepts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s New in MockOla 3.0&lt;/strong&gt;&lt;br&gt;
In version 3.0, we add a formal template gallery with IT templates for &lt;strong&gt;application architecture, cloud, .NET, Java, UML, UI, and other software concepts&lt;/strong&gt;. Users can quickly modify and customize predesigned templates to minimize design time. &lt;a href="https://mockola.com"&gt;MockOla&lt;/a&gt; v3.0 also adds an image gallery containing specialized images and logos, allowing users to make effective, communicative diagrams.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dBR6J9p5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vc6uqbdx0jnbdi7dqzcc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dBR6J9p5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vc6uqbdx0jnbdi7dqzcc.png" alt="Screenshot of App Orchestration Diagram" width="880" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;“Software concepts are often complex to illustrate,” Keyhole Director of Operations Lauren Bogner said. “The goal with MockOla v3.0 was to make designing them even easier, especially for our IT users. The templates fit a wide range of business and application needs, particularly in software design.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mockola.com"&gt;MockOla&lt;/a&gt; is a free, web-based design and diagramming tool for creating custom designs. This software empowers users to create IT diagrams, user interface wireframes, and freeform designs by dragging and dropping customizable elements onto a canvas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5jyitUR0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eixba63t1smj8hshgnmu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5jyitUR0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eixba63t1smj8hshgnmu.png" alt="MockOla Screenshot Of mobile UI" width="880" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mockola.com"&gt;MockOla&lt;/a&gt; boasts a long list of features, including specialized item palettes like shapes, architecture, UI, data, object relationship management to illustrate flow, software logo gallery, visual layer design, and responsive content. After creation, users can save and download diagrams as JSON or image files for efficient sharing and collaboration.&lt;/p&gt;

&lt;p&gt;“We designed it to be useful for devs who don’t have the time to start from scratch,” Managing Partner David Pitt said. “MockOla now lets users easily drop in a web application architecture template, and with a few customizations make it their own. I use it frequently to quickly design application diagrams and UI mockups for documentation and project proposals.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mockola.com"&gt;MockOla&lt;/a&gt; was developed by Keyhole Software with React, Node.JS, Express, MongoDB, CosmosDB, and Docker and is hosted in Azure. MockOla is free to use. For more information, please visit &lt;a href="https://mockola.com"&gt;MockOla.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About Keyhole&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://mockola.com"&gt;MockOla&lt;/a&gt; was developed by &lt;a href="https://keyholesoftware.com"&gt;Keyhole Software&lt;/a&gt; is a custom development and software consulting firm comprised solely of elite, vetted employees who are as passionate as they are skilled. They build custom solutions that solve complex business problems and have a long track record of delivering on time and on budget. Key technologies utilized include Java, .NET, JavaScript, Cloud, and others.&lt;/p&gt;

</description>
      <category>freetools</category>
      <category>showdev</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Moving Google Tag Manager from Xamarin to Flutter</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Tue, 26 Apr 2022 16:12:42 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/moving-google-tag-manager-from-xamarin-to-flutter-3iac</link>
      <guid>https://dev.to/keyholesoftware/moving-google-tag-manager-from-xamarin-to-flutter-3iac</guid>
      <description>&lt;p&gt;While working with a major theater chain, I was tasked with implementing Analytics into their Xamarin app. Part of that work included implementing Google Tag Manager. At a later date, I was again tasked with implementing Google Tag Manager but this time, into their Flutter app. I expected the implementation to be similar and definitely easier the second time around, but I found that to be false. The setup had some major twist and turns that I didn't expect.&lt;/p&gt;

&lt;p&gt;Although this was sometimes a frustrating challenge, I had fun figuring it out. Most companies running their apps with Xamarin will be moving their apps to a newer SDK like Flutter at some point. Flutter allows cross platform development for Android, iOS, Linux, macOS, Windows, Google Fuchsia, and Web from a single codebase and shared languages. So this move is almost inevitable.&lt;/p&gt;

&lt;p&gt;This is where the idea for this blog came from. The transition is complicated, and I could have used a blog like this one to explain the transition to Flutter. Plus, even if you haven’t been faced with this shift yet, there’s a good chance you will in the future. Hopefully, whether it’s now or at a later date, this post will save some of you from spending painful time and effort transitioning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In this post, I’ll take you through a step-by-step tutorial of transferring from Xamarin to Flutter. By the end, GTM will be in your Flutter app! Before we get started, I’ve listed a brief overview of the steps below.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post was originally published on the &lt;a href="https://keyholesoftware.com/2022/04/22/moving-google-tag-manager-from-xamarin-to-flutter/"&gt;Keyhole Software blog&lt;/a&gt; by &lt;a href="https://keyholesoftware.com/author/alink/"&gt;Andy Link&lt;/a&gt; on 4/22/2022.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;b&gt;Android&lt;/b&gt;&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Grab the Container Files from Xamarin&lt;/li&gt;
    &lt;li&gt;Copy the Environment Container Files into Flutter&lt;/li&gt;
    &lt;li&gt;Add the Default Container File&lt;/li&gt;
    &lt;li&gt;Create the Python Copy Script for Prod&lt;/li&gt;
    &lt;li&gt;Add the GTM Dependency&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;b&gt;iOS&lt;/b&gt;&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Grab the Container Files from Xamarin&lt;/li&gt;
    &lt;li&gt;Copy the Environment Container Files into Flutter&lt;/li&gt;
    &lt;li&gt;Add the Default Container File&lt;/li&gt;
    &lt;li&gt;Create the Xcode Build Phase (prod Run Script)&lt;/li&gt;
    &lt;li&gt;Add the GTM Dependency&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No matter how daunting a task this may seem, together, we’ll get through it. This blog should seriously help ease your pain when moving your current GTM setup in Xamarin to Flutter. Let’s get started!&lt;/p&gt;

&lt;h2&gt;Android&lt;/h2&gt;

&lt;p&gt;We’ll begin with Android.&lt;/p&gt;

&lt;h3&gt;Step 1: Grab the Android Container Files from Xamarin&lt;/h3&gt;

&lt;p&gt;First, let's grab all the &lt;code&gt;GTM-XXXXXXX.json&lt;/code&gt; container files from your Droid Xamarin project. The files will be located in &lt;code&gt;Droid\Assets\containers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gz17p4Hc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gz17p4Hc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-1.png" alt="" width="232" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy these files to your Desktop, so you can easily access and copy them later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BQ8E50Ja--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BQ8E50Ja--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-2.jpg" alt="" width="512" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, you now have your Android container files ready to copy into your Flutter app!&lt;/p&gt;

&lt;h3&gt;Step 2: Copy your Android Environment Container Files into Flutter&lt;/h3&gt;

&lt;p&gt;You can place these container files anywhere inside the &lt;code&gt;android\app&lt;/code&gt; folder. For my app, I’ve placed them under &lt;code&gt;android\app\google_services\dev\containers&lt;/code&gt; and &lt;code&gt;android\app\google_services\prod\containers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e7QrQQxt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e7QrQQxt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-3.png" alt="" width="302" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new android folders will act as a storing location for each environment's container file. These files won’t directly be used during runtime, but one of these will be copied into the proper runtime folder during app build.&lt;/p&gt;

&lt;h3&gt;Step 3: Add the Default Android Container File&lt;/h3&gt;

&lt;p&gt;If you need/want GTM setup for local android development, you’ll still need to add the dev container file inside &lt;code&gt;android\app\src\main\containers&lt;/code&gt; as the default file. This way, Flutter can always reference a file during runtime without any file movement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TxNHJHRo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TxNHJHRo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-4.png" alt="" width="298" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; The &lt;a href="https://developers.google.com/tag-platform/tag-manager/android/v5#add-gtm-to-project" rel="noopener"&gt;official documents&lt;/a&gt; state you’ll need your container file inside the &lt;code&gt;android\app\assets\containers&lt;/code&gt; folder during runtime. My setup was a tad different since my app requires this file be in the &lt;code&gt;android\app\src\main\assets\containers&lt;/code&gt; folder. You’ll have to confirm which location works for you.&lt;/p&gt;

&lt;p&gt;For my setup, I not only wanted GTM working locally for Android, but I also wanted to use the same dev container file for every environment outside of the prod environment. Adding the dev container file into &lt;code&gt;android\app\src\main\containers&lt;/code&gt; as the default file has kept me from needing to create multiple copy scripts for each environment. There’s only a need for one copy script for prod!&lt;/p&gt;

&lt;h3&gt;Step 4: Create the Android Prod Copy Script&lt;/h3&gt;

&lt;p&gt;In Xamarin, the environment container files existed side by side in the &lt;code&gt;Droid\Assets\containers&lt;/code&gt; folder. This was only possible because the &lt;code&gt;Droid.csproj&lt;/code&gt; file controlled which file the project included based on the project's current configuration (ex. Debug, Dev, Test, Prod).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tk-ADzLV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tk-ADzLV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-5.jpg" alt="" width="512" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Xamarin and Flutter, it's &lt;b&gt;REQUIRED&lt;/b&gt; that only a single Android container file is included in the &lt;code&gt;containers&lt;/code&gt; folder during runtime. Otherwise, the app doesn’t know which file to use and will use the first container file it finds. This is a no go if you care about your test data being logged in production and vice-versa!&lt;/p&gt;

&lt;p&gt;Since Flutter doesn’t have a &lt;code&gt;Droid.csproj&lt;/code&gt; file to accomplish the Xamarin solution, we’ll need to create another solution to swap the container file inside Flutter’s &lt;code&gt;android\app\assets\containers&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;For my Flutter app, the easiest solution was to create a python script that Jenkins would execute. Inside my Jenkins folder, I added a new &lt;code&gt;android_prod.py&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RmI23Ub1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RmI23Ub1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-6.png" alt="" width="295" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, I added this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import shutil

## Override Google Tag Manager container file with the PROD version
# Deletes GTM file in containers folder
os.remove("android/app/src/main/assets/containers/GTM-XXXXXXX.json")
# Copies Prod GTM File to the needed containers folder
shutil.copyfile("android/app/google_services/prod/containers/GTM-XXXXXXX.json", "android/app/src/main/assets/containers/GTM-XXXXXXX.json")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; Replace the &lt;code&gt;GTM-XXXXXXXX.json&lt;/code&gt; with your appropriate dev and prod container file names.&lt;/p&gt;

&lt;p&gt;Your setup should now work for both dev and prod environments. We’re almost done with the Android setup. The last step is to install the GTM packages.&lt;/p&gt;

&lt;h3&gt;Step 5: Add the Android GTM Dependency&lt;/h3&gt;

&lt;p&gt;Add…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;compile "com.google.android.gms:play-services-tagmanager:17.0.0" 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…as a dependency to your &lt;code&gt;app\build.gradle&lt;/code&gt; file like so.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ey62hb__--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ey62hb__--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-7.jpg" alt="" width="512" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now the Android setup is complete! Nice work. Let’s move to the iOS setup.&lt;/p&gt;

&lt;h2&gt;iOS&lt;/h2&gt;

&lt;h3&gt;Step 1:Grab the iOS Container Files from Xamarin&lt;/h3&gt;

&lt;p&gt;First, let's grab all the &lt;code&gt;GTM-XXXXXXX.json&lt;/code&gt; container files from your iOS Xamarin project. The files will be located in &lt;code&gt;iOS\container&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ylGatUAP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ylGatUAP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-8.png" alt="" width="272" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy these files to your Desktop so you can easily access and copy them later. Now, on your Desktop, create a folder called container and create subfolders called dev and prod inside.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LWPOQMph--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LWPOQMph--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-9.jpg" alt="" width="512" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the iOS dev and prod GTM container files you pulled from Xamarin into each subfolder.&lt;/p&gt;

&lt;p&gt;Great, you now have your iOS container files ready to copy into your Flutter app!&lt;/p&gt;

&lt;h3&gt;Step 2: Copy the iOS Environment Container Files into Flutter&lt;/h3&gt;

&lt;p&gt;First, open your project in Xcode.&lt;/p&gt;

&lt;p&gt;Next, you’ll copy that new container folder you created on your Desktop into the Xcode project navigator. Copy it inside to the &lt;code&gt;Runner\Runner&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; It’s required that you add the container folder through Xcode. Xcode won’t see the folder reference if you add it through the Mac Finder file explorer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZbecBsbH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-10.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZbecBsbH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-10.jpg" alt="" width="269" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new ios folders will act as a storing location for each environment's container file. These files won’t directly be used during runtime, but one of these will be copied into the proper runtime folder during app build.&lt;/p&gt;

&lt;h3&gt;Step 3: Add the Default iOS Container File&lt;/h3&gt;

&lt;p&gt;If you need/want GTM setup for local iOS development, you’ll still need to add the dev container file inside &lt;code&gt;Runner\Runner\container&lt;/code&gt; as the default file. This way, Flutter can always reference a file during runtime without any file movement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fvgHLKTg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-11.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fvgHLKTg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-11.jpg" alt="" width="273" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; The &lt;a href="https://developers.google.com/tag-platform/tag-manager/ios/v5" rel="noopener"&gt;official documents&lt;/a&gt; state you’ll need to put your container file inside the &lt;code&gt;ios\Runner\container&lt;/code&gt; folder during runtime.&lt;/p&gt;

&lt;p&gt;For my setup, I not only wanted GTM working locally for iOS, but I also wanted to use the same dev container file for every environment outside of the prod environment. Adding the dev container file into &lt;code&gt;ios\Runner\container&lt;/code&gt; as the default file has kept me from needing to create multiple copy scripts for each environment. There’s only a need for one copy script for prod!&lt;/p&gt;

&lt;h3&gt;Step 4: Create the iOS Xcode Build Phase (prod Run Script)&lt;/h3&gt;

&lt;p&gt;In Xamarin, the environment container files existed side by side in the &lt;code&gt;iOS\container&lt;/code&gt; folder. This was only possible because the &lt;code&gt;iOS.csproj&lt;/code&gt; file controlled which file the project included based on the project's current configuration (ex. Debug, Dev, Test, Prod).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bHAqj2ZC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-12.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bHAqj2ZC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-12.jpg" alt="" width="437" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Xamarin and Flutter it's &lt;b&gt;REQUIRED&lt;/b&gt; that only a single iOS container file is included in the &lt;code&gt;container&lt;/code&gt; folder during runtime. Otherwise, the app doesn’t know which file to use and will use the first container file it finds. This is a no go if you care about your test data being logged in production and vice-versa!&lt;/p&gt;

&lt;p&gt;Since Flutter doesn’t have a &lt;code&gt;iOS.csproj&lt;/code&gt; file, to accomplish the Xamarin solution, we’ll need to create another solution to swap the container file inside Flutter’s &lt;code&gt;ios\Runner\container&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;For my Flutter app, the easiest solution was to create a Build Phase (Run Script) inside Xcode.&lt;/p&gt;

&lt;p&gt;In Xcode, go to &lt;code&gt;Runner&lt;/code&gt; -&amp;gt; &lt;code&gt;Targets Runner&lt;/code&gt; -&amp;gt; &lt;code&gt;Build Phase&lt;/code&gt; and click the &lt;code&gt;+&lt;/code&gt; button to create a new Run Script. Call it Copy environments GoogleTagManager config file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---nNP6kN---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Screen-Shot-2022-04-25-at-4.53.39-PM.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---nNP6kN---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Screen-Shot-2022-04-25-at-4.53.39-PM.jpg" alt="" width="880" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside the new Run Script add this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Removes all GTM-#######.json files inside only the parent container folder. This does not delete the GTM-#######.json files inside the dev and prod subfolders. We need the subfolder GTM-#######.json files intact so we can grab them during the copy process farther down the file
rm -vf ${PROJECT_DIR}/Runner/container/GTM*

# Environment Constants
PRODUCTION_ENVIRONMENT=Release-production
PRODUCTION_GTM_FILE_NAME=GTM-XXXXXXX.json
PROD_ENVIRONMENT_FOLDER=prod
DEV_GTM_FILE_NAME=GTM-XXXXXXX.json
DEV_ENVIRONMENT_FOLDER=dev

# Variables are adjusted if CONFIGURATION is equal to the PRODUCTION_ENVIRONMENT
ENVIRONMENT=$DEV_ENVIRONMENT_FOLDER
GTM_FILE_NAME=$DEV_GTM_FILE_NAME
if [[ "$CONFIGURATION" == "$PRODUCTION_ENVIRONMENT" ]]; then
    GTM_FILE_NAME=$PRODUCTION_GTM_FILE_NAME
    ENVIRONMENT=$PROD_ENVIRONMENT_FOLDER
fi

#Once the ENVIRONMENT and GTM_FILE_NAME are set we build the file location for that environment file
GTM_INFO_FILE=${PROJECT_DIR}/Runner/container/${ENVIRONMENT}/${GTM_FILE_NAME}

# Make sure the GTM_FILE_NAME exists
echo "Looking for ${GTM_FILE_NAME} in ${GTM_INFO_FILE}"
if [ ! -f $GTM_INFO_FILE ]; then
    echo "No ${GTM_FILE_NAME} found. Please ensure it's in the proper directory."
    exit 1
else
    echo "${GTM_FILE_NAME} config file was found"
fi
# Get a reference to the destination location for the GTM-XXXXXXX.json
# This is the default location where Firebase init code expects to find GTM-XXXXXXX.json file
GTM_DESTINATION=${PROJECT_DIR}/Runner/container/${GTM_FILE_NAME}

echo "Will copy ${GTM_FILE_NAME} to final destination: ${GTM_DESTINATION}"

# Copies GTM_INFO_FILE into the GTM_DESTINATION
cp ${GTM_INFO_FILE} ${GTM_DESTINATION}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; Replace the GTM-XXXXXXXX.json with your appropriate dev and prod container file names.&lt;/p&gt;

&lt;p&gt;Your setup should now work for both dev and prod environments. We’re almost done with the iOS setup. The last step is to install the GTM dependencies.&lt;/p&gt;

&lt;h3&gt;Step 5: Add the iOS GTM Dependency&lt;/h3&gt;

&lt;p&gt;Navigate to the iOS folder and look for a Podfile. If the Podfile already exists, skip these next steps.&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Open the terminal&lt;/li&gt;
    &lt;li&gt;Run &lt;code&gt;sudo gem install cocoapods&lt;/code&gt;
&lt;/li&gt;
    &lt;li&gt;Change to the iOS project directory&lt;/li&gt;
    &lt;li&gt;Run &lt;code&gt;pod init&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you’ve created or located your Podfile, you’ll add the dependency into the Podfile.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AXWtQ8ZI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-14.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AXWtQ8ZI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-14.jpg" alt="" width="512" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the terminal, change directories to the &lt;code&gt;ios&lt;/code&gt; folder and run &lt;code&gt;pod install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;iOS setup is complete.&lt;/p&gt;

&lt;p&gt;Great job! Android and iOS are now set up to use GTM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T2vTJIhM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-15.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T2vTJIhM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Link-15.jpeg" alt="" width="512" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;Transitioning to Flutter allows you to take advantage of writing code a single time, in the same languages. It also overcomes the traditional limitations of cross-platform work.&lt;/p&gt;

&lt;p&gt;After walking through this post, you now have GTM in your new Flutter app! It's a big accomplishment and one that'll benefit you for the foreseeable future.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you found this helpful, check out some of the other posts on the &lt;a href="https://keyholesoftware.com/blog/"&gt;Keyhole Dev Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>xamarin</category>
      <category>flutter</category>
      <category>mobile</category>
      <category>googletagmanager</category>
    </item>
    <item>
      <title>Node.JS Revolution: Farewell to Axios and Fetch API in Version 17.5.0</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Wed, 06 Apr 2022 17:00:54 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/nodejs-revolution-farewell-to-axios-and-fetch-api-in-version-1750-mco</link>
      <guid>https://dev.to/keyholesoftware/nodejs-revolution-farewell-to-axios-and-fetch-api-in-version-1750-mco</guid>
      <description>&lt;p&gt;Every self-respecting student of programming has already needed to use libraries (the famous “lib”) in the development of their applications. Examples include the Axios and Fetch libs, which are widely used on the front-end and back-end to consume external APIs.&lt;/p&gt;

&lt;p&gt;Recently, Node.JS v17.5 underwent two changes that divided opinions, so let's explain how these changes impact the issues of effectiveness and agility in code production. Additionally, I will also discuss the pros and cons of this update.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="https://keyholesoftware.com/2022/03/28/node-js-revolution-farewell-to-axios-and-fetch-api-in-version-17-5-0/"&gt;Keyholesoftware.com&lt;/a&gt; by Bernardo Leite on 3/28/22.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Understanding the Different Libraries&lt;/h2&gt;

&lt;p&gt;To start, let's remember how the &lt;b&gt;Axios&lt;/b&gt;, &lt;b&gt;Fetch&lt;/b&gt;, and &lt;b&gt;Node Fetch&lt;/b&gt; libs work.&lt;/p&gt;

&lt;p&gt;With an excellent reputation in the community, Axios is a popular Javascript lib for making HTTP requests from Node.JS or &lt;code&gt;XMLHttpRequests&lt;/code&gt; through the browser. It also supports the Promise API, which is native to JS ES6, transforms data into JSON, and transforms and intercepts request and response data (request/response). In addition to all this, on the client-side, it supports XSRF protection.&lt;/p&gt;

&lt;p&gt;In the Javascript language, Axios is an alternative to the &lt;code&gt;fetch()&lt;/code&gt; method because it can do an automatic analysis of JSON and works very well in partnership with Express. While Axios sends the web request, Express responds to these requests.&lt;/p&gt;

&lt;p&gt;On the other hand, Axios is an external package that you need to install in your project, unlike Fetch, which is already built into the browser and, therefore, lighter than Axios.&lt;/p&gt;

&lt;p&gt;For those who are on the Node-Fetch team, you know that it will be easy and light to install the module. With a one simple &lt;code&gt;npm install node-fetch&lt;/code&gt; you have the Fetch resources in a much more direct way and without having to implement the &lt;code&gt;XMLHttpRequest&lt;/code&gt;. That is, it is an attraction for those who like a cleaner and more organized code.&lt;/p&gt;

&lt;p&gt;Here’s an example of Node-Fetch using &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; asynchronous functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require(“express”);
const fetch = require(“node-fetch”);
const app = express();

app.get(‘/’, async function(req, res){
const response = await fetch(‘https://dog.ceo/api/breeds/list/all’)
    const app = await response.json()
    console.log(app)
})
app.listen(3000);
module.export = app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that in the application, you may need frameworks and maybe other libraries in addition to Node-Fetch. This can make the application heavy with so many dependencies.&lt;/p&gt;

&lt;p&gt;Simply put, new to the Node.JS ecosystem is a native way to implement the Fetch API without lib or installing external modules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1Q7kXZTl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Bernardo-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1Q7kXZTl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Bernardo-1.png" alt="Axios and Fetch" width="330" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will now have the &lt;code&gt;fetch&lt;/code&gt;, &lt;code&gt;Request&lt;/code&gt;, &lt;code&gt;Response&lt;/code&gt;, and the &lt;code&gt;Headers&lt;/code&gt; as globals. Then, just consume the Fetch API! Here is a very simple example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z08GBEqy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Bernardo-2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z08GBEqy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://keyholesoftware.com/wp-content/uploads/Bernardo-2.jpg" alt="" width="512" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, all you need is to send the request and the external API will respond back in a much simpler way.&lt;/p&gt;

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

&lt;p&gt;There are many reasons for you to consider using &lt;code&gt;fetch()&lt;/code&gt; in Node.JS because, in addition to being excellent for performing simple requests, we don't need to worry about possible changes in external libs and consequently compromising our applications.&lt;/p&gt;

&lt;p&gt;It is still important to remember that native Fetch is not yet available for the LTS version, which means that there may be versioning issues or instability in the latest version. However, it is nothing that compromises the new implementation.&lt;/p&gt;

&lt;p&gt;Native Fetch also references &lt;a href="https://undici.nodejs.org/#/" rel="noopener"&gt;Undici&lt;/a&gt;, which guarantees a significant improvement in latency and file transfer rate.&lt;/p&gt;

&lt;p&gt;For more on Node.JS and other popular technologies, take a look at our &lt;a href="https://keyholesoftware.com/blog/"&gt;Development Blog&lt;/a&gt;. All posts are written by other expert Keyhole consultants.&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Story Point Estimation: Could Your Team Do Better?</title>
      <dc:creator>Keyhole Software</dc:creator>
      <pubDate>Mon, 11 Oct 2021 21:08:43 +0000</pubDate>
      <link>https://dev.to/keyholesoftware/story-point-estimation-could-your-team-do-better-gpg</link>
      <guid>https://dev.to/keyholesoftware/story-point-estimation-could-your-team-do-better-gpg</guid>
      <description>&lt;p&gt;It can be rough to ask your development team to estimate work based on abstract story point values, especially when they are new to it or to each other. I know this and have experienced this in full.&lt;/p&gt;

&lt;p&gt;So in this blog, I am going to share an exercise with you that will give every member of your team the same frame of reference for estimating the size of their work. I call this exercise Story Point Benchmarking. (If you've never heard of agile story points, check out &lt;a href="https://www.mountaingoatsoftware.com/blog/what-are-story-points"&gt;this primer&lt;/a&gt;.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post was first published on the &lt;a href="https://keyholesoftware.com/2021/08/30/story-point-estimation-could-your-team-do-better/"&gt;Keyhole Software blog&lt;/a&gt; by &lt;a href="https://keyholesoftware.com/author/rdivine/"&gt;Rusty Divine&lt;/a&gt; on August 30, 2021.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;What Are Story Point Benchmarks?&lt;/h2&gt;

&lt;p&gt;Story point benchmarking is a meeting. You and your team will review the work you've completed in the recent past and sort it into piles according to the relative effort, then choose a story or two from each pile to represent the benchmark for that size of work.&lt;/p&gt;

&lt;p&gt;Those benchmarks can then be used in estimation meetings to compare future work against. Even members new to the team can quickly become effective at estimating work using these benchmarks.&lt;/p&gt;

&lt;p&gt;Performing the exercise I describe below should help both teams who are new to story pointing and teams who have gotten into a rut where every story is a 5 or an 8. Your team can repeat the exercise every year to re-baseline or whenever you start a new project.&lt;/p&gt;

&lt;h2&gt;Story Point Benchmarking Exercise&lt;/h2&gt;

&lt;p&gt;You should plan for this exercise to take about an hour to complete and an hour to prepare for. I've listed the steps below!&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Using index cards, sticky notes, or an online tool like &lt;a href="https://app.mural.co/"&gt;Mural&lt;/a&gt;, create your point cards.
&lt;ul&gt;
    &lt;li&gt;If you are using story points, these would be 0.5, 1, 2, 3, 5, 8, 13, 20.&lt;/li&gt;
    &lt;li&gt;You could also use days, t-shirt sizes (xs, s, m, l, xl), or any other categorizations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
    &lt;li&gt;Create work cards for the last few months of work that are short (just a title, or brief description like "Add contact us form"), and eliminate any that seem like they were just a one-off work item.
&lt;ul&gt;
    &lt;li&gt;The exercise will work best with about 30-60 cards for the participants to sort through.&lt;/li&gt;
    &lt;li&gt;The work cards should not have any former estimates on them. In fact, I wouldn't put the actual time spent on the work card even if I had it available.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
    &lt;li&gt;During the meeting, everyone on the team should participate in sorting the work cards into the story point piles.
&lt;ul&gt;
    &lt;li&gt;Ensure participation by dealing the work cards out to all the members beforehand.&lt;/li&gt;
    &lt;li&gt;Encourage the team to discuss moving work cards between piles as they learn more about what kinds of work cards are going into the piles.&lt;/li&gt;
    &lt;li&gt;Hopefully, this generates some good discussions for the team.&lt;/li&gt;
    &lt;li&gt;Throw out any work cards that are causing controversy and delay.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
    &lt;li&gt;Now, divvy up the point piles amongst the team, and ask them to pick two work cards that clearly fit into that pile in a way that they think the whole team would be familiar with. These are your potential benchmark cards.
&lt;ul&gt;
    &lt;li&gt;If you started with enough work cards, you should have at least one work card per story point pile.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
    &lt;li&gt;Take turns talking about the two potential benchmark cards, and see if the team can narrow it down to one. If not, keep both.&lt;/li&gt;
    &lt;li&gt;Record the benchmark cards in a document that can be printed off.
&lt;ul&gt;
    &lt;li&gt;Sometimes the work card text can be edited a little during this to make it more broadly applicable (e.g. change "Add Terms Screen" to "Add a new screen with only words").&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
    &lt;li&gt;Print off the benchmark doc for each team member and ask them to bring it to the next estimation meeting.
&lt;ul&gt;
    &lt;li&gt;While estimating upcoming work, the team should refer back to their benchmarks and use them to debate where a work item best fits compared to the benchmarks.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Trust me, this exercise will save your team lots of time and frustration in the future. After completing the exercise, the entire team will share a context for estimating work based on work they did in the past and were already familiar with.&lt;/p&gt;

&lt;p&gt;I know from experience that without doing this activity, it can take teams months to gain that common understanding.&lt;/p&gt;

&lt;h2&gt;FAQs, Tips, &amp;amp; Tricks&lt;/h2&gt;

&lt;h4&gt;Why not just estimate in hours instead?&lt;/h4&gt;

&lt;p&gt;Estimation in hours tends to be overly optimistic. When someone asks me how many hours it will take me to do something, I reply with the best-case scenario of this work. Story points, on the other hand, are designed to be relative to other work the team has completed. It doesn’t depend on who did it, and it forces us to change our context to think about things in comparison to other similar work we’ve done.&lt;/p&gt;

&lt;p&gt;If you find your team saying something like, "It will be 2 points if the expert does it, and 5 points if I do it," then you need to let them know that points are not based on who does the work, points are based on the comparison to other work done in the past and the rest will average out over time.&lt;/p&gt;

&lt;p&gt;Let me put it this way. Imagine you have two teams, one is made up of seasoned developers and the other more junior developers. Imagine that each team has an identical backlog and the points are also identical on all the stories.&lt;/p&gt;

&lt;p&gt;If the two teams used hours to estimate instead, they would both perform the same number of hours of work in two weeks. The junior team just gets fewer work items complete. If they used story points, though, we could say the seasoned team might be able to complete 40 points in two weeks, and the junior team might only be able to complete 20 points in two weeks. Now imagine you shuffle the team members up. Both teams still complete the same number of hours, obviously, and they would also both complete 30 points.&lt;/p&gt;

&lt;p&gt;Basically, it’s hard to make truly accurate hour-based estimates because hours are based on personal experience; they're a subjective unit. Points are based on team experience instead, which makes them an objective, more universal unit.&lt;/p&gt;

&lt;h4&gt;How does complexity affect estimates?&lt;/h4&gt;

&lt;p&gt;When a team discusses complexity, encourage them to discuss comparative complexity.&lt;/p&gt;

&lt;p&gt;For example, if the team is embarking on something they've never tried before, there will be a lot of complexity involved, which often drives the story points up artificially high. If you can, find examples of when the team tackled similar obstacles, and use that comparative complexity as an example.&lt;/p&gt;

&lt;p&gt;You can tell them, "When we integrated with that third-party service last time, we didn't know what we were getting into exactly either, but it turned out to be about a 13. Do you think this new work item is really any more complex than that one was?"&lt;/p&gt;

&lt;h4&gt;What if the team has no past work items?&lt;/h4&gt;

&lt;p&gt;It's going to be difficult for a team that hasn't worked together to get a common context for estimating work. If you have a backlog of upcoming work, you can try doing this exercise with the upcoming work instead of past work. That would still give the team the experience of sorting all the work out into the piles instead of trying to consider each new work item on its own.&lt;/p&gt;

&lt;p&gt;You can also introduce the concept of story point benchmarking by making up a bunch of fake work cards. I have done this for user groups by using chores around the house. The participants take some work cards I've printed like "Sweep the floor" and "Replace garbage disposal" and sort those out into point piles. It gives them a good feel for what benchmarking entails. If you'd like to try it, you can &lt;a href="https://www.dropbox.com/s/k2h3lanpfvcvgxw/StoryPointBenchmarkingCards.zip?dl=0"&gt;download and print some example cards here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;Should my sprint have 20 points or 100 points? How much should each member be assigned?&lt;/h4&gt;

&lt;p&gt;If your team is struggling to complete stories or you're just starting out, then a good rule of thumb is that each developer on the team might do around 8 or 13 points per sprint. You can then predict how many points your team might be able to do in a sprint (your velocity) by multiplying the number of developers by that number.&lt;/p&gt;

&lt;p&gt;After you have successfully completed 3 sprints, you can start calculating your velocity based on the average of how much work the team completed across the last three sprints.&lt;/p&gt;

&lt;p&gt;Normally, I don't favor considering testing effort in the points unless it is abnormally large. Teams who estimate a work item by asking developers for an estimate than asking testers for an estimate are just asking for trouble.&lt;/p&gt;

&lt;p&gt;Some teams assign points to bugs and research items (spikes), and others don't. I think it makes sense to assign points to anything the developers are working on, but not to things that business analysts or possibly architects are working on.&lt;/p&gt;

&lt;p&gt;The team's goal should be to create a fairly consistent velocity and to complete all the work committed to in a sprint. With any extra time, they can be documenting, researching, fixing bugs, working ahead, or addressing technical debt.&lt;/p&gt;

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

&lt;p&gt;Your team will thank you for making their lives easier with story point benchmarking.&lt;/p&gt;

&lt;p&gt;Everyone will quickly get on the same page when it comes to estimating work, there will be fewer arguments, and shorter estimation meetings. It only takes an hour, and the dividends just keep paying off - especially when you roll new members onto the team.&lt;/p&gt;

&lt;p&gt;I hope you try it out, and if you do, please leave a comment here to let me know how it goes for you!&lt;/p&gt;

</description>
      <category>agile</category>
      <category>productivity</category>
      <category>scrum</category>
    </item>
  </channel>
</rss>
