<?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>Forem: Ryan James</title>
    <description>The latest articles on Forem by Ryan James (@ryanjames1729).</description>
    <link>https://forem.com/ryanjames1729</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1240492%2F451a5a57-8d67-4871-a68e-e8a0caeea6be.jpeg</url>
      <title>Forem: Ryan James</title>
      <link>https://forem.com/ryanjames1729</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ryanjames1729"/>
    <language>en</language>
    <item>
      <title>Create a Spelling App</title>
      <dc:creator>Ryan James</dc:creator>
      <pubDate>Sun, 02 Jun 2024 19:51:44 +0000</pubDate>
      <link>https://forem.com/ryanjames1729/create-a-spelling-app-2be7</link>
      <guid>https://forem.com/ryanjames1729/create-a-spelling-app-2be7</guid>
      <description>&lt;h2&gt;
  
  
  The Why: What is the Need?
&lt;/h2&gt;

&lt;p&gt;I am a parent and I also work in a school. I am always trying to figure out a way to automate processes or just make my life easier. When my child brought home a list of spelling words that they were going to be tested on, I thought there had to be an easy-to-use site for this. Then, I realized that I could just build the thing rather than find the thing. So, let’s do this together. What you’re going to do is this: build a website that has a simple form with a button, the button will use a text-to-speech library reading from a word list, and then the form will check if what is typed matches the current word in the list. The word list will be maintained in a headless CMS so that the data isn’t contained in the website itself. Let’s get started!&lt;/p&gt;

&lt;p&gt;*Disclaimer: This guide is not a step-by-step guide. It is recommended that you understand some basics about React, Javascript, and GraphQL queries before starting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Set Up: Where Do I Start?
&lt;/h2&gt;

&lt;p&gt;Before we get started, let’s take a sneak peek at what you’re going to build. The site lives at CDS Spells! where you can edit the word list and create a new quiz. I built this site using NextJS and TailwindCSS and I use Hygraph for data management and Vercel for hosting the site.&lt;/p&gt;

&lt;p&gt;Start by opening up your terminal and using npm to build your project. Run the command npx create-next-app@latest to build your project. This will guide you through all of the build steps. In addition, you will want to install 2 libraries with npm. Install both graphql-request and react-speech-kit. Once you get the project created, go ahead and open up your project folder with your favorite text editor. NextJS will give you a good boilerplate, but you will want to get rid of most of that.&lt;/p&gt;

&lt;p&gt;Since we are using the new App router in NextJS 14, you will want to do a few things. The app is going to have a landing page with a link to the quiz route.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;p&amp;gt;
  &amp;lt;Link href="/quiz"&amp;gt;Take a Quiz&amp;lt;/Link&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add a new route, you will create a folder in the app directory and name it ‘quiz’. Then in this new folder add a new page and save it as page.js. You can test your new route if you add a default export where it returns a message like “Quiz” between some h1 tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default async function Quiz() {
  return (
    &amp;lt;h1&amp;gt;Quiz Page&amp;lt;/h1&amp;gt;
   )}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Data Set Up: Using Hygraph and GraphQL
&lt;/h2&gt;

&lt;p&gt;Now that you have a basic app with a new route added, you’re ready to start pulling data. There are a few ways to pull data into a NextJS app. In this app, you are going to be using Hygraph to pull in GraphQL data. Hygraph is a headless CMS that makes creating, mutating, and reading data pretty easy. If you haven’t already, go ahead and sign up with an account. Once you get your account made, you’ll have the option to create a project.&lt;/p&gt;

&lt;p&gt;Once you have created your project, you will create a model for your project. You can add what fields you want to add, but these are the fields that I used for my project:&lt;br&gt;
userName - this will be the user that created the quiz&lt;br&gt;
quizName - this will be the name of the quiz that the user set&lt;br&gt;
words - this will be the list of words&lt;br&gt;
slug - this will be the link for the quiz as a combination of the userName and the quizName&lt;br&gt;
The only field of these shown above that will be unique is the slug. The slug needs to be unique so that duplicate links are not created for different quizzes. Using the menu, click the Schema link to add a Model to your project. &lt;/p&gt;

&lt;p&gt;Now that you have a model set up, you’re ready to set up a test quiz and see if you can pull the data in. Entries can be added by clicking the Content option. You can also add content through API endpoints, but we’ll get to that later. Create a new entry in your content. For my app, I used a dedicated character for separating my words in the wordlist like a semi-colon. Feel free to think about how you want to separate words in your wordlist. Once you get your information typed in, click the save and publish button. Publish will take it out of the draft stage and put it on the published stage. The published stage is the default for the public API endpoint for reading your data.&lt;/p&gt;

&lt;p&gt;Find the Project Settings menu now. Then go down to API Access. When you first set up a project, nothing is shared. In the Public Content API, you can turn on defaults for reading the data. If you’re planning to use data that needs to be secured with an access token, you can do that on the menu item below this. We’re not going to publish the API endpoint, so I feel public access is going to be fine for a list of spelling words. Then, you’re going to grab the URL for your API endpoint. It is going to be the top link of this page under the heading Content API. That link will be what you fetch from your app. If you haven’t already, create a .env.local file in the root directory of your project. You will create a variable for your endpoint there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env.local
GRAPHQL_API_URL = "https://url-for-my-api"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you’re ready to fetch some data in your app!&lt;/p&gt;

&lt;p&gt;In the quiz page that you created, you’re going to add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ./app/quiz/page.js
const hygraph = new GraphQLClient(process.env.GRAPHQL_API_URL)
const { wordLists } = await hygraph.request(
{
        wordLists {
            id
            slug
            userName
            words
        }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will pull your word list entries from Hygraph (all 1 of them) and assign the data to the destructured object wordLists. Then, you can map over the word lists that have been imported by their id number in your return statement for the default export.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{wordLists.map((wordList) =&amp;gt; {
            return (
                &amp;lt;div key={wordList.id}&amp;gt;
                    &amp;lt;h2&amp;gt;{wordList.userName}&amp;lt;/h2&amp;gt;
                    &amp;lt;p&amp;gt;{wordList.words}&amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;
            )
        })}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get your project running locally and navigate to the /quiz route to see if the data that you entered in Hygraph is showing up. Huzzah! You are fetching data from your headless CMS with your API endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic App Flow
&lt;/h2&gt;

&lt;p&gt;Now that you have connected your app to your Public API Endpoint, you can start building out the logic for the app. The app should work this way:&lt;/p&gt;

&lt;p&gt;Load the route for a quiz.&lt;br&gt;
Pull the data from Hygraph asynchronous.&lt;br&gt;
Set the current word as a random word from the list.&lt;br&gt;
Allow for the user of the app to hear the word with a ‘speak’ button.&lt;br&gt;
Allow for the user to to type the word in a text field and give the user feedback if it's correct or not.&lt;br&gt;
Repeat for more words in the list.&lt;/p&gt;

&lt;p&gt;Let’s start with loading the route. You can use NextJS dynamic routing with a request to your content in Hygraph to build out our routes for each quiz. In your app, create a folder and name it quizzes. Then inside of that folder, create another folder and name it [slug]. Then inside of that folder create a page.js file. This will create routes with the syntax: /quizzes/. Your new page inside the [slug] folder will be a template for all quizzes. To ensure all data gets to the right places, you’ll do 2 different GraphQL requests: one to generate the dynamic routes and one for the content that needs to be on each route. The one to generate the routes uses generateStaticParams() from NextJS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function generateStaticParams() {
    const hygraph = new GraphQLClient(process.env.GRAPHQL_API_URL)
    const { wordLists } = await hygraph.request(
    {
        wordLists {
                slug
        }
    })
         return wordLists.map((wordList) =&amp;gt; ({
        slug: wordList.slug,
          }))
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this on the page, each quiz that has a slug entry will be generated on the fly with dynamic routing. When new quizzes get added, they will automatically get routes created for them when this request gets made. The second request that will be made is similar to one that was created in the previous step. Just copy and paste that GraphQL request over and update it to filter your request by the slug: wordLists (where: {slug: "${params.slug}"}) {.&lt;/p&gt;

&lt;p&gt;Now that routes are set up and we know that the words from each word list are being routed to the right route, it's time for the game flow. You’ll take in the words as a String and splice it with punctuation marks. If the word list is separated by commas, then splice by commas. Following that, trim each word’s whitespace and shift it to lowercase. Then, you’ll want to randomize the order of the words in your array so that it’s not the same every time you come to the page. I did this by iterating over the list swapping random terms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for(let j = wordArray.length - 1; j &amp;gt; 0; j--) {
    const k = Math.floor(Math.random() * (j + 1));
    [wordArray[j], wordArray[k]] = [wordArray[k], wordArray[j]];
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you have an array of words that are in random order. The app will iterate over this list to assign the current word to an element in that array. This can be done using React’s useState. The next step is to add the user input. For this app, you should build out a simple UI that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a ‘speak’ button that will speak the chosen word&lt;/li&gt;
&lt;li&gt;a text entry on a form&lt;/li&gt;
&lt;li&gt;a submit button for the form&lt;/li&gt;
&lt;li&gt;a ‘next’ button for the user that will skip to the next word
In addition to initializing the current word that needs to be guessed, a few more items need to be taken care of:
create a score variable 
create a variable for the guessed or typed word
initialize the speak object using useSpeechSynthesis()
The library that you will use is from the library, react-speech-kit. You can find the documentation for react-speech-kit in npm’s library. To get all of this set up, add this code after your code segment to pick a word.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [word, setWord] = React.useState(wordArray[0]);
  const [guessedWord, setGuessedWord] = React.useState('');
  const [score, setScore] = React.useState(0);
  const { speak } = useSpeechSynthesis();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now with this setup, you can tie the ‘speak’ button with an onclick event to call the speak function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button onClick={() =&amp;gt; {
speak({ text: word  });
}}
&amp;gt;Speak&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the user uses the ‘speak’ button, they will type into the form with the text box that you created already. The submit button will then compare what they typed to what the word was set to. Following this, you can give the user feedback on whether they were correct or not. The easiest way to do this would be with alert messages. You can also use a banner that has absolute positioning with a z-index higher than your main content. If they get it right, add 10 points to their score and move on to another word. &lt;/p&gt;

&lt;h2&gt;
  
  
  Customization: Add Some Flair
&lt;/h2&gt;

&lt;p&gt;Now with what we’ve done so far, it's just the workflow of the program. Here are some ideas to make it more attractive and engaging:&lt;br&gt;
Make the background not a single color - in my final version I created a radial background that was a combination of the school colors for the students that were going to use this&lt;br&gt;
Use additional features from the react-speech-kit library like the pitch and speed options&lt;br&gt;
Create fun messages for students - use emojis!&lt;br&gt;
Add in CSS to make it a mobile version so that it can be used from any device&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding More: The More Robust Version
&lt;/h2&gt;

&lt;p&gt;Moving from a prototype to finished product involves a few more steps. In my version, I changed the organization of the program. I did this because I wanted users to be able to login and create their own quizzes. By using things like nextAuth or Clerk, you can link Google SSO to your app. Then, your app can have a profile page for users when they login. The profile page can have a form where they can create a new quiz and then populate the area under the form with their current quizzes. By taking advantage of NextJS’s revalidate parameter in its fetch API, you can let NextJS update the page by comparing what’s in the cache and fetching new data if the current list of quizzes has gone stale.&lt;/p&gt;

&lt;p&gt;The quizzes can have a better point system. In the point system that is listed above, when a student hits refresh or leaves the site and comes back, their points will be reset to zero. The teacher then doesn’t get any feedback. By adding a points column to the schema for your quizzes in Hygraph, you can keep track of how many cumulative points have been earned for the quiz. This makes each quiz a community effort and doesn’t reward specific students. It can bring a piece of data to teachers that represents how much work a class has put into learning a list of words. &lt;/p&gt;

&lt;p&gt;The live version of this project lives at &lt;a href="https://cds-spelling.netlify.app/"&gt;CDS Spells!&lt;/a&gt; and the repo can be found on &lt;a href="https://github.com/ryanjames1729/cds-spelling/"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article was originally published on &lt;a href="https://www.ryan-james.dev/articles/blog/cds-spells"&gt;ryan-james.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>graphql</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Building a Word Game: Let's Play Alpha</title>
      <dc:creator>Ryan James</dc:creator>
      <pubDate>Thu, 04 Apr 2024 15:04:44 +0000</pubDate>
      <link>https://forem.com/ryanjames1729/building-a-word-game-lets-play-alpha-234p</link>
      <guid>https://forem.com/ryanjames1729/building-a-word-game-lets-play-alpha-234p</guid>
      <description>&lt;p&gt;As a computer science teacher I love teaching algorithms without using code. One of my favorite ways to introduce divide and conquer algorithms is to start with the binary search algorithm. What I like to do is grab a dictionary from the library and challenge the students to how many guesses it will take to find what page number any word in the dictionary is on. A dictionary that may have hundreds of pages gets consolidated down to a small amount of page turns using that algorithm.&lt;/p&gt;

&lt;p&gt;Since I like this way of teaching, I came across the site that hryanjones was hosting on Github Pages, &lt;a href="https://hryanjones.com/guess-my-word/"&gt;Guess My Word&lt;/a&gt;. What if we recreated something popular like Wordle with this kind of algorithm? That’s where the idea for Let’s Play Alpha came from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Get Into This
&lt;/h2&gt;

&lt;p&gt;When I had the idea for this I tried to think of what would it need to pull it off. How much heavy lifting did I need frameworks to do for me? Since this is going to be a mainly client side game that didn’t need to do much fetching or server rendering, I knew I was going to use React. I thought I could have all the data for the game served on the client side since the word list I was going to use was going to be a dictionary and I didn’t want it to do a bunch of fetching once the game was first loaded. &lt;/p&gt;

&lt;p&gt;So here is my stack for this game. I built it with &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt;, because it’s easy for developers and it doesn’t have a lot of the bulk that some of the other React frameworks have. I could build my game in components with React added onto Astro. I used &lt;a href="https://tailwindcss.com"&gt;Tailwind&lt;/a&gt; for my styling and like I said, all the data is served up in my loading algorithms from javascript files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up My Project
&lt;/h2&gt;

&lt;p&gt;Once I got my project started with the usual starting point in Astro, I started to get my main component ready for the project. Astro files and components can be built with the .astro extension but since I was going to build a React component, I used typescript for that. On my landing page for the app, I imported the component and used the client directive to hydrate it correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Sample client:only=”react” /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I could build a React component in my components folder and let it do its thing. Easy peezy!&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does This Game Work?
&lt;/h2&gt;

&lt;p&gt;Ok, so how exactly did I get everything to work as I wanted it to? First, I needed a list of words. I grabbed a list of words using a Scrabble API. I then consolidated it down to a smaller list because it was huge and the list included a ton of words that were just ridiculous. After shuffling the list I set up a dictionary where the key is the date and the word is the value. That way I could fetch the word for each day without having to worry about what time of the day it was.&lt;/p&gt;

&lt;p&gt;Then, I decided to set up the number of guesses and rounds. The player of the game gets 5 guesses, similar to Wordle. After 5 guesses, they move to the next round where all their previous guesses get cleared out. When the user guesses a word, the words that they guess are stacked around the input field in a way that shows them the alphabetical order of the guesses. This is meant to help the player. They can use this to narrow down their guesses. To do this, I set up 2 arrays. When they guess a word, it will be placed in that array whether it's before or after the solution of the game that day.&lt;/p&gt;

&lt;p&gt;Lastly, I decided to give the user a little more feedback. When I tried this out with friends it seemed a little too hard to get the word in 5 guesses. That’s when I used the green highlighting that Wordle does to show if a letter is correct. So this is like a ‘Guess My Word’ and ‘Wordle’ mashup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning New Things
&lt;/h2&gt;

&lt;p&gt;Alright, so what was the real purpose behind this? I love making new things like this, especially when they only take a few days to ship something. But, I really like to do this to learn something new. So, here’s a list of things I learned or maybe got better at.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Building with Astro.&lt;/em&gt; &lt;br&gt;
Prior to this I haven’t built something with Astro in a year or so. This gave me a great opportunity to dig into their documentation and build something from the ground up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;React.&lt;/em&gt;&lt;br&gt;
I have been using other frameworks to build with React. I skipped learning React and learned the frameworks. After going through this project, I understand React so much better. I’m not a professional React developer or anything like that. But, for someone who is a self-taught developer I feel much more comfortable inside of React code. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Fun stuff like animations and modals.&lt;/em&gt; &lt;br&gt;
I added modals that pop up for the user on the loading of the screen and when they solve a puzzle. I’ve done this before, but I had never done that with React before so it was fun to put those pieces together. Also, the confetti animation. Who doesn’t love a good confetti moment?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;I spent maybe 3 to 4 days on this before I shipped it for my first iteration. Again, this was all just in my free time. Then, I think I spent another week to get it to its final state. If I was working all day on it, this would have been around 1 to 2 days of building - this seems crazy to get something like this done by then. &lt;/p&gt;

&lt;p&gt;You can see the site live at &lt;a href="https://lets-play-alpha.netlify.app"&gt;https://lets-play-alpha.netlify.app&lt;/a&gt; and the repo is available at &lt;a href="https://github.com/ryanjames1729/word-of-the-day"&gt;https://github.com/ryanjames1729/word-of-the-day&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This article was originally posted on &lt;a href="https://ryan-james.dev"&gt;https://ryan-james.dev&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Learning with Astro:DB</title>
      <dc:creator>Ryan James</dc:creator>
      <pubDate>Fri, 22 Mar 2024 01:00:19 +0000</pubDate>
      <link>https://forem.com/ryanjames1729/learning-with-astrodb-25og</link>
      <guid>https://forem.com/ryanjames1729/learning-with-astrodb-25og</guid>
      <description>&lt;p&gt;People that know me or have read my previous articles know that I typically work in one of those other React frameworks that we will not name here. I skipped the line on learning web development and reached for one of the easiest when I decided to learn Javascript after using Python for shipping sites.&lt;/p&gt;

&lt;p&gt;What do I know about using Astro? I would say that I know very little. I have only built a few micro-sites and on the one I spent the most time on, I was building a React component for all the heavy lifting of the site. So I was literally learning as I was building a site to connect with Astro’s new db feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I Started
&lt;/h2&gt;

&lt;p&gt;I had an idea of what I wanted to do from the beginning. Previously, I build a small project to simulate a &lt;a href="https://crud-with-forms.vercel.app/"&gt;full-stack app &lt;/a&gt;that could create, read, update, and delete data that I was accessing through GraphQL. I wrote an article about that and you can read more about that experience &lt;a href="https://dev.to/ryanjames1729/building-a-full-stack-app-with-crud-3kk8"&gt;here&lt;/a&gt;. That is what I’m using as my frame of reference.&lt;/p&gt;

&lt;p&gt;I created my astro app with the same setup as usual using &lt;code&gt;npm create astro@latest&lt;/code&gt;. Then, I knew I wanted to add Tailwind to this so I used the Astro commands to do that. I cleared out most of the boilerplate and added a new component to my components list named ReadForm. I then imported that and put the component at the bottom of my index.astro page.&lt;/p&gt;

&lt;p&gt;The db had to be set up as well, so I used the Astro commands to do that. When you create your db, you get a new directory set up with a couple files that you’ll use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with the DB
&lt;/h2&gt;

&lt;p&gt;I used the Astro documentation (which is superb by the way!) to help get everything set up correctly. There were some flashbacks for sure. When I first started developing websites, I was using Python with the Flask library and SQLlite. Astro:db uses SQL as well. When installing astro:db, you get the two files that you need to set up: config.ts and seed.ts. Config is used to set up the schema for your data and seed is used to create some starter data and seed your data types.&lt;/p&gt;

&lt;p&gt;I first set up my config.ts file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//./db/config.ts
import { defineDb, defineTable, column } from 'astro:db';

const User = defineTable({
  columns: {
    userId: column.number({ primaryKey: true}),
    userName: column.text(),
    userScore: column.number(),
  },
  })

export default defineDb({
  tables: { User },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I just wanted something basic for the Users with an id, name, and score. Then after that I put some data into my seed file to get the db started with some information that I could query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./db/seed.ts
import { db, User } from 'astro:db';

export default async function seed() {
    await db.insert(User).values([
    { userId: 1, userName: 'Rust', userScore: 100},
    { userId: 2, userName: 'Marty', userScore: 90},
    { userId: 3, userName: 'Audrey', userScore: 80},
    { userId: 4, userName: 'Errol', userScore: 70},
    { userId: 5, userName: 'Thomas', userScore: 60},
    { userId: 6, userName: 'Maynard', userScore: 50}
])
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I had some data locally, I was ready to see if I could query it with a form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Form For Reading Data
&lt;/h2&gt;

&lt;p&gt;I wanted my form to function this way. If the form entry was blank or could not find the data, to not display anything. If the form entry was the text “all”, I wanted the form to fetch all the data and show it. Lastly, if the form entry was a specific user name, then I wanted the form to only fetch that row from my table and show it.&lt;/p&gt;

&lt;p&gt;To do this in Astro, I had to create a form in my component and then I put the logic for fetching the data in the frontmatter of the component. This is similar to using markdown files to drive a site. The important piece of fetching from the db is simple. I fetched all the data at once with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users = await db.select().from(User);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then I used conditional statements from the form to filter my data. This is not the best way, I know. I still need to learn the correct syntax for filtering the data fetch. It’s on my TODO list.&lt;/p&gt;

&lt;p&gt;Then from there, you can put a conditional statement in your component that if there are any users, to display those in your HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local First
&lt;/h2&gt;

&lt;p&gt;The cool thing about the db is that its local first. So, let’s say you have a site that you’re building that’s just blog posts. You can insert your blog post onto your local db and then when you ship it, it will pull from that db in the build commands. There’s no reason to have your blog site continually do data fetches if you can build it straight into the HTML.&lt;/p&gt;

&lt;p&gt;Now, I wanted to play around with how it would work hosted remotely. So, I set up an account with Astro Studio. When you create your project with Astro Studio, you can link it to a Github repo which I did. In addition, you want to create a token for when you ship your site. But, before I got into all that, I wanted to test it in development. Using Astro commands, I linked my project on my computer to the project I had created on Astro Studio using &lt;code&gt;astro link&lt;/code&gt;. Then, I was able to push all of my data in my seed file to what was being hosted on Astro Studio. &lt;/p&gt;

&lt;p&gt;To run the site using the remote data, you just add the flag –remote to your dev and build commands in your config file. There was a small bug with getting it to find the remote db, but I just needed to update in npm and it was working fine.&lt;/p&gt;

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

&lt;p&gt;Finally at the end, I shipped it with Netlify. Astro makes all of these steps super easy. Using the Netlify adapter in my config on my Astro project and then putting my token on Netlify connected all of my data well and it works. The next step for me is to get better with the data query and start adding in other pieces like the insert and delete for the db. &lt;/p&gt;

&lt;p&gt;You can see the site live at &lt;a href="https://aesthetic-truffle-e7d729.netlify.app/"&gt;https://aesthetic-truffle-e7d729.netlify.app/&lt;/a&gt; and the repo is available at &lt;a href="https://github.com/ryanjames1729/testing-astro-db"&gt;https://github.com/ryanjames1729/testing-astro-db&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This article was originally posted on &lt;a href="https://ryan-james.dev"&gt;https://ryan-james.dev&lt;/a&gt;,&lt;/p&gt;

</description>
      <category>astro</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Building a Full Stack App with CRUD</title>
      <dc:creator>Ryan James</dc:creator>
      <pubDate>Fri, 01 Mar 2024 01:11:26 +0000</pubDate>
      <link>https://forem.com/ryanjames1729/building-a-full-stack-app-with-crud-3kk8</link>
      <guid>https://forem.com/ryanjames1729/building-a-full-stack-app-with-crud-3kk8</guid>
      <description>&lt;h2&gt;
  
  
  Building a CRUD App
&lt;/h2&gt;

&lt;p&gt;I’ve been using NextJS for a good bit now building my web applications. For my personal site and other applications I felt really comfortable using the Pages router. With some of the newer features that started in version 13 and now in 14, I figured I need to start getting used to the App router. As a computer science teacher, I think that building an application that uses CRUD (create-read-update-delete), is a great way to understand a lot of programming concepts from arrays, objects, sorting data, filtering data, and mutating data. So, I took it on myself to do this for myself. In this guide, I’ll share what I learned about building a full-stack application using NextJS 14 with a GraphQL CMS.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Did I Use to Start
&lt;/h2&gt;

&lt;p&gt;To get started on my project, I created a NextJS app with the latest build where I used Typescript and TailwindCSS. In addition I installed the &lt;code&gt;graphql-request&lt;/code&gt; library using npm. I got rid of the boilerplate for the page on the ‘/’ route. I created a readme section at the top, some toggle buttons for the type of request, a form, and a footer component. I have the form set up so that it changes based on which toggle button is being selected.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Setting Up the Form
&lt;/h2&gt;

&lt;p&gt;Like I indicated, the toggle buttons change how the form shows up. When each of the inputs gets checked the other ones get unchecked and that triggers a new form to be shown. Using React’s useState makes this really simple. Then using the state of the variable that is set by the checkbox makes the form change. You can see the implementation for the form below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ read &amp;amp;&amp;amp; (
            &amp;lt;form onSubmit={async (event) =&amp;gt; {
            event.preventDefault()
            const names = getNames(readName)
            setUsernames(await names);
            }}&amp;gt;
            &amp;lt;label htmlFor="read"&amp;gt;
            &amp;lt;span&amp;gt;Read&amp;lt;/span&amp;gt;
            &amp;lt;input type="text" name="readName" id="readName" placeholder="Read Name goes here" value={readName} onChange={e =&amp;gt; setReadName(e.target.value)}/&amp;gt;
            &amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;button type="submit"&amp;gt;Read&amp;lt;/button&amp;gt;
            &amp;lt;/label&amp;gt;
        &amp;lt;/form&amp;gt;
        )}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get this working the way I wanted it to, I have React changing the value of my variable &lt;code&gt;readName&lt;/code&gt; as the client types in the form. Then, when the form is submitted, instead of it performing a typical POST request and sending the form data, I call an asynchronous function that I have on another file, actions.tsx. This part was something that was new for me.&lt;/p&gt;

&lt;p&gt;In the past using the pages router with NextJS, I was used to using static props. I could pass props in from the same javascript file or from another one in my app directory without any problems. The new thing for me was that if you use any React hooks, your file or function needs to only use the client by adding the &lt;code&gt;'use client'&lt;/code&gt; declaration. That is how I had my form set up. So, then all of my asynchronous requests for my GraphQL CMS had to use the server by adding the &lt;code&gt;'use server'&lt;/code&gt; declaration. You can’t use both at the same time. But, by separating them like components, it tells your app what requests/work that you want to be done where: server or client end.&lt;/p&gt;

&lt;p&gt;To get my data fetching and mutating working correctly, remember I had my functions on my actions.tsx file. Below you can see how I set up my function for fetching names from my CMS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function getNames(name: string) {
  const endpoint = process.env.GRAPHQL_PUBLIC_ENDPOINT ? process.env.GRAPHQL_PUBLIC_ENDPOINT : '';
  const graphQLClient = new GraphQLClient(endpoint);

  const searchname = name ? name : "";
  console.log("name", searchname)

  try{
    const { usernames }: any = await graphQLClient.request(`
    query Usernames($searchname: String!) {
    usernames(where: {name_contains: $searchname}) {
        id
        name
        points
    }
    }
    `, { searchname }); // variables must be part of the request arguments!
    console.log(usernames);
    return usernames;
  }
  catch (e) {
    console.error(e);
    return [];
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this function, it fetches the names that it's looking for in the CMS and returns the array of usernames back to the main page. The main page when it receives those names will show them under the form using React hooks.&lt;/p&gt;

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

&lt;p&gt;This project was really meaningless. I’m never going to use it for any other projects but as a reference for myself. But, it was a lot of fun trying to learn some of the newer features in NextJS 14, really understanding React hooks, and knowing how to properly set up forms. Now, this still may not be the best way to set up forms. But for me as a self-taught developer, I’m very happy with how this turned out.&lt;/p&gt;

&lt;p&gt;You can see the site live at &lt;a href="https://crud-with-forms.vercel.app/"&gt;https://crud-with-forms.vercel.app/&lt;/a&gt; and the repo is available at &lt;a href="https://github.com/ryanjames1729/crud-with-forms"&gt;https://github.com/ryanjames1729/crud-with-forms&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This article was originally posted on &lt;a href="https://ryan-james.dev"&gt;https://ryan-james.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>graphql</category>
      <category>typescript</category>
    </item>
    <item>
      <title>My First PWA in NextJS</title>
      <dc:creator>Ryan James</dc:creator>
      <pubDate>Mon, 15 Jan 2024 12:56:21 +0000</pubDate>
      <link>https://forem.com/ryanjames1729/my-first-pwa-in-nextjs-2l90</link>
      <guid>https://forem.com/ryanjames1729/my-first-pwa-in-nextjs-2l90</guid>
      <description>&lt;p&gt;Have you ever fallen in love with a character from a book or movie so much that you want to talk them all the time? That's my feeling towards Rust Cohle from True Detective S1. While rewatching season one, I started to look for a quote generator for the character, Rust. I couldn't find anything that I was looking for, so I decided to build one!&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Quote Generator
&lt;/h2&gt;

&lt;p&gt;In this, we're going to build a quote generator that prompts subscribed users with a message to check for the quote of the day. There's going to be a few steps, but this is how we can do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build out an API with pre-loaded quotes&lt;/li&gt;
&lt;li&gt;Build a NextJS site to do API fetch calls and manage the cached data&lt;/li&gt;
&lt;li&gt;Add on a PWA library to handle the subscriptions for users&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building a REST API
&lt;/h2&gt;

&lt;p&gt;The first to do in this project is to build out an REST API using Node with an Express server. This will be a quick overview of the steps. If you want to go deeper into building a REST API, I recommend reading &lt;a href="https://blog.postman.com/how-to-create-a-rest-api-with-node-js-and-express/"&gt;this article from Postman&lt;/a&gt;.&lt;br&gt;
You'll start by creating a directory for your project and then use &lt;code&gt;npm init&lt;/code&gt; to create a Node application. You'll install express with &lt;code&gt;npm install express&lt;/code&gt; . Go ahead and open up your project in your code editor. You'll add a new file and name it app.js. This will be your working app file where you'll want to set up the server and also include your routes. You can see my app.js file on &lt;a href="https://github.com/ryanjames1729/rust-cohle-quotes/blob/main/app.js"&gt;Github&lt;/a&gt;.&lt;br&gt;
To get everything running for testing, you'll want to add the following code:&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 app = express();
app.use(express.json());
const port = process.env.PORT || 3000;
app.listen(port, () =&amp;gt; {
    console.log(`Server Listening on PORT: ${port}`);
});
app.get("/", (request, response) =&amp;gt; {
    response.status(200).json({
        message: "Waiting on Requests"
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have the option of using multiple GET routes or one depending on your project. I have two, one for testing which is my "/" route and then another one for returning quotes "/new-quote". My "new-quote" route will go through my array of quotes and then find one randomly. So then the API will return the result of that call.&lt;br&gt;
Once you get everything running how you want it, it's time to get it online. If you already have a &lt;a href="https://www.heroku.com/"&gt;Heroku&lt;/a&gt; account or some other cloud based hosting provider, then go that route. I went with a free account on &lt;a href="https://render.com/"&gt;Render&lt;/a&gt;. The docs on Render were clear and it made it easy to use my repo that I had on Github and have it online.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a NextJS App to Fetch Data
&lt;/h2&gt;

&lt;p&gt;Before we get into this, let's clear the air on NextJS. I understand that using a React framework for this simple project is bloated. I know that a simpler way would be to use vanilla javascript and manipulate the DOM for this. The reason I pulled for NextJS is that I'm familiar with it, I wanted to use the cache management from NextJS, and I knew that adding on the PWA capabilities would be easy with NextJS.&lt;br&gt;
I created a project by using the NextJS starter with TailwindCSS. I wasn't really concerned with the look and feel of this project. I wanted to create this more of a 'proof of concept' than anything. TailwindCSS made it easy to get things styled quickly without having to write any CSS classes from the start. Once I got the project going, I wanted to do a couple things at the beginning. I wanted to create a 404 page in the app folder. I created a not-found.js file for my 404 route. Then on the page.js file in my app folder, I deleted most of the content. &lt;br&gt;
For the page.js file, I used the fetch API to send a request to the API that I was hosting. Then, I could just insert this into the JSX for my page. I want the page to just say the quote. I didn't wan't anything else really on the page. This makes the return for the default function Home() very short.&lt;br&gt;
The next piece that I wanted to implement since I was using NextJS, was to take advantage of the cache management. Since I wanted this to be a 'Quote of the Day' app, I only wanted a new quote to appear each day. To do this, I used the revalidate feature in NextJS. If you haven't used this before, when you use the fetch API, you can add a revalidate parameter to it. This will check the cache and use the time that you specifiy to determine if the cache is stale or not. So based on that time, I can have what is fetched replace the current cache. And voila, a quote of the day using a time of 86400 seconds. I backed mine down to 72000 in case someone checks it every 22 hours and not right on 24 or later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time for a PWA
&lt;/h2&gt;

&lt;p&gt;This was my first time trying to take a current site and implement it as a PWA. The reason why I was wanting a PWA was because I want users (mainly myself) to be able to subscribe to the site and get daily notifications to check the quote of the day. I went with OneSignal to manage the notifications. I know I could have done it easier by adding my own service workers, but I since this was my first time, I wanted something easier for me.&lt;br&gt;
After you create your account in &lt;a href="https://onesignal.com/"&gt;OneSignal&lt;/a&gt;, you then can create a new app. After you get your website linked with your OneSignal account, you can download the OneSignal SDK. By putting the OneSignal SDK into your project directory, that takes care of the subscriptions for you. OneSignal has really good documentation on all of this. I followed this instructions on their site and it took maybe 10 minutes to set up. OneSignal has a great dashboard that allows you to manage users and send out notificaiton messages. SInce I wanted this to be a daily app where users would get a notification each day, I created an Automated message. By creating an Automated message for users, this scheduling of sending the message would be done. I wouldn't have to try to figure out how to trigger an event on OneSignal with an external tool. OneSignal takes care of all of that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This was a fun project. I can see this being a bigger project or a project that can easily be replicated. Often students in school are studying a famous figure in history where there are tons of quotes that can be set up in an API. I think it would be a cool idea for while a teacher is teaching about this person, they could have a daily or hourly quote app that they pull up or allow students to subscribe to. Also, I've seen some neat projects where people are not just using a quote api, but using AI tools to generate new quotes each day based on their data set of available quotes. All fun projects!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was originally pubblished on &lt;a href="https://www.ryan-james.dev/articles/blog/my-first-pwa"&gt;ryan-james.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>pwa</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
