<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Jesstin Swadley]]></title><description><![CDATA[Technology]]></description><link>https://www.jesstin.com/</link><image><url>https://www.jesstin.com/favicon.png</url><title>Jesstin Swadley</title><link>https://www.jesstin.com/</link></image><generator>Ghost 5.76</generator><lastBuildDate>Tue, 05 May 2026 14:21:09 GMT</lastBuildDate><atom:link href="https://www.jesstin.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Polling Webs App - Deleting Results & Making a Nextjs Frontend]]></title><description><![CDATA[<p>I haven&apos;t had the best start for making progress on personal projects in 2025, however after taking most of January off from this, I am hoping to take small steps to continue my progress. Continuing upon my incremental progress on the backend I added the functionality to delete</p>]]></description><link>https://www.jesstin.com/deleting-results-making-nextjs-frontend/</link><guid isPermaLink="false">67a2b6267363e704d12771fc</guid><category><![CDATA[technology]]></category><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Fri, 07 Feb 2025 18:00:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1504917595217-d4dc5ebe6122?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDd8fGNvbnN0cnVjdGlvbnxlbnwwfHx8fDE3Mzg3MTY3MDN8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1504917595217-d4dc5ebe6122?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDd8fGNvbnN0cnVjdGlvbnxlbnwwfHx8fDE3Mzg3MTY3MDN8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Polling Webs App - Deleting Results &amp; Making a Nextjs Frontend"><p>I haven&apos;t had the best start for making progress on personal projects in 2025, however after taking most of January off from this, I am hoping to take small steps to continue my progress. Continuing upon my incremental progress on the backend I added the functionality to delete a result. Along with the delete result this is my introduction of my Nextjs frontend. </p><h2 id="deleting-results">Deleting Results</h2><h3 id="delete-result-service">Delete Result Service</h3><p>In the &quot;services/results&quot; directory add a new service file named &quot;deleteResults.ts&quot;. This file imports &quot;eq&quot; from &quot;drizzle-orm&quot;, &quot;db&quot; from &quot;drizzle.ts&quot;, and &quot;results&quot; from &quot;schema.ts&quot;. Make an export function was created this time called &quot;deleteResultById&quot;. The function queries the database to locate a result with a matching provided ID then deletes it. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { eq } from &quot;drizzle-orm&quot;;
import { db } from &quot;../../db/drizzle&quot;;
import { results } from &quot;../../db/schema&quot;;

export const deleteResultById = async (resultID: any) =&gt; {
	const deleteResult = await db
		.delete(results)
		.where(eq(results.id, resultID))

	return deleteResult
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">deleteResults.ts</span></p></figcaption></figure><h3 id="delete-result-controller">Delete Result Controller</h3><p>In &quot;results.controller.ts&quot; a new controller has to be made for the &quot;deleteResultById&quot; function. Import the &quot;deletePollsById&quot; into &quot;results.controller.ts&quot;, then create an export function called &quot;deletePollByIdController&quot;. This controller uses a try/catch block. Within the&#xA0;try&#xA0;block, the&#xA0;&quot;resultID&quot;&#xA0;is retrieved from the request&apos;s query parameters and validated. If valid, the&#xA0;&quot;deleteResultById&quot;&#xA0;service is called to delete the poll, and a success message is returned, while the catch block handles the any errors. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { deleteResultById } from &quot;../services/results/deleteResults&quot;;

export const deleteResultByIdController = async (req: Request, res: Response) =&gt; {
	try {
		const { resultID } = req.query;

		if (!resultID) {
			return res.sendStatus(400);
		}

		const poll = await deleteResultById(resultID);

		return res.status(200).send(&quot;Result was deleted&quot;);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);
	}
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Modified result controller </span></p></figcaption></figure><h3 id="delete-result-router">Delete Result Router</h3><p>A new route was made for the server to handle deleting results, the route&apos;s prefix is &quot;/delete-by-id&quot; and the HTTP method is a DELETE. Import the &quot;deleteResultByIdController&quot; into &quot;results.router.ts&quot; and have the newly created route use the imported function.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { ..., deleteResultsByIdController } from &quot;../controllers/result.controller&quot;;


pollRouter.delete(&quot;/delete-by-id&quot;, deleteResultByIdController);</code></pre><figcaption><p><span style="white-space: pre-wrap;">delete result route</span></p></figcaption></figure><h2 id="nextjs-frontend">Nextjs Frontend</h2><p>Having worked on the backend over the last few weeks to months I think time I start at least working on the frontend. While there is still more to be done, there is enough of a barebones backend to work with that I can make a frontend that interacts with it. </p><p>Going to <a href="react.dev" rel="noreferrer">react.dev</a> in the documentation &quot;Start a New React Project&quot; they give a list of frameworks that you can use to quickly get started working with React. I simply went with the first option on the list &quot;Next.js&quot; Navigating to the top level of my project directory in a terminal I entered the following command:</p><figure class="kg-card kg-code-card"><pre><code class="language-NPM">npx create-next-app@latest</code></pre><figcaption><p><span style="white-space: pre-wrap;">Make a Nextjs App</span></p></figcaption></figure><p>After that it gave me a list of options to create my React/Next.js application. I simply entered the options that I wanted to work with and was able to get started working from there. </p><figure class="kg-card kg-code-card"><pre><code class="language-Terminal">What is your project named? example-name
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like your code inside a `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to use Turbopack for `next dev`?  No / Yes
Would you like to customize the import alias (`@/*` by default)? No / Yes
What import alias would you like configured? @/*</code></pre><figcaption><p><span style="white-space: pre-wrap;">Nextjs Creation Options</span></p></figcaption></figure><h3 id="nextjs-poll-route">Nextjs Poll Route</h3><p>From the Nextjs documentation: </p><blockquote>&quot;Next.js uses&#xA0;<strong>file-system based routing</strong>, meaning you can use folders and files to define routes.&quot;</blockquote><p>Simply put the name of the folder becomes the name of the route and a file called &quot;page.js&quot; or &quot;page.tsx&quot; is the actual &quot;page&quot; of the route. I made a new directory called &quot;polls&quot; and file called &quot;page.tsx&quot; </p><figure class="kg-card kg-code-card"><pre><code class="language-Directory">polls
|- page.tsx</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">New Nextjs Polls route</span></p></figcaption></figure><p>I made a simple export function in the poll&apos;s &quot;page.tsx&quot; to return an h1 with the value &quot;Hello from the Polls Routes&quot;</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">export default function Page() {
	return (
		&lt;h1&gt;Hello from the Polls Routes&lt;/h1&gt;
	)
}</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Poll Route return value</span></p></figcaption></figure><h2 id="conclusion">Conclusion</h2><p>My experimentation continues as I have not used Nextjs before so adding it to my ever growing list of things to learn throughout this project seems almost fitting. For the time being making a simple route is a good starting point in the proceeding updates I will expand not only the apps capabilities, but also my understanding of Nextjs. </p><hr><p>If you would like to check out the code or us it for yourself:&#xA0;<a href="https://github.com/JesstinSwadley/polling-app?ref=jesstin.com" rel="noreferrer">Github Link</a></p><p>If this blog was helpful to you please consider subscribing.</p>]]></content:encoded></item><item><title><![CDATA[Polling Webs App - Deleting Options & Updating Results]]></title><description><![CDATA[<p>Like most people the holiday season is a rather busy time, this meant that for several weeks I made no progress when it came to this app. Starting back up after the holiday season has always been rather slow for me, but I am hoping to pick up where I</p>]]></description><link>https://www.jesstin.com/polling-webs-app-deleting-options-updating-results/</link><guid isPermaLink="false">677f6b8b7363e704d12771dc</guid><category><![CDATA[technology]]></category><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Mon, 13 Jan 2025 18:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1516321318423-f06f85e504b3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDh8fG9mZmljZXxlbnwwfHx8fDE3MzY0MDUxOTF8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1516321318423-f06f85e504b3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDh8fG9mZmljZXxlbnwwfHx8fDE3MzY0MDUxOTF8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Polling Webs App - Deleting Options &amp; Updating Results"><p>Like most people the holiday season is a rather busy time, this meant that for several weeks I made no progress when it came to this app. Starting back up after the holiday season has always been rather slow for me, but I am hoping to pick up where I left off. Similar to my last update I will be doing a fairly small update to the app adding the features to delete an option and update a result. </p><h2 id="deleting-options">Deleting Options</h2><h3 id="deleting-option-service">Deleting Option Service</h3><p>Starting off with with deleting an option add a new service file named &quot;deleteOptions.ts&quot; in the &quot;services/options&quot; directory. As with all of the other options services import the following &quot;eq&quot; from &quot;drizzle-orm&quot;, &quot;db&quot; from &quot;drizzle.ts&quot;, and &quot;options&quot; from &quot;schema.ts&quot;. The &quot;deleteOptions.ts&quot; service has  an export function was created this time called &quot;deleteOptionById&quot;. The function queries the database to locate a poll with a matching provided ID then deletes it, since the schema has cascading configured it will also delete options and responses that match the poll id. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { eq } from &quot;drizzle-orm&quot;;
import { db } from &quot;../../db/drizzle&quot;;
import { options } from &quot;../../db/schema&quot;;

export const deleteOptionById = async (optionID: any) =&gt; {
	const deleteOption = await db
		.delete(options)
		.where(eq(options.id, optionID))

	return deletePoll
}</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">deleteOptions.ts</span></p></figcaption></figure><h3 id="deleting-option-controller">Deleting Option Controller</h3><p>Moving on from the deleting options service into the delete options controller. In the &quot;options.controller.ts&quot; file import the &quot;deleteOptionById&quot;, then create an export function called &quot;deleteOptionByIdController&quot;. The export function has a try/catc block that uses the &quot;optionID&quot; that was retrieved from the request&apos;s query parameters and validates it. If the id is valid it moves on from there and calls the delete option by id function and takes the option id as its argument, if successful a success message is returned, while the catch block handles the any errors. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { deleteOptionById } from &quot;../services/polls/deleteOptions&quot;;

export const deleteOptionByIdController = async (req: Request, res: Response) =&gt; {
	try {
		const { optionID } = req.query;

		if (!optionID) {
			return res.sendStatus(400);
		}

		const poll = await deleteOptionById(OptionID);

		return res.status(200).send(&quot;Option was deleted&quot;);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);
	}
}</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Modified option controller </span></p></figcaption></figure><h3 id="deleting-option-router">Deleting Option Router</h3><p>Lastly for delete option a new route was made, the route&apos;s prefix for the route is &quot;/delete-by-id&quot; and the HTTP method is a DELETE. Import from the options controller &quot;deleteOptionByIdController&quot; and have the newly created route use the imported function.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { ..., deleteOptionByIdController } from &quot;../controllers/option.controller&quot;;


pollRouter.delete(&quot;/delete-by-id&quot;, deleteOptionByIdController);</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">delete option route</span></p></figcaption></figure><h2 id="updating-results">Updating Results</h2><h3 id="get-result-by-id-service">Get Result By ID Service</h3><p>Two additional features need to be made in order to make the update results work as I expect it to, the first being getting the current result total and updating the results with the incoming increase. In &quot;getResults.ts&quot; add a new export function called &quot;getResultById&quot;. It takes a parameter called resultID checks if there is a result with a matching id and returns the existing total.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">export const getResultById = async (resultID: any) =&gt; {
        const getResultTotal = await db
                        .select({
                           total: results.total
                        })
                        .from(results)
                        .where(eq(results.id, resultID))
                      
        return getResultTotal
}
</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Get Results By ID</span></p></figcaption></figure><h3 id="update-results-service">Update Results Service</h3><p>The second part needed to update the results is actually updating the result in the database. In the &quot;services/options&quot; directory add a file called &quot;updateOptions.ts&quot;. Import &quot;eq&quot; from &quot;drizzle-orm&quot;, &quot;db&quot; from &quot;drizzle.ts&quot;, and its schema &quot;results&quot; from &quot;schema.ts&quot;. Create an export function called &quot;updateResultById&quot;, it takes 3 arguments, the result id, the amount to increase by, and the current total. Assign a new variable named &quot;newTotal&quot; that is equal to the current total and the increase amount. Query the database using the result id argument and set the current total to the new total.  </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { eq } from &quot;drizzle-orm&quot;;
import { db } from &quot;../../db/drizzle&quot;;
import { results } from &quot;../../db/schema&quot;;

export const updateResultById = async (resultID: any, increase: number, total: any) =&gt; {
	const newTotal = total + increase
    
	const updateResult = await db
				.update(results)
				.set({
        				total: newTotal
				})
        			.where(eq(results.id, resultID))

	return updateResult
}</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">updateResults.ts</span></p></figcaption></figure><h3 id="update-results-controller">Update Results Controller</h3><p>Now it&apos;s time to have the two services work together in order to update the result. Add a new file in the &quot;controllers&quot; directory called &quot;results.controller.ts&quot;. This file imports &quot;Request&quot; and &quot;Response&quot; from &quot;express&quot;, &quot;updateResultById&quot; from &quot;updateResults.ts&quot;, and &quot;getResultById&quot; from &quot;getResults.ts&quot;. Like other controller functions it uses a try/catch block, within the try portion begin by destructing &quot;resultID&quot; and &quot;increase&quot; from the request body. Assign a variable that collects the result from &quot;getResultById&quot;. Call the update result function with acquired total, result id, &amp; increase. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { Request, Response } from &quot;express&quot;;
import { updateResultById } from &quot;../services/results/updateResults&quot;;
import { getResultById } from &quot;../services/results/getResults&quot;;

export const updateResultByIdController = async (req: Request, res: Response) =&gt; {
	try {
		const { resultID, increase } = req.body;

		if (!resultID) {
			return res.sendStatus(400);
		}

		const total = await getResultById(resultID);

		const result = await updateResultById(resultID, increase, total[0].total);

		return res.status(200).send(&quot;Result was updated&quot;);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);
	}
}</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">results.controller.ts</span></p></figcaption></figure><h3 id="update-results-router">Update Results Router</h3><p>Next, for update results is to configure a router for the options. Import &quot;Router&quot; from &quot;express&quot; to setup the router and &quot;updateResultByIdController&quot; from &quot;results.controller.ts&quot;. Create a const called &quot;resultRouter&quot; that calls the Router function. Define a new route with a PATCH method that has a prefix of &quot;/update-by-id&quot; and uses the &quot;updateResultByIdController&quot;. In &quot;app.ts&quot; import the router and use it in a &quot;/results&quot; route.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { Router } from &quot;express&quot;;
import { updateResultByIdController } from &quot;../controllers/results.controller&quot;;

export const resultRouter = Router();

resultRouter.patch(&quot;/update-by-id&quot;, updateResultByIdController);</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">results.routes.ts</span></p></figcaption></figure><h2 id="conclusion">Conclusion</h2><p>This was a fairly simple update that built upon my previous work. This was the first update that allowed my app to be able to have options my independent from polls. As this project continues more combined and independent systems will need to be developed, this was, but a brick in the foundation that will continue to grow. </p><hr><p>If you would like to check out the code or us it for yourself:&#xA0;<a href="https://github.com/JesstinSwadley/polling-app?ref=jesstin.com" rel="noreferrer">Github Link</a></p><p>If this blog was helpful to you please consider subscribing.</p>]]></content:encoded></item><item><title><![CDATA[Polling Webs App - Deleting Polls & Updating Options]]></title><description><![CDATA[<p>Progress for this project at the moment is rather incremental, adding only minor functionality that while making it more capable doesn&apos;t add any additional features. The biggest addition this update is the ability to update an option by its id. Expanding upon the work I have done previously</p>]]></description><link>https://www.jesstin.com/polling-webs-app-deleting-polls-updating-options/</link><guid isPermaLink="false">675932c17363e704d12771c4</guid><category><![CDATA[technology]]></category><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Wed, 11 Dec 2024 18:00:04 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1600881217197-068e1d79ffb8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fGRlbGV0aW5nJTIwfGVufDB8fHx8MTczMzg5ODkyOXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1600881217197-068e1d79ffb8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fGRlbGV0aW5nJTIwfGVufDB8fHx8MTczMzg5ODkyOXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Polling Webs App - Deleting Polls &amp; Updating Options"><p>Progress for this project at the moment is rather incremental, adding only minor functionality that while making it more capable doesn&apos;t add any additional features. The biggest addition this update is the ability to update an option by its id. Expanding upon the work I have done previously for polls I have added the ability to delete a poll by its id as well.</p><h2 id="deleting-polls">Deleting Polls </h2><h3 id="deleting-poll-service">Deleting Poll Service</h3><p>In the &quot;services/polls&quot; directory add a new service file named &quot;deletePolls.ts&quot;. Similar to update polls the following were imported &quot;eq&quot; from &quot;drizzle-orm&quot;, &quot;db&quot; from &quot;drizzle.ts&quot;, and &quot;polls&quot; from &quot;schema.ts&quot;. With all services so far an export function was created this time called &quot;deletePollById&quot;. The function queries the database to locate a poll with a matching provided ID then deletes it, since the schema has cascading configured it will also delete options and responses that match the poll id. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { eq } from &quot;drizzle-orm&quot;;
import { db } from &quot;../../db/drizzle&quot;;
import { polls } from &quot;../../db/schema&quot;;

export const deletePollById = async (pollID: any) =&gt; {
	const deletePoll = await db
		.delete(polls)
		.where(eq(polls.id, pollID))

	return deletePoll
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">deletePoll.ts</span></p></figcaption></figure><h3 id="deleting-poll-controller">Deleting Poll Controller</h3><p>In &quot;polls.controller.ts&quot; a controller has to be made for &quot;deletePollsById&quot; function. Start by importing the &quot;deletePollsById&quot; into &quot;polls.controller.ts&quot;, then create an export function called &quot;deletePollByIdController&quot;. This controller uses a try/catch block. Within the&#xA0;try&#xA0;block, the&#xA0;&quot;pollID&quot;&#xA0;is retrieved from the request&apos;s query parameters and validated. If valid, the&#xA0;&quot;deletePollById&quot;&#xA0;service is called to delete the poll, and a success message is returned, while the catch block handles the any errors. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { deletePollById } from &quot;../services/polls/deletePolls&quot;;

export const deletePollByIdController = async (req: Request, res: Response) =&gt; {
	try {
		const { pollID } = req.query;

		if (!pollID) {
			return res.sendStatus(400);
		}

		const poll = await deletePollById(pollID);

		return res.status(200).send(&quot;Poll was deleted&quot;);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);
	}
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Modified poll controller </span></p></figcaption></figure><h3 id="deleting-poll-router">Deleting Poll Router</h3><p>A new route was made for the server to handle deleting polls, the route&apos;s prefix is &quot;/delete-by-id&quot; and the HTTP method is a DELETE. Import the &quot;deletePollByIdController&quot; into &quot;polls.router.ts&quot; and have the newly created route use the imported function.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { ..., deletePollByIdController } from &quot;../controllers/polls.controller&quot;;


pollRouter.delete(&quot;/delete-by-id&quot;, deletePollByIdController);</code></pre><figcaption><p><span style="white-space: pre-wrap;">delete poll route</span></p></figcaption></figure><h2 id="updating-options">Updating Options</h2><h3 id="updating-options-service">Updating Options Service</h3><p>Up to this point options have been directly connected to polls, this will the first option service that can be used independently of polls. Create a new file called &quot;updateOptions.ts&quot; in the &quot;services/options&quot; directory. Standard for our services import &quot;eq&quot; from &quot;drizzle-orm&quot;, &quot;db&quot; from &quot;drizzle.ts&quot;, and its schema &quot;options&quot; from &quot;schema.ts&quot;. Add an export function called &quot;updateOptionById&quot;. The function takes arguments &quot;optionID&quot; with a type of any and &quot;updatedOption&quot; with a type of string. Query the database using the &quot;pollingID&quot; and update the option field using the &quot;updatedOption&quot;. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { eq } from &quot;drizzle-orm&quot;;
import { db } from &quot;../../db/drizzle&quot;;
import { options } from &quot;../../db/schema&quot;;

export const updateOptionById = async (optionID: any, updatedOption: string) =&gt; {
	const updatePoll = await db
				.update(options)
				.set({
        				option: updatedOption
				})
        			.where(eq(options.id, optionID))

	return updatePoll
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">updateOptions.ts</span></p></figcaption></figure><h3 id="updating-options-controller">Updating Options Controller</h3><p>Building upon the updating Options service, a controller will need to be made. In the &quot;controllers&quot; directory make a new file called &quot;options.controller.ts&quot;. In the options controller import &quot;updateOptions.ts&quot; from the services directory. Add an export function &quot;updateOptionByIdController&quot; that uses a try-catch block. Within the try block, destructure the &quot;optionID&quot; and &quot;updatedOption&quot; from the request body, validate them both, and call the &quot;updateOptoinById&quot; function to perform the database update. Once the database update is completed is send a success response, while the catch block handles the any errors. </p><pre><code class="language-Typescript">import { Request, Response } from &quot;express&quot;;
import { updateOptionById } from &quot;../services/options/updateOptions&quot;;

export const updateOptionByIdController = async (req: Request, res: Response) =&gt; {
	try {
		const { optionID, updatedOption } = req.body;

		if (!optionID) {
			return res.sendStatus(400);
		}

		const poll = await updateOptionById(optionID, updatedOption)

		return res.status(200).send(&quot;Poll was updated&quot;);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);
	}
}</code></pre><h3 id="updating-options-router">Updating Options Router</h3><p>Next, for the update options is to configure a router for the options. Import &quot;Router&quot; from &quot;express&quot; to setup the router and &quot;updateOptionByIdController&quot; from &quot;options.controller.ts&quot;. Create a const called &quot;optionRouter&quot; that calls the Router function. Define a new route with a PATCH method that has a prefix of &quot;/update-by-id&quot; and uses the &quot;updateOptionByIdController&quot;. </p><pre><code class="language-Typescript">import { Router } from &quot;express&quot;;
import { updateOptionByIdController } from &quot;../controllers/options.controller&quot;;

export const optionRouter = Router();

optionRouter.patch(&quot;/update-by-id&quot;, updateOptionByIdController);</code></pre><h3 id="creating-an-options-route">Creating an Options Route</h3><ol><li>Import the options router into app.ts</li><li>Have a new app.use &quot;/options&quot; for the </li></ol><p>Lastly, in order to use the router we need to make sure the server is able to use the &quot;options.routes.ts&quot;. In the &quot;app.ts&quot; file import the options router. Have a new &quot;app.use&quot; method, and specify the path as &quot;/options&quot; while using the &quot;optionRouter&quot;. </p><pre><code class="language-Typescript">import { optionRouter } from &apos;./router/options.routes&apos;;

app.use(&quot;/options&quot;, optionRouter);</code></pre><h2 id="conclusion">Conclusion</h2><p>This was a fairly simple update that built upon my previous work. This was the first update that allowed my app to be able to have options my independent from polls. As this project continues more combined and independent systems will need to be developed, this was, but a brick in the foundation that will continue to grow. </p><hr><p>If you would like to check out the code or us it for yourself:&#xA0;<a href="https://github.com/JesstinSwadley/polling-app?ref=jesstin.com" rel="noreferrer">Github Link</a></p><p>If this blog was helpful to you please consider subscribing.</p>]]></content:encoded></item><item><title><![CDATA[Polling Webs App - Routers, Controllers & Updating Polls]]></title><description><![CDATA[<p>As I have continued to develop the polling app parts of it have grown and will continue to grove unless I get ahead of it. Currently I only have a few routes, however it has become apparent that I have to separate  the router and controller from &quot;app.ts&</p>]]></description><link>https://www.jesstin.com/polling-webs-app-routers-controllers-updating-polls/</link><guid isPermaLink="false">6747bab27363e704d127719c</guid><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Mon, 02 Dec 2024 18:00:10 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1594935975218-a3596da034a3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHN1YndheSUyMG1hcHxlbnwwfHx8fDE3MzMxMjk2NDh8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1594935975218-a3596da034a3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHN1YndheSUyMG1hcHxlbnwwfHx8fDE3MzMxMjk2NDh8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Polling Webs App - Routers, Controllers &amp; Updating Polls"><p>As I have continued to develop the polling app parts of it have grown and will continue to grove unless I get ahead of it. Currently I only have a few routes, however it has become apparent that I have to separate  the router and controller from &quot;app.ts&quot;. While it might seem like optimizing a bit too early, doing so now, while the app&apos;s functionality is fairly simple, makes it more manageable. Along with separating the router and controller, I also added functionality to allow polls to be updated. This is the first time a feature was made entirely with the new separation. </p><h2 id="polling-router-and-controller">Polling Router and Controller</h2><h3 id="separating-the-routes-into-a-router">Separating the routes into a Router</h3><p>Starting off by separating the routes from &quot;app.ts&quot;, first make a new directory in the &quot;src&quot; directory called router that will host all of the router files. Within the new directory add a file named &quot;polls.routes.ts&quot; this file will host the routes related to polls. From &quot;app.ts&quot; copy the existing poll routes and paste them into the newly created file. Import the &quot;Router&quot; module from Express and make an export const that use the &quot;Router&quot; module name &quot;pollRouter&quot;. Lastly change two things about the existing routes, change it so they use &quot;pollRouter&quot; instead of &quot;app&quot; and the create names so they are &quot;create&quot;, &quot;get-all&quot; and &quot;get-by-id&quot;.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { Router } from &quot;express&quot;

export const pollRouter = Router();

pollRouter.post(&quot;/create&quot;, ...);
pollRouter.get(&quot;/get-all&quot;, ...);
pollRouter.get(&quot;/get-by-id&quot;, ...);</code></pre><figcaption><p><span style="white-space: pre-wrap;">polls.routes.ts file and code</span></p></figcaption></figure><h3 id="creating-a-controller-for-the-router">Creating a Controller for the Router</h3><p>For the next part, is to separate out the controller from the router. Similar to what was done with the router, make a new directory in the &quot;src&quot; called &quot;controller&quot;. Make a new file called &quot;polls.controller.ts&quot; where the controller of polls will live. In the newly created file import &quot;Request&quot; and &quot;Response&quot; from &quot;Express&quot;. Additionally import the services from &quot;addPolls.ts&quot; and &quot;getPolls.ts&quot;. Create three export functions, they are named the following &quot;addPollController&quot;, &quot;getAllPollsController&quot; and &quot;getPollsByIdController&quot;. From each route copy the portion of the route that handles what the route actually doesinto the appropriate function. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { Request, Response } from &quot;express&quot;;
import { addPoll } from &quot;../services/polls/addPolls&quot;;
import { getAllPolls, getPollsById } from &quot;../services/polls/getPolls&quot;;

export const addPollController = async (req: Request, res: Response) =&gt; {
	try {
		const { question, options } = req.body;

		if (!question) {
			return res.sendStatus(400);
		}

		const poll = await addPoll(question, options)

		return res.status(201).send(&quot;Poll was created&quot;);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);
	}
}

export const getAllPollsController = async (req: Request, res: Response) =&gt; {
	try {
		const polls = await getAllPolls();

		return res.status(200).json(polls);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);	
	}
}

export const getPollByIdController = async (req: Request, res: Response) =&gt; {
	try {
		const { pollID } = req.query

		if (!pollID) {
			return res.sendStatus(400);
		}

		const poll = await getPollsById(pollID);

		return res.status(200).json(poll);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);	
	}
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">polls.controller.ts</span></p></figcaption></figure><h3 id="setting-up-the-router-for-the-server">Setting Up the Router for the Server</h3><p>With the router and controller being separated, the functions &quot;addPollController&quot;, &quot;getAllPollsController&quot; and &quot;getPollsByIdController&quot; need to be imported into &quot;polls.controller.ts&quot;. Replace the existing router logic and the correct controller so that the route uses that controller. Moving from the poll router to &quot;app.ts&quot; remove any routes in the file. Import the router into app.ts and make a new endpoint that uses the poll router with a route prefix of &quot;/polls&quot;.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { Router } from &quot;express&quot;;
import { addPollController, getAllPollsController, getPollByIdController } from &quot;../controllers/polls.controller&quot;;


export const pollRouter = Router();

pollRouter.post(&quot;/create&quot;, addPollController);
pollRouter.get(&quot;/get-all&quot;,getAllPollsController);
pollRouter.get(&quot;/get-by-id&quot;, getPollByIdController);</code></pre><figcaption><p><span style="white-space: pre-wrap;">polls.routes.ts with controller</span></p></figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { pollRouter } from &apos;./router/polls.routes&apos;;

app.use(&quot;/polls&quot;, pollRouter);</code></pre><figcaption><p><span style="white-space: pre-wrap;">app.ts new polls route</span></p></figcaption></figure><h2 id="updating-polls">Updating Polls</h2><h3 id="making-the-updatepoll-service">Making the updatePoll Service</h3><p>Moving on from the separation of the router and controller from &quot;app.ts&quot;, to updating a poll, more specifically its question/query. In the &quot;services/polls&quot; directory a new file was added named &quot;updatePolls.ts&quot;. In this file these were imported into it, &quot;eq&quot; from &quot;drizzle-orm&quot;, &quot;db&quot; from &quot;drizzle.ts&quot;, and &quot;polls&quot; from &quot;schema.ts&quot;. An exported function,&#xA0;&quot;updatePollById&quot;, was created to handle poll updates. This function takes two arguments:&#xA0;&quot;pollID&quot;&#xA0;of type&#xA0;&quot;any&quot;&#xA0;and&#xA0;&quot;updatedQuestion&quot;&#xA0;of type&#xA0;&quot;string&quot;. The function queries the database to locate a poll with a matching provided ID then update the question field to the provided string. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { eq } from &quot;drizzle-orm&quot;;
import { db } from &quot;../../db/drizzle&quot;;
import { polls } from &quot;../../db/schema&quot;;

export const updatePollById = async (pollID: any, updatedQuestion: string) =&gt; {
	const updatePoll = await db
          .update(polls)
          .set({
            question: updatedQuestion
          })
          .where(eq(polls.id, pollID))

	return updatePoll
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">updatePolls.ts</span></p></figcaption></figure><h3 id="making-the-updatepoll-controller">Making the updatePoll Controller</h3><p>A new controller for the &quot;updatePollById&quot; needs to be created. Start by importing the &quot;updatePollById&quot; service into the polls controller. Make a new export function called &quot;updatePollByIdController&quot; that uses a try-catch block. Within the try block, destructure the &quot;pollID&quot; and &quot;updatedQuestion&quot; from the request body, validate the presence of both, and call the &quot;updatePollId&quot; function to perform the database update. Once the database update is completed is send a success response, while the catch block handles the any errors. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { updatePollById } from &quot;../services/polls/updatePolls&quot;;

export const updatePollByIdController = async (req: Request, res: Response) =&gt; {
	try {
		const { pollID, updatedQuestion } = req.body;

		if (!pollID) {
			return res.sendStatus(400);
		}

		const poll = await updatePollById(pollID, updatedQuestion)

		return res.status(200).send(&quot;Poll was created&quot;);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);
	}
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">update polls by id in polls.controller.ts</span></p></figcaption></figure><h3 id="making-the-updatepoll-route">Making the updatePoll Route</h3><p>For the server to handle polls update a new route has to be made, the route&apos;s prefix will be &quot;/update-by-id&quot; and the HTTP method will be a PATCH. Import the &quot;updatePollByIdController&quot; into &quot;polls.router.ts&quot; and have the newly created route use the imported function. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { ..., updatePollByIdController } from &quot;../controllers/polls.controller&quot;;


pollRouter.patch(&quot;/update-by-id&quot;, updatePollByIdController);</code></pre><figcaption><p><span style="white-space: pre-wrap;">update poll route</span></p></figcaption></figure><h2 id="conclusion">Conclusion </h2><p>By separating out the router and controller from &quot;app.ts&quot; early, I make structure of the project easier to work with as I continue from this point onward. It might be a little premature to this, but by addressing this now I don&apos;t have to worry about having to handle such a larger overhaul to achieve the same end goal.</p><p>Trying out the new structure of the app with an additional feature was a great test of how well it can be done, while at the same time expanding what the app is capable of. There is still more to be done for the backend at the moment, but the app continues to take shape with each new feature. </p><hr><p>If you would like to check out the code or us it for yourself:&#xA0;<a href="https://github.com/JesstinSwadley/polling-app?ref=jesstin.com" rel="noreferrer">Github Link</a></p><p>If this blog was helpful to you please consider subscribing.</p>]]></content:encoded></item><item><title><![CDATA[Polling Webs App - Adding Results & Getting Options with a Poll]]></title><description><![CDATA[<p>Unfortunately I made a rather simple mistake when requesting a poll by its id. Looking back I should have noticed it sooner, by mainly testing things I some times won&apos;t test it or it will simply slip my made to do so. Regardless all you can hope for</p>]]></description><link>https://www.jesstin.com/polling-webs-app-adding-results-getting-options-with-a-poll/</link><guid isPermaLink="false">674170bc7363e704d1277045</guid><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Mon, 25 Nov 2024 18:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1499750310107-5fef28a66643?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fGNvbXB1dGVyJTIwcXVpenxlbnwwfHx8fDE3MzI2MDE0OTZ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1499750310107-5fef28a66643?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fGNvbXB1dGVyJTIwcXVpenxlbnwwfHx8fDE3MzI2MDE0OTZ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Polling Webs App - Adding Results &amp; Getting Options with a Poll"><p>Unfortunately I made a rather simple mistake when requesting a poll by its id. Looking back I should have noticed it sooner, by mainly testing things I some times won&apos;t test it or it will simply slip my made to do so. Regardless all you can hope for is to catch them early before they become greater issues in the future. </p><p>The process of making my polling app continues, when you get a poll you might also want the options for it. Separately you might also like to have results for a poll. While it would be easy to store the results along side their options, I am experimenting to see how things will go if they were split from each other. </p><h2 id="fixing-issues-with-polls-get-request">Fixing Issues with Polls GET Request </h2><p>While testing &quot;/single-poll&quot; two issues were discovered, those being you can&apos;t use request.body with a GET request and the type for &quot;getPollsById&quot; cause issues when changing to query strings. The first fix is fairly simple, while still destructing for &quot;pollID&quot; change the req.body to req.query. This change however causes issues with the &quot;getPollsById&quot; function as its wants to take in a string, but gets a Query String instead. To address this conflict simply changing the type in &quot;getPollsById&quot; from &quot;string&quot; to &quot;any&quot; resolves this. </p><h2 id="returning-poll-options-with-getting-polls">Returning Poll Options with Getting Polls</h2><h3 id="collecting-poll-options">Collecting Poll Options</h3><p>To collect all options for a specific poll, a new service file, &quot;getOptions.ts&quot;, was added to the services/options directory. Within the file, similar to &quot;getPolls.ts&quot;, import &quot;eq&quot; from &quot;drizzle-orm&quot;, &quot;db&quot; from &quot;drizzle.ts&quot;, and the matching schema. Make a new export function named &quot;getAllPollOptions&quot; that takes &quot;pollID&quot; as an argument with a type of &quot;any&quot;. In the function have a database query that does the following, selects the &quot;id&quot; and &quot;option&quot; fields, uses the options table, and has the options.pollId is equal to the &quot;pollID&quot;. Store the query in a const and return the const. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { eq } from &quot;drizzle-orm&quot;;
import { db } from &quot;../../db/drizzle&quot;;
import { options } from &quot;../../db/schema&quot;;

export const getAllPollOptions = async (pollID: any) =&gt; {
	const pollOptions = await db
    	.select({
          id: options.id,
          option: options.option
        })
        .from(options)
        .where(eq(options.pollId, pollID))

	return pollOptions
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">getAllPollOptions service</span></p></figcaption></figure><h3 id="getting-a-poll-also-gets-their-options">Getting A Poll Also Gets Their Options</h3><p>Now that there is something to collect the options for a poll, update the &quot;getPollsById&quot; to also respond with the options for that poll. In the &quot;getPoll.ts&quot; import the &quot;getAllPollOptions&quot; function. Add an empty Typescript Object named &quot;results&quot; within &quot;getPollsById&quot;. Next store the database query for polls in a const named &quot;poll&quot;. After that have a const name &quot;options&quot; that calls the &quot;getAllPollOptions&quot; using the &quot;poll&quot; id const as an argument. Have result now equal &quot;poll&quot; and &quot;options&quot;, lastly return results. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">export const getPollsById = async (pollID: any) =&gt; {
	let result = {}

	const poll = await db
		.select()
		.from(polls)
		.where(eq(polls.id, pollID))

	const options = await getAllPollOptions(poll[0].id)

	result = {poll, options}

	return result
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Updated getPollsById</span></p></figcaption></figure><h2 id="poll-results">Poll Results</h2><h3 id="creating-the-results-schema">Creating the Results Schema</h3><p>For the third schema for the polling application, a new &quot;results&quot; scheme is added to &quot;schema.ts&quot; it will track a poll&apos;s outcome. Like the other schemas, it uses the &quot;pgTable&quot; function with the tables naming being &quot;results&quot;. The results table is composed of four parts. First the &quot;id&quot;, similar to the others it is a generated uuid. Next the &quot;total&quot; is an integer that will store the total votes for an option. There will be two foreign keys used, one for pollId and the other for optionsId. Both foreign keys will be configured with cascading deletes, that automatically removing related results when a poll or its options are deleted.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">export const results = pgTable(&apos;results&apos;, {
	id: uuid(&apos;id&apos;)
          .default(sql`gen_random_uuid()`)
          .primaryKey(),
	total: integer(&apos;total&apos;),
	optionId: uuid(&apos;option&apos;)
          .references(() =&gt; options.id, {onDelete: &apos;cascade&apos;})
          .notNull(),
	pollId: uuid(&apos;poll_id&apos;)
          .references(() =&gt; polls.id, {onDelete: &apos;cascade&apos;})
          .notNull()
});</code></pre><figcaption><p><span style="white-space: pre-wrap;">Results Schema</span></p></figcaption></figure><h3 id="returning-the-options-id">Returning the Option&apos;s ID</h3><p>As the &quot;results&quot; schema requires the &quot;optionId&quot; there needs to be some changes done to &quot;addOption&quot;. To guarantee that results will be made the &quot;addOption&quot; needs to store an &quot;id&quot; as a const when it is inserting an option into the database. The function takes that const value and returns it. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">export const addOption = async (option: string, pollID: string) =&gt; {
	const id = await db
    	.insert(options)
    	.values({
    		option,
    		pollId: pollID
    	})
    	.returning({ id: options.id })
					
	return id
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Modified addOption function</span></p></figcaption></figure><h3 id="make-a-results-entry">Make a Results Entry</h3><p>Finishing the setup for the &quot;results&quot; schema, create a new directory in the &quot;services&quot; directory for &quot;results&quot;. Within this directory, make a file called &quot;addResults.ts&quot; to handle adding results to a database. Import the &quot;db&quot; from &quot;drizzle.ts&quot; and &quot;results&quot; from &quot;schema.ts&quot;. A new export function called &quot;addResult&quot; is added to the file. The function takes two arguments the first being &quot;optionID&quot; and the other being &quot;pollID&quot;, both with a type of any. For the moment it simply calls the db function to insert into the database with values of the option id, poll id, and setting the total to zero. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { db } from &quot;../../db/drizzle&quot;;
import { results } from &quot;../../db/schema&quot;;

export const addResult = async (optionID: any, pollID: any) =&gt; {
	await db
	.insert(results)
	.values({
		total: 0,
		optionId: optionID,
		pollId: pollID
	})
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">addResult</span></p></figcaption></figure><h3 id="modify-create-polls-to-create-results-as-well">Modify Create Polls To Create Results As Well</h3><p>To make sure that results are being made the fastest way to incorporate it into the existing code is to import it in &quot;addPoll.ts&quot;. While the for loop is making options change the function to store the return id in a let variable. Then call the &quot;addResult&quot; function with the poll id and option id as its arguments. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { addResult } from &quot;../results/addResults&quot;;

export const addPoll = async (question: string, options: any) =&gt; {
	const poll = await db
			.insert(polls)
			.values({
				question
			})
			.returning({ id: polls.id })

	for (const value in options) {
		let option = await addOption(options[value], poll[0].id)
		addResult(option[0].id, poll[0].id)
	}

	return poll[0].id</code></pre><figcaption><p><span style="white-space: pre-wrap;">Modified addPoll.ts</span></p></figcaption></figure><h2 id="conclusion">Conclusion</h2><p>As I continue to make this polling app, I will continue to make both minor and major issues I hope to catch them before I post about it, but that is not something I can guarantee. For the moment I am happy with the solution, but I might come back to it and try some other solutions out. </p><p>The schemas for polls, options, and results should be enough for the time being, to setup most of the basic functionality for the polling app. That doesn&apos;t mean I will not have additional schemas in the future, but I will either be modifying the existing three or leave them as is. Speaking on basic functionality, I have now added to the app the ability to collect options and create results. </p><hr><p>If you would like to check out the code or us it for yourself:&#xA0;<a href="https://github.com/JesstinSwadley/polling-app?ref=jesstin.com" rel="noreferrer">Github Link</a></p><p>If this blog was helpful to you please consider subscribing.</p>]]></content:encoded></item><item><title><![CDATA[Polling Webs App - Adding Options & Getting Polls]]></title><description><![CDATA[<p>A poll is made of multiple parts, first the inquiry/question, next the options, and lastly the responses of the poll. At the moment I am only storing the first part, so it is time to expand my apps capabilities by storing options and getting polls. </p><h2 id="storing-options">Storing Options</h2><h3 id="options-schema">Options Schema</h3>]]></description><link>https://www.jesstin.com/polling-webs-app-add-options-getting-polls/</link><guid isPermaLink="false">673e9a027363e704d1276f23</guid><category><![CDATA[technology]]></category><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Wed, 20 Nov 2024 18:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1680602239356-f919632ce80d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHF1ZXN0aW9ubmFpcmV8ZW58MHx8fHwxNzMyMTU1ODk1fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1680602239356-f919632ce80d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHF1ZXN0aW9ubmFpcmV8ZW58MHx8fHwxNzMyMTU1ODk1fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Polling Webs App - Adding Options &amp; Getting Polls"><p>A poll is made of multiple parts, first the inquiry/question, next the options, and lastly the responses of the poll. At the moment I am only storing the first part, so it is time to expand my apps capabilities by storing options and getting polls. </p><h2 id="storing-options">Storing Options</h2><h3 id="options-schema">Options Schema</h3><p>In the &quot;schema.ts&quot; file, add a new exported constant called &quot;options.&quot; Like &quot;polls,&quot; it is created using the &quot;pgTable&quot; function, which takes two arguments: its name and a JavaScript object defining its columns. The &quot;options&quot; table includes an &quot;id&quot; column as a UUID and a &quot;text&quot; column for the option. Additionally, it introduces a &quot;poll_id&quot; column as a foreign key to reference the &quot;polls&quot; table, ensuring that each option is linked to a specific poll. To maintain data integrity, the foreign key is configured with a cascading delete, so all associated options are removed when a poll is deleted.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">export const options = pgTable(&apos;options&apos;, {
	id: uuid(&apos;id&apos;).default(sql`gen_random_uuid()`).primaryKey(),
	option: text(&apos;option&apos;).notNull(),
	pollId: uuid(&apos;poll_id&apos;).references(() =&gt; polls.id, {onDelete: &apos;cascade&apos;}).notNull()
});</code></pre><figcaption><p><span style="white-space: pre-wrap;">Postgres Options Table Schema</span></p></figcaption></figure><h3 id="addoptions-service">addOptions Service</h3><p>With the&#xA0;&quot;options&quot;&#xA0;schema in place, implement the &quot;addOption&quot; service to handle adding options to the database. This service is located in a new file,&#xA0;&quot;addOption.ts&quot;, stored a new directory &quot;options&quot; inside the&#xA0;&quot;services&quot;&#xA0;directory. The function takes two arguments: the option text and the associated poll ID. Using the&#xA0;&quot;db&quot;&#xA0;connection and the&#xA0;&quot;Options&quot;&#xA0;schema, it inserts the provided data into the&#xA0;&quot;options&quot;&#xA0;table, ensuring each option is linked to its respective poll.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { db } from &quot;../../db/drizzle&quot;;
import { options } from &quot;../../db/schema&quot;;

export const addOption = async (option: string, pollID: string) =&gt; {
	await db
      .insert(options)
      .values({option,pollId: pollID})
      .returning({ id: options.id })
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">addOption Service</span></p></figcaption></figure><h2 id="modifying-poll-services-routes">Modifying Poll Services &amp; Routes</h2><h3 id="updating-the-poll-service">Updating the Poll service</h3><p>Now that the &quot;addOption&quot; service is in place, the &quot;addPoll&quot; function can now support creating poll options after being saved in a database. Start by importing the &quot;addOption&quot; function into the &quot;addPoll.ts&quot; file. A second argument, &quot;options&quot; with a type of &quot;any&quot; is added to &quot;addPoll&quot;. Next store the poll id in a variable to be used for options. Then, a&#xA0;&quot;for&quot;&#xA0;loop iterates through the provided options, using the&#xA0;&quot;addOption&quot; service along with the poll id variable. Lastly, the function returns the ID of the created poll.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { db } from &quot;../../db/drizzle&quot;;
import { polls } from &quot;../../db/schema&quot;;
import { addOption } from &quot;../options/addOption&quot;;

export const addPoll = async (question: string, options: any) =&gt; {
	const poll = await db
      .insert(polls)
      .values({question})
      .returning({ id: polls.id })

	for (const value in options) {
		addOption(options[value], poll[0].id)
	}

	return poll[0].id
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Updated Poll service to create options</span></p></figcaption></figure><h3 id="adding-options-to-the-poll-post-route">Adding Options to the Poll POST Route</h3><p>Having updated the addPoll function, it makes sense to have those changes be reflective in the POST &quot;/poll&quot; route in &quot;app.ts&quot;. Along with destructing the &quot;question&quot; from the &quot;req.body&quot;, &quot;options&quot; can also be destructed. Pass the &quot;options&quot; as the second argument in the &quot;addPoll&quot; function. After successfully creating a poll, instead of send the id as the response message send a string saying &quot;Poll was created&quot;. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">app.post(&quot;/poll&quot;, async (req: Request, res: Response) =&gt; {
	try {
		const { question, options } = req.body;

		if (!question) {
			return res.sendStatus(400);
		}

		const poll = await addPoll(question, options)

		return res.status(201).send(&quot;Poll was created&quot;);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);
	}
});</code></pre><figcaption><p><span style="white-space: pre-wrap;">Updated POST Poll Route</span></p></figcaption></figure><h2 id="collecting-the-polls">Collecting The Polls</h2><p>The next feature to add is retrieving polls from the database, currently there are only two methods to do so. The first is to fetch all polls in the database, and the second being to retrieve a specific poll by its unique ID. Create a new file the polls services directory called &quot;getPolls.ts&quot;</p><h3 id="getpolls-service">getPolls Service</h3><p>In the new &quot;getPolls.ts&quot;, import the &quot;db&quot; from &quot;drizzle.ts&quot; and &quot;polls&quot; from &quot;schema.ts&quot;. Next add an export async function called &quot;getAllPolls&quot;. In the &quot;getAllPolls&quot; function have a const for storing the results from getting all polls from the database. The function returns the results of the query.</p><p>Import another package this time from &quot;drizzle-orm&quot; this being &quot;eq&quot; to apply a filter of equals to queries. Additionally, make a second export async function that takes a single argument of &quot;pollID&quot; with a type of string. Similar to &quot;getAllPolls&quot;  store the result from the database query in a &quot;result&quot; const. Use the &quot;where&quot; method to filter the query, using &quot;eq&quot; have the filter equal polls.id to pollID. Lastly have it return the results from the query.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { eq } from &quot;drizzle-orm&quot;;
import { db } from &quot;../../db/drizzle&quot;;
import { polls } from &quot;../../db/schema&quot;;

export const getAllPolls = async () =&gt; {
	const results = await db
		.select()
		.from(polls)

	return results
}

export const getPollsById = async (pollID: string) =&gt; {
	const result = await db
		.select()
		.from(polls)
		.where(eq(polls.id, pollID))

	return result
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">getPolls.ts Service</span></p></figcaption></figure><h3 id="get-polls-routes">Get Polls Routes</h3><p>After creating the service functions in &quot;getPolls.ts&quot;, the next step is to make routes for the corresponding functions. Both routes will be GET requests, one will be calling &quot;getAllPolls&quot; function, while the other will collect the &quot;pollID&quot; and using the &quot;getPollById&quot; function.</p><p>The first GET request route called &quot;/all-polls&quot; calls the &quot;getAllPolls&quot; function in a try/catch block. If it successfully retrieves the polls it returns a response with a status code of 200 and the list of polls as JSON. If there were any errors it simply logs them in the console and returns a 400 status code. </p><p>For the second GET request route, this time called &quot;/single-poll&quot;, it calls the &quot;getPollsById&quot; again in a try/catch block. In the try portion restructure the &quot;pollID&quot; from &quot;req.body. Next check that it isn&apos;t empty if it is simply return a response of 400. Follow the check, call the &quot;getPollById&quot; function and store its value in a const of &quot;poll&quot;. Return a response of 200 along with the &quot;poll&quot; const in JSON. Lastly for catch, use the same code for the error handling. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">app.get(&quot;/all-polls&quot;, async(req: Request, res: Response) =&gt; {
	try {
		const polls = await getAllPolls();

		return res.status(200).json(polls);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);	
	}
});

app.get(&quot;/single-poll&quot;, async(req: Request, res: Response) =&gt; {
	try {
		const { pollID } = req.body;

		if (!pollID) {
			return res.sendStatus(400);
		}

		const poll = await getPollsById(pollID);

		return res.status(200).json(poll);
	} catch (error) {
		console.error(error);
		return res.sendStatus(400);	
	}
});</code></pre><figcaption><p><span style="white-space: pre-wrap;">Get Poll Routes</span></p></figcaption></figure><h2 id="conclusion">Conclusion</h2><p>Now that I&#x2019;ve integrated the ability to create polls and options, the app can seamlessly store and link options to their corresponding polls. By updating the <code>addPoll</code> function to handle options after a poll is saved, I ensured that each poll can have a dynamic set of options attached to it. The <code>addOption</code> service is used to store those options in the database, making the whole process efficient.</p><p>To make this work, I updated the POST route for creating polls (<code>/poll</code>) to accept both the poll question and the options, which are passed to the <code>addPoll</code> function. After the poll is created, I iterate over the options and use the <code>addOption</code> service to store them. Additionally, I created routes to retrieve polls, whether fetching all polls or a specific poll by its ID. These routes use the newly created services functions to query the database, making it easy to access both poll data and its options.</p><hr><p>If you would like to check out the code or us it for yourself:&#xA0;<a href="https://github.com/JesstinSwadley/polling-app?ref=jesstin.com" rel="noreferrer">Github Link</a></p><p>If this blog was helpful to you please consider subscribing.</p>]]></content:encoded></item><item><title><![CDATA[Polling Web App - Storing Polls]]></title><description><![CDATA[<p>As I continue my experiment with Typescript, I&apos;ve learned that the Typescript support for Sequelize while existing, isn&#x2019;t quite there yet. There&#x2019;s a fair amount of tinkering involved, in order to make everything work. This raised the question: why not explore a new ORM?</p>]]></description><link>https://www.jesstin.com/polling-web-app-storing-polls/</link><guid isPermaLink="false">664e768d7363e704d1276b7f</guid><category><![CDATA[technology]]></category><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Mon, 18 Nov 2024 18:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1597852074816-d933c7d2b988?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDR8fGRhdGFiYXNlfGVufDB8fHx8MTcxNjQxOTIzOXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1597852074816-d933c7d2b988?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDR8fGRhdGFiYXNlfGVufDB8fHx8MTcxNjQxOTIzOXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Polling Web App - Storing Polls"><p>As I continue my experiment with Typescript, I&apos;ve learned that the Typescript support for Sequelize while existing, isn&#x2019;t quite there yet. There&#x2019;s a fair amount of tinkering involved, in order to make everything work. This raised the question: why not explore a new ORM? </p><p>There are several natively supported Typescript ORMs, however I decided to go with <a href="https://orm.drizzle.team/?ref=jesstin.com" rel="noreferrer">Drizzle ORM</a>. If I am already learning a new ORM I might as well try my hand at learning a new database as well. Having experience SQL through MySQL, PostgreSQL seemed like a safe, but unique option in this &quot;in for a penny, in for a pound&quot; experiment. </p><h2 id="npm-packages-packagejson-scripts">NPM Packages &amp; Package.JSON Scripts</h2><p>To get started with Drizzle, you need to install the necessary dependencies. Run the following command to install the dependencies Drizzle ORM &amp; Node-Postgres with the following command:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">npm i drizzle-orm pg</code></pre><figcaption><p><span style="white-space: pre-wrap;">NPM Install Drizzle ORM &amp; Node-Postgres</span></p></figcaption></figure><p>To ensure some additional tooling install types for Postgres and drizzle-kit for a CLI tool for Drizzle:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">npm i -D @types/pg drizzle-kit</code></pre><figcaption><p><span style="white-space: pre-wrap;">NPM Install Dev Dependencies Drizzle Kit &amp; Node-Postgres Types</span></p></figcaption></figure><p>This setup ensures both runtime and development tools for Drizzle ORM and Postgres.</p><p>Modifying the&#xA0;&quot;package.json&quot;&#xA0;file to include two scripts for database management. These scripts are used for: generating SQL files and applying migrations to Postgres.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">&quot;scripts&quot;: {
  &quot;db:generate&quot;: &quot;drizzle-kit generate --config=./src/drizzle.config.ts&quot;,
  &quot;db:migrate&quot;: &quot;drizzle-kit migrate --config=./src/drizzle.config.ts&quot;
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Package.JSON Drizzle-Kit Scripts</span></p></figcaption></figure><h2 id="configuring-drizzle-kit">Configuring Drizzle-Kit </h2><p>After setting up the packages and scripts, create a&#xA0;&quot;drizzle.config.ts&quot;&#xA0;file in the&#xA0;&quot;src&quot;&#xA0;directory. This file is critical for configuring Drizzle ORM, as it specifies how to connect to the database and where to store the schema and migration files.</p><p>This file imports &quot;defineConfig&quot; from Drizzle Kit, configures database credentials using environment variables, and defines paths for the schema and migration files. Using environment variables helps keep sensitive database credentials secure and out of the source code.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { defineConfig } from &apos;drizzle-kit&apos;;

const host = process.env.DB_HOST;
const port = process.env.DB_PORT;
const user = process.env.DB_USER;
const password = process.env.DB_PASSWORD;
const database = process.env.DB_DATABASE;

export default defineConfig({
	dialect: &quot;postgresql&quot;,
	schema: &quot;./src/db/schema.ts&quot;,
	out: &quot;./src/db/migrations&quot;,
	dbCredentials: {
		host,
		port,
		user,
		password,
		database
	}
});</code></pre><figcaption><p><span style="white-space: pre-wrap;">Typescript code for Drizzle Config</span></p></figcaption></figure><p>To avoid committing generated files to version control, add the migrations directory to the&#xA0;&quot;.gitignore&quot;&#xA0;file:</p><figure class="kg-card kg-code-card"><pre><code># Drizzle Migrations
*migrations/</code></pre><figcaption><p><span style="white-space: pre-wrap;">Gitignore ignore &quot;migrations&quot; directories</span></p></figcaption></figure><h2 id="drizzleorm-connection-schema">DrizzleORM Connection &amp; Schema</h2><p>With the configuration in place, the next step involved creating the database schema and establishing a connection. A new &quot;db&quot; directory was added in the &quot;src&quot; directory to organize the schema and database connection.</p><p>The &quot;db&quot; directory contains two files. Those files being &quot;drizzle.ts&quot;and &quot;schema.ts&quot;. </p><figure class="kg-card kg-code-card"><pre><code>| db
|-- drizzle.ts
|-- schema.ts</code></pre><figcaption><p><span style="white-space: pre-wrap;">db directory structure</span></p></figcaption></figure><h3 id="database-schema">Database Schema</h3><p>In &quot;schema.t, the database schema is defined by importing &quot;sql&quot; from &quot;drizzle-kit&quot; along with &quot;pgTable&quot;, &quot;text&quot;, and &quot;uuid&quot; from &quot;drizzle-orm/pg-core&quot;.</p><p>An exported constant &quot;polls&quot; is created as a &quot;pgTable&quot; function. This function takes two arguments: the database name as a string and a JavaScript object specifying the column names and data types. </p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { sql } from &apos;drizzle-orm&apos;;
import { pgTable, serial, text, uuid } from &apos;drizzle-orm/pg-core&apos;;

export const polls = pgTable(&apos;polls&apos;, {
	id: uuid(&apos;id&apos;).default(sql`gen_random_uuid()`).primaryKey(),
	question: text(&apos;question&apos;).notNull()
});</code></pre><figcaption><p><span style="white-space: pre-wrap;">scheme.ts </span></p></figcaption></figure><h3 id="postgresql-pool-connection">PostgreSQL Pool Connection</h3><p>In &quot;drizzle.ts&quot;, the database connection is created by importing &quot;drizzle&quot; from &quot;drizzle-orm/node-postgres&quot; and &quot;Pool&quot; from &quot;pg&quot;. Variables are defined to hold environment variables for database credentials, which are then used to configure a new &quot;Pool&quot; connection. Finally, &quot;drizzle&quot; is exported with the SQL connection.</p><pre><code class="language-Typescript ">import { drizzle } from &quot;drizzle-orm/node-postgres&quot;;
import { Pool } from &quot;pg&quot;;

const host = process.env.DB_HOST;
const port = process.env.DB_PORT;
const user = process.env.DB_USER;
const password = process.env.DB_PASSWORD;
const database = process.env.DB_DATABASE;

const sql: Pool = new Pool({
	host,
	port,
	user,
	password,
	database
});

export const db = drizzle(sql);</code></pre><h2 id="developing-the-poll-feature"><strong>Developing the Poll Feature</strong></h2><h3 id="adding-poll-logic">Adding Poll Logic</h3><p>After configuring the setup, the next step was to develop the logic for creating polls. A &quot;services&quot; directory was created with a &quot;polls&quot; subdirectory to organize poll-specific logic.</p><figure class="kg-card kg-code-card"><pre><code>| src
|-- services
|---- polls </code></pre><figcaption><p><span style="white-space: pre-wrap;">Services directory &amp; polls subdirectory</span></p></figcaption></figure><p>In the &quot;polls&quot; directory, an &quot;addPolls.ts&quot; file was created to handle adding polls to the database. The database connection from &quot;drizzle.ts&quot; and the schema from &quot;schema.ts&quot; are imported. An exported async function named &quot;addPoll&quot; is defined, which takes a single argument, &quot;question&quot;, as a string. The function uses the &quot;db&quot; function to insert the question into the &quot;polls&quot; table and returns the generated ID.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">import { db } from &quot;../../db/drizzle&quot;;
import { polls } from &quot;../../db/schema&quot;;

export const addPoll = async (question: string) =&gt; {
  await 
    db
    .insert(polls)
    .values({question})
    .returning({ id: polls.id })
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Function to insert an entry into the Poll Table</span></p></figcaption></figure><h3 id="modify-post-poll-route">Modify POST Poll Route</h3><p>The POST&#xA0;&quot;/poll&quot;&#xA0;route in&#xA0;&quot;app.ts&quot;&#xA0;uses a simple try/catch block. </p><p>In the try section,&#xA0;&quot;req.body&quot;&#xA0;is destructured to extract the&#xA0;&quot;question&quot;&#xA0;field from the body. A guard clause checks if the<code>question</code>&#xA0;is provided; if not, an error response is returned. The ID value from the&#xA0;<code>addPoll</code>&#xA0;function is stored in a variable. A response with a status code 0f 201 is sent upon successful creation, including a JSON body with the poll ID. </p><p>In the catch section, any errors are logged, and a 400 response is returned.</p><figure class="kg-card kg-code-card"><pre><code class="language-Typescript">app.post(&quot;/poll&quot;, async (req: Request, res: Response) =&gt; {
  try {
      const { question } = req.body;

      if (!question) {
        return res.sendStatus(400);
      }

      const poll = await addPoll(question)

      return res.status(201).json(poll);
    
    } catch (error) {
      console.error(error);
      return res.sendStatus(400);
    }
});</code></pre><figcaption><p><span style="white-space: pre-wrap;">Modified POST Poll Request</span></p></figcaption></figure><h3 id="running-migrations-and-testing"><strong>Running Migrations and Testing</strong></h3><p>To generate the SQL files, run the following command in the terminal:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">PORT=Server_Port DB_HOST=Database_IP DB_PORT=Database_Port DB_USER=Database_Username DB_PASSWORD=Database_Password npm run db:generate</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to generate SQL</span></p></figcaption></figure><p>This created the SQL migration files in the specified directory. Next, apply the migrations:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">PORT=Server_Port DB_HOST=Database_IP DB_PORT=Database_Port DB_USER=Database_Username DB_PASSWORD=Database_Password npm run db:migrate</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to generate SQL</span></p></figcaption></figure><p>With the database set up, start the server:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">PORT=Server_Port DB_HOST=Database_IP DB_PORT=Database_Port DB_USER=Database_Username DB_PASSWORD=Database_Password npm run start</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to start the server</span></p></figcaption></figure><p>Test the&#xA0;&quot;/poll&quot;&#xA0;endpoint using Postman or VS Code&#x2019;s Thunder Client by sending a POST request:</p><figure class="kg-card kg-code-card"><pre><code class="language-JSON">{
  &quot;question&quot;: &quot;A simple question?&quot;
}
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Poll Request </span></p></figcaption></figure><p>Finally, verify the new poll&#x2019;s existence by querying the database directly in PostgreSQL.</p><h2 id="conclusion">Conclusion</h2><p>Setting up Drizzle ORM with TypeScript and PostgreSQL was an insightful and rewarding experience. While the initial configuration required some effort, the combination of TypeScript&apos;s type safety and Drizzle ORM&apos;s simplicity made the process more manageable and efficient. By taking the time to explore these tools, a strong foundation for building robust database-driven applications was established. The journey not only expanded my technical knowledge but also provided a deeper understanding of how to integrate modern TypeScript ORM solutions with PostgreSQL, which will be valuable for future projects.</p><hr><p>If you would like to check out the code or us it for yourself: <a href="https://github.com/JesstinSwadley/polling-app?ref=jesstin.com" rel="noreferrer">Github Link</a></p><p>If this blog was helpful to you please consider subscribing.</p>]]></content:encoded></item><item><title><![CDATA[Polling Web App - Setting up the Polls]]></title><description><![CDATA[<p>With my ongoing e-commerce project with <a href="https://jaml.co/?ref=jesstin.com" rel="noreferrer">Jamil Madison</a>, I wanted to experiment &amp; understand Typescript. As this is an election year, I thought a fun little project to do would be a &quot;simple&quot; polling app. This app will consist of a frontend, backend, and database. I chose to</p>]]></description><link>https://www.jesstin.com/polling-web-app-setting-up-the-polls/</link><guid isPermaLink="false">662499d57363e704d1276975</guid><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Fri, 15 Nov 2024 18:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1494172961521-33799ddd43a5?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHBvbGxzfGVufDB8fHx8MTcxNDYyNDE4OXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1494172961521-33799ddd43a5?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHBvbGxzfGVufDB8fHx8MTcxNDYyNDE4OXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Polling Web App - Setting up the Polls"><p>With my ongoing e-commerce project with <a href="https://jaml.co/?ref=jesstin.com" rel="noreferrer">Jamil Madison</a>, I wanted to experiment &amp; understand Typescript. As this is an election year, I thought a fun little project to do would be a &quot;simple&quot; polling app. This app will consist of a frontend, backend, and database. I chose to work with React for the frontend, Nodejs for the backend, &amp; MySQL for the database. Lastly I will deploy this to AWS, using EC2 and RDS. </p><p>While the above will make the application usable and accessible on the internet. I want to practice high scalability and high availability. For this reason, I will be using Auto Scaling Groups, Multi-AZ RDS, Amazon Elasticache, &amp; Elastic Load Balancers. </p><h2 id="setting-up-the-project">Setting up the project</h2><p>I setup the repo to include both the backend and frontend instead of having them separate. This means I will have a repo containing the &quot;server&quot; for the backend and &quot;client&quot; for the frontend.</p><p>First in the project directory I made the following files: LICENSE, README.md, &amp; .gitignore. The LICENSE file is the software license for the project; the README is a text file that contains information about the project it might contain instructions or helpful information; lastly the .gitignore tells git which folders or files to ignore.</p><p>Next created a directory called sever at the top level of the project. Within the server directory I added a &quot;src&quot; directory that will contain all the server code. Within the &quot;src&quot; directory I made a single file, that being app.ts. At this time the project should look like the following: </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/05/polling_app_project.png" class="kg-image" alt="Polling Web App - Setting up the Polls" loading="lazy" width="1360" height="660" srcset="https://www.jesstin.com/content/images/size/w600/2024/05/polling_app_project.png 600w, https://www.jesstin.com/content/images/size/w1000/2024/05/polling_app_project.png 1000w, https://www.jesstin.com/content/images/2024/05/polling_app_project.png 1360w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Polling App Project Structure</span></figcaption></figure><p>I completed the two following things: First is initializing git and second is initializing npm. Opening a terminal at the top level directory of the project run git init to initialize git. Next change directories to backend and run npm init, follow the prompts from npm to complete the npm init process. Once completed you can go back to the top level directory, add all the files to the git staging to track them, finally git commit the files. </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">$ git init
$ cd backend/
$ npm init
$ cd ..
$ git add .
$ git commit -m &quot;Your commit message&quot;</code></pre><figcaption><p><span style="white-space: pre-wrap;">Git &amp; NPM Init Commands</span></p></figcaption></figure><p>The last part of the setup I did was update the .gitignore for now I have a personal one that I use, however here is what I used to start mine: <a href="https://github.com/github/gitignore/blob/main/Node.gitignore?ref=jesstin.com">https://github.com/github/gitignore/blob/main/Node.gitignore</a></p><h2 id="creating-an-express-server">Creating an Express Server</h2><p>Moving from project setup, I started by installing Typescript &amp; Express Types as a developer dependency, as well as express as a dependency in the server directory. </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">$ cd server
$ npm i express
$ npm i -D typescript @types/express</code></pre><figcaption><p><span style="white-space: pre-wrap;">NPM Install Express, Typescript &amp; Express Types</span></p></figcaption></figure><p>Once completed it should add a new directory in the server directory called node_modules &amp; a new file called package-lock.json. The packages do the following:</p><ul><li>Express: a lightweight framework that simplifies web server creation</li><li>Typescript: a superset of Javascript that introduces types</li><li>Types/Express: are the type definitions for Express</li></ul><p>In the app.ts file I first imported express as well as the express type into the file. Next I made a constant to use express while making sure it&apos;s type is the express type.</p><figure class="kg-card kg-code-card"><pre><code class="language-typescript">import express, { Express } from &apos;express&apos;;
const app: Express = express();
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Import Express and use express</span></p></figcaption></figure><p>Next I want to make sure the server is listening to a port. Under the above code add the following to it:</p><figure class="kg-card kg-code-card"><pre><code class="language-typescript">app.listen(3000, () =&gt; {
  console.log(`Server is on Port: 3000`);
});</code></pre><figcaption><p><span style="white-space: pre-wrap;">Express server listening to port 3000</span></p></figcaption></figure><h2 id="configuring-typescript-for-nodejs">Configuring Typescript for Node.js</h2><p>While I may have made an Express server Node.js can&apos;t run typescript files so I need to make it possible for Node to run my code. First I ran the typescript compiler, that was installed locally when I installed typescript as a dev dependency, to initialize it. </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">$ npx tsc --init</code></pre><figcaption><p><span style="white-space: pre-wrap;">Typescript Init</span></p></figcaption></figure><p>This command will generate a tsconfig.json file that is used to configure how typescript behaves when compiling the typescript code. I changed two parts in the typescript config:</p><ol><li>Setting rootDir to &quot;./src&quot;</li><li>Setting outDir to &quot;./dist&quot;</li></ol><p>I simply uncommented them and changed the value to use them. The &quot;rootDir&quot; is checking what directory to compile the typescript from &amp; the &quot;outDir&quot; is what directory to use when compiling the typescript into javascript.</p><p>You can compile the typescript &amp; run the server from the terminal I added two scripts inside of the package.json:</p><figure class="kg-card kg-code-card"><pre><code class="language-json">&quot;build&quot;: &quot;tsc --build&quot;,
&quot;start&quot;: &quot;node ./dist/app.js&quot;</code></pre><figcaption><p><span style="white-space: pre-wrap;">Scripts to build typescript &amp; start a node server</span></p></figcaption></figure><p>The first script tells typescript to build/compile the &quot;src&quot; directory &amp; the second tells Node to start the server. I made these scripts so that I don&apos;t have to type out or cycle through previous terminal commands in order to run the server.</p><p>In the terminal simply run the two npm scripts together &amp; the server should start.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">$ npm run build
$ npm run start</code></pre><figcaption><p><span style="white-space: pre-wrap;">Run NPM Build &amp; Start Scripts in the terminal</span></p></figcaption></figure><h2 id="routes-user-input">Routes &amp; User Input</h2><p>Having a server that simply runs on a machine is nice, but I want it to be able to respond to request &amp; take in user input. </p><p>First I changed the express import to also have the Request &amp; Response Types. Next I configured the &quot;app&quot; to use express.json function. After that I created a PORT constant that either collects the environment variable &quot;PORT&quot; or uses 3000. Lastly I made a route that collects the request body &amp; responds with it. The app.ts should look like the following:</p><figure class="kg-card kg-code-card"><pre><code class="language-typescript ">import express, { Express, Request, Response } from &apos;express&apos;;

const app: Express = express();
app.use(express.json());

const PORT = process.env.PORT || 3000;

app.post(&quot;/poll&quot;, (req: Request, res: Response) =&gt; {
	const test = req.body.test;

	res.status(200).send(`Reponse ${test}`);
});

app.listen(PORT, () =&gt; {
	console.log(`Running on Port: ${PORT}`);
});</code></pre><figcaption><p><span style="white-space: pre-wrap;">app.ts file</span></p></figcaption></figure><p>If you would like to check out the code or us it for yourself: <a href="https://github.com/JesstinSwadley/polling-app?ref=jesstin.com" rel="noreferrer">Github Link</a></p><p>If this blog was helpful to you please consider subscribing.</p>]]></content:encoded></item><item><title><![CDATA[Vict E-commerce: Open for business]]></title><description><![CDATA[<p>For the last blog <a href="https://www.jesstin.com/vict-e-commerce-adding-basic-security/" rel="noreferrer">Vict E-commerce: Adding Basic Security</a> I implemented a change going from JWT to sessions while my overall understanding of security has improved slightly there is still room for improvement not only on my comprehension, but also in the code for this project. If you would like</p>]]></description><link>https://www.jesstin.com/vict-e-commerce-open-for-business/</link><guid isPermaLink="false">6627076f7363e704d1276979</guid><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Tue, 23 Apr 2024 20:14:14 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1603912699214-92627f304eb6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fGdyYW5kJTIwb3BlbmluZ3xlbnwwfHx8fDE3MTM4MzgwOTh8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1603912699214-92627f304eb6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fGdyYW5kJTIwb3BlbmluZ3xlbnwwfHx8fDE3MTM4MzgwOTh8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Vict E-commerce: Open for business"><p>For the last blog <a href="https://www.jesstin.com/vict-e-commerce-adding-basic-security/" rel="noreferrer">Vict E-commerce: Adding Basic Security</a> I implemented a change going from JWT to sessions while my overall understanding of security has improved slightly there is still room for improvement not only on my comprehension, but also in the code for this project. If you would like to read about the changes Jamil is doing check out his site:&#xA0;<a href="https://jaml.co/?ref=jesstin.com" rel="noreferrer">jaml.co</a></p><p>Over the previous two blogs we focused on making sure things were coming along to allow for merchants to have what they need to interact with for products &amp; storefronts. Now that those pieces have been developed we want to shift focus to make progress on the customer portion of the e-commerce application. </p><h2 id="first-time-customers">First Time Customers</h2><p>Similar to merchants we made a model for sequelize to use as a database table. We tracked the following things in a database:</p><ul><li>First Name</li><li>Last Name</li><li>Email</li><li>Password</li><li>ID</li><li>Created_At</li><li>Updated_At</li></ul><p>The model is put in the db directory in our src directory:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">module.exports = (sequelize, DataTypes) =&gt; {
	const Customer = sequelize.define(&quot;customers&quot;, 
	{
		firstName: {
			type: DataTypes.STRING,
			allowNull: false,
		},
		lastName: {
			type: DataTypes.STRING,
			allowNull: false
		},
		email: {
			type: DataTypes.STRING,
			allowNull: false,
			unique: true,
			validate: {
				isEmail: {
					msg: &quot;Must be a valid email address&quot;
				}
			}
		},
		password: {
			type: DataTypes.STRING,
			allowNull: false,
		},
		id: {
			type: DataTypes.UUID,
			defaultValue: DataTypes.UUIDV4,
			primaryKey: true,
			allowNull: false
		}
	});

	return Customer
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Customer Model</span></p></figcaption></figure><p>Sequelize generates for us the created_at &amp; updated_at options as well as generating a uuid upon an entry into the database being added. </p><h3 id="customer-business-logic">Customer Business Logic</h3><p>Now that sequelize can use the customer model we want our server to perform three functions to register, login, &amp; logout a customer. In the controller directory we want to make a customer controller. </p><p>For the register function we want it to do the following things:</p><ol><li>Collect the necessary data from the request body</li><li>Define the number of salt rounds to use for bcrypt</li><li>Encrypt the password that the customer provides </li><li>Make a new customer entry in the database</li><li>Make a new session for the recently registered customer</li><li>Send a response that the entry was successful</li></ol><p> The code should look like the following:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const registerCustomer = async (req, res) =&gt; {
	const { firstName, lastName, email, password } = req.body;
	const saltRounds = 10;

	try {
		const hashPassword = await bcrypt.hash(password, saltRounds);

		const customer = await Customer.create({
			firstName,
			lastName,
			email,
			password: hashPassword
		});

		req.session.userId = customer.id;
		return res.status(200).send({message: &quot;Registration successful&quot;, customer});
	} catch (error) {
		console.error(&quot;Registration error:&quot;, error);
		return res.status(500).send(error.message);
	}
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Register customer function</span></p></figcaption></figure><p>With a customer registered we want to allow them to login. The login function should do the following:</p><ol><li>Collect the email &amp; password from the request body</li><li>Check if the provided email is a customer in the customer database</li><li>Compare the provided password to the one stored in the database using bcrpyt</li><li>Make a new session for the customer</li><li>Send a response that the customer successfully logged in</li></ol><p>The login code should look like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const loginCustomer = async (req, res) =&gt; {
	const { email, password } = req.body;

	try {
		const customer = await Customer.findOne({ where: { email } });
		const passwordMatch = await bcrypt.compare(password, customer.password);

		if (customer == null || !passwordMatch) {
			return res.status(401).send(&quot;Incorrect Email or Password&quot;);
		}

		req.session.userId = customer.id;
		res.status(200).send(customer);
	} catch (error) {
		console.error(&quot;Login error:&quot;, error);
		return res.status(500).send(error.message);
	}
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Login customer function</span></p></figcaption></figure><p>Lastly for the customer controller functions is the function to logout a customer. Its logic is much simpler than registering or logging in a customer. We simply need to know the session we want to remove from the database &amp; delete that entry. </p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const logoutCustomer = async (req, res) =&gt; {
	const sId = req.session.id;

	req.session.destroy(sId, () =&gt; {
		res.send(&quot;Customer has been logged out&quot;);
	});
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Logout customer function</span></p></figcaption></figure><p>With the model &amp; controller made for the customer with need to make the routes necessary for the server to perform them upon request. Simply import the controller into the router file &amp; make three post routes that each execute a function. </p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const express = require(&quot;express&quot;);
const router = express.Router();
const { registerCustomer, loginCustomer, logoutCustomer } = require(&quot;../controllers/customer.controller&quot;);

router.post(&quot;/register&quot;, registerCustomer);
router.post(&quot;/login&quot;, loginCustomer);
router.post(&quot;/logout&quot;, logoutCustomer);


module.exports = router;</code></pre><figcaption><p><span style="white-space: pre-wrap;">Customer Router</span></p></figcaption></figure><h2 id="making-a-purchase">Making a purchase</h2><p>As with the rest of our code we want to make a model for sequelize to use to store transactions. The simplest transaction needs to track, the customer that wants to make a purchase, the product being purchased, the total of the transaction, and the id for the transaction. </p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">module.exports = (sequelize, DataTypes) =&gt; {
	const Transaction = sequelize.define(&quot;transactions&quot;, 
	{
		id: {
			type: DataTypes.UUID,
			defaultValue: DataTypes.UUIDV4,
			primaryKey: true,
			allowNull: false
		},
		total: {
			type: DataTypes.DECIMAL(10, 2),
			allowNull: false
		},
		customer_id: {
			type: DataTypes.STRING,
			allowNull: false
		},
		product_id: {
			type: DataTypes.STRING,
			allowNull: false
		}
	});

	return Transaction
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Transaction model</span></p></figcaption></figure><p>While it works as a transaction model I currently have defined it so it doesn&apos;t track multiple products. I will either have to change how a transaction tracks products or simply track that a transaction occurred and orders/purchases track products for a transaction. </p><h3 id="transaction-business-logic">Transaction Business Logic</h3><p>The logic necessary for transaction at this time needs to perform the following:</p><ul><li>Create a transaction upon purchase</li><li>Read All Transaction that a customer performed</li><li>Refunding a transaction</li></ul><p>In order to create a transaction we first need to collect the transaction total, the product&apos;s id, &amp; the customer&apos;s id from the request body. The next part is to create a transaction entry in the database. Finally sending a response if the transaction was completed successfully. </p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const createTransaction = async (req, res) =&gt; {
	const { total, product_id, customer_id } = req.body;

	try {
		const transaction = await Transaction.create({
			total,
			product_id,
			customer_id
		});

		return res.status(201).send(&quot;Transaction complete&quot;);
	} catch (error) {
		return res.send(error);
	}
};</code></pre><figcaption><p><span style="white-space: pre-wrap;">Create Transaction Function</span></p></figcaption></figure><p>At the moment I only made it possible to collect all the transactions for a customer not for a storefront or even a single transaction. To view all the transactions for a customer simply collect the customer id from the request query. </p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const getAllCustomerTransactions = async (req, res) =&gt; {
	const customer_id = req.query.customer_id
	
	let { count, rows } = await Transaction.findAndCountAll({ where: { customer_id }});

	return res.send(rows);
};</code></pre><figcaption><p><span style="white-space: pre-wrap;">Get All of a Customer&apos;s Transactions</span></p></figcaption></figure><p>Similar to the get all of a customer&apos;s transactions we only need to know the transaction&apos;s id and delete it from the database. </p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const refundTransaction = async (req, res) =&gt; {
	let id = req.query.transaction_id;

	try {
		await Transaction.destroy({
			where: {
				id,
			},
		});
		if (result[0] !== 0) {
			return res.send(&quot;Transaction refund successfully&quot;);
		}

		return res.status(404).send(&quot;Transaction not found.&quot;);	
	} catch (error) {
		return res.send(error);
	}
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Delete Transaction</span></p></figcaption></figure><h2 id="future-plans">Future Plans</h2><p>With the customer &amp; transactions now added this means we have made a fairly simple store, that is unable to process payment to complete transactions, but who is keeping track of that. We want to spend our next update making improvement to the actual user experience of the store. </p><p>On the customer side of things we first want to implement a cart to track multiple items for a transaction. Modify transactions for multiple product purchases. View their order history. While on the merchant side we want to modify their orders to track and perform actions on them. For both of them we want to add roles to prevent one or the other from performing actions unless they are supposed to. </p><p>If you would like to check out the code or us it for yourself:&#xA0;<a href="https://github.com/JesstinSwadley/vict-ecommerce?ref=jesstin.com" rel="noreferrer">Github Link</a></p>]]></content:encoded></item><item><title><![CDATA[Vict E-commerce: Adding Basic Security]]></title><description><![CDATA[<h2 id="researching-security-options">Researching Security Options</h2><p>In my previous blog <a href="https://www.jesstin.com/vict-ecommerce/" rel="noreferrer">Vict E-commerce: Setting Up</a> Shop we went over setting things up for working collaboratively between myself &amp; Jamil, as well as database management for merchants, products, &amp; storefronts. In this blog, I go over what we learned about JWT vs sessions and how</p>]]></description><link>https://www.jesstin.com/vict-e-commerce-adding-basic-security/</link><guid isPermaLink="false">6611a66f7363e704d1276866</guid><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Sun, 07 Apr 2024 19:12:26 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1496368077930-c1e31b4e5b44?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHNlY3VyaXR5fGVufDB8fHx8MTcxMjQzMjg5OXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<h2 id="researching-security-options">Researching Security Options</h2><img src="https://images.unsplash.com/photo-1496368077930-c1e31b4e5b44?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHNlY3VyaXR5fGVufDB8fHx8MTcxMjQzMjg5OXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Vict E-commerce: Adding Basic Security"><p>In my previous blog <a href="https://www.jesstin.com/vict-ecommerce/" rel="noreferrer">Vict E-commerce: Setting Up</a> Shop we went over setting things up for working collaboratively between myself &amp; Jamil, as well as database management for merchants, products, &amp; storefronts. In this blog, I go over what we learned about JWT vs sessions and how we implemented sessions. If you would like to read about the changes Jamil is doing check out his site: <a href="https://jaml.co/?ref=jesstin.com" rel="noreferrer">jaml.co</a></p><p>Originally we thought to do JsonWebTokens (JWT), however upon further research we discovered what we&apos;re currently developing sessions are a better direction. </p><p>A few reasons for the switch are:</p><ul><li>It is much harder to revoke JWT without significant development investment such as making a blacklist</li><li>Sessions provide a stronger guarantee that each individual request is authorized and are simpler to implement securely</li><li> If a user is signed into multiple devices and a condition on the account where to change such as permissions or login status there is no guarantee that all devices will change</li></ul><p>Both authentication methods require some consideration in order to prevent their specific vulnerabilities, but at the moment since we have made the decision to go with sessions, we are going to have to spend the time implementing solutions in order to mitigate those vulnerabilities.</p><h2 id="implementing-sessions">Implementing Sessions</h2><p>Sessions are normally stored in-memory on the server, however you can store sessions elsewhere. If you have a caching service, such as Redis, you might want to use that instead of in-memory storage on the server. Since we are using MySQL or more specifically Sequelize for MySQL, we will store sessions through Sequelize into our MySQL Database.</p><p>Install the following packages in order to create &amp; store sessions:</p><ul><li>express-session</li><li>connect-session-sequelize</li></ul><p>Express-session is used to create sessions with express &amp; connect-session-sequelize to connect express-session to mysql through sequelize. </p><figure class="kg-card kg-code-card"><pre><code class="language-npm">npm i express-session connect-session-sequelize</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">NPM Install Command to create sessions &amp; store them in database</span></p></figcaption></figure><p>In app.js add the following code to use the packages.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const session = require(&quot;express-session&quot;);
const SequelizeStore = require(&quot;connect-session-sequelize&quot;)(session.Store);</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Use express-session &amp; connect-session-sequlize</span></p></figcaption></figure><p>Now that we are able to use the packages that we installed. Next we will make sure that the express server is using sessions. For the session configuration we will be defining the following attributes:</p><ul><li>secret: used to sign the session ID cookie</li><li>store: where we will be storing the session</li><li>resave: whether or not we want to force saving sessions to store</li><li>saveUnitialized: whether or not we want to force saving sessions to store that is uninitiated</li><li>cookie:<ul><li>maxAge: how long a cookie will live</li><li>secure: whether or not your site&apos;s cookies will be secure, requires HTTPS</li><li>sameSite: set the Same-Site attribute in cookies</li></ul></li></ul><figure class="kg-card kg-code-card"><pre><code class="language-javascript">app.use(
	session({
		secret: &quot;{YOUR SECRECT KEY}&quot;,
		store: new SequelizeStore({
			db: db.sequelize,
		}),
		resave: false,
		saveUninitialized: false,
		cookie: {
			maxAge: 60000 * 60, 
			secure: false, 
			sameSite: &quot;Lax&quot;
		},
	})
);</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">app.use for express server to create &amp; store sessions</span></p></figcaption></figure><p>Now that our server can use sessions, we need to actually create them during our authentication. When registering or logging in with merchants we will create a session and have to be associated with our merchant&apos;s id.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">req.session.userId = merchant.id;</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Make a session and associate it to our merchant id</span></p></figcaption></figure><h3 id="adding-an-auth-check-middleware">Adding an Auth Check Middleware</h3><p>The server can now create &amp; store sessions with that we want to protect our storefront &amp; product routes, by using an authentication check. We created a middleware on our express server that will be in front of certain routes that first checks that they have a session with the server then allows them to perform the actions.</p><p>First make a directory in the &quot;src&quot; folder. Next we made a authCheck.middleware.js file that stores our logic for the middleware to be used by our routes. In the authCheck middleware we want to check for 2 things at the moment, one being if they have a session with us and two if that session is in our database. Last we need to make sure that we are exporting it so routes will be able to use them.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">module.exports = (req, res, next) =&gt; {
	console.log(&quot;authCheck middleware triggered&quot;);
	
	if (!req.session) {
		return res.status(500).json({ error: &quot;Internal server error&quot; });
	}

	try {
		if (req.session.userId) {
			next();
		} else {
			res.status(401).json({ error: &quot;Unauthorized. Please login.&quot; });
		}
	} catch (error) {
		res.status(500).json({ error: &quot;Internal server error&quot; });
	}
};</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Middleware to check if merchant has a session &amp; is in the session database</span></p></figcaption></figure><p>With a middleware created we want to import it in our storefront &amp; product router files. </p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">const authCheck = require(&quot;../middleware/authCheck.middleware&quot;);</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Import authCheck middleware</span></p></figcaption></figure><p>In the create, update, &amp; delete for both product &amp; storefront we will update the route to have a middleware check. Here is an example of what it should look like:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">router.post(&quot;/create&quot;, authCheck, createProduct);</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Example for authCheck</span></p></figcaption></figure><p>When a route is triggered it does the following, first it does the authCheck middleware, if successful it moves onto the controller code and if nothing goes wrong it will complete the code and respond to the merchant.</p><h3 id="updating-merchants-products">Updating Merchants &amp; Products</h3><p>There are some slight tweaks we want to get handled now while we are updating the security for the server. We want to update the merchant to collect first &amp; last names &amp; update products to have storefront association. </p><p>In the merchant model we updated the model to include first name &amp; last name with the data type being STRING &amp; allowNull to be false. This means that in the MySQL Table that we will store the data as strings and it cannot be null value.</p><p>Similar to the merchant model we updated the product model to include storefront id with a STRING data type &amp; allowNull to be false. This means that every product will be associated with a storefront.</p><p>In the merchant controller, we collect the first name &amp; last name when registering a merchant. At this time we don&apos;t have anything developed to update a merchants information or even the password. For the product we collect the storefront id when creating a product. When updating a product we shouldn&apos;t be able to switch products to a different storefront so we don&apos;t collect it during that.</p><h2 id="future-plans">Future Plans</h2><p>Our next part we want to develop &quot;customers&quot; and the necessary features for that, which will include: </p><ul><li>Completing transactions</li><li>Having an order history </li><li>User authentication. </li><li>User Profiles</li></ul><p>For the security we want to handle data sanitization to lower our risk of SQL Injections, rate limiting to prevent abuse &amp; DoS attacks. There are additional benefits to rate limiting besides those two such as resource management. </p><p>Once we implement &quot;customers&quot; we want to update the Personal Identifiable Information (PII) to have psuedonymization when storing data in the database.</p><p>If you would like to check out the code or us it for yourself: <a href="https://github.com/JesstinSwadley/vict-ecommerce?ref=jesstin.com" rel="noreferrer">Github Link</a></p>]]></content:encoded></item><item><title><![CDATA[Vict E-commerce: Setting Up Shop]]></title><description><![CDATA[<p>The world continues to move towards a digital and omni-channel approach when it comes to shopping, with the largest platforms being closed sourced, such as Amazon and Shopify, my friend Jamil and I decided to see what it would be like to develop a platform for ourselves. </p><p>A secondary reason</p>]]></description><link>https://www.jesstin.com/vict-ecommerce/</link><guid isPermaLink="false">65f676c97363e704d12766ec</guid><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Sun, 17 Mar 2024 22:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1534665482403-a909d0d97c67?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIzfHxjb2Rpbmd8ZW58MHx8fHwxNzEwNzgyMjM5fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1534665482403-a909d0d97c67?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIzfHxjb2Rpbmd8ZW58MHx8fHwxNzEwNzgyMjM5fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Vict E-commerce: Setting Up Shop"><p>The world continues to move towards a digital and omni-channel approach when it comes to shopping, with the largest platforms being closed sourced, such as Amazon and Shopify, my friend Jamil and I decided to see what it would be like to develop a platform for ourselves. </p><p>A secondary reason is, while not the most unique project we still have to solve certain features and challenges: user authentication, product management, data visualization, and lastly user interface and experience. </p><p>While we could have worked on the project separately or developed our own solutions to this, we decided to work together for this. One of the reasons being it&apos;s a chance for us to work collaboratively instead of individually on something. Next,  we currently each have a preference on working on different parts of the application. I have a preference with working on the backend and my friend, Jamil prefers to work on the frontend. </p><p>In our blog updates, I will concentrate on detailing the backend development aspects, my area of expertise, while Jamil will cover the backend development. Check out Jamil&apos;s insights on his blog here: <a href="https://www.jaml.co/writing/vict-ecommerce-1?ref=jesstin.com" rel="noreferrer">Jamil Blog</a></p><h2 id="collaboration">Collaboration</h2><h3 id="git-github">Git &amp; Github</h3><p>In order to make the collaboration between the two of us possible we have to use a version control system specifically Git. We setup a repository with Github for our code. I added Jamil as a collaborator in the Github repo settings to allow him to push code to it. We made two separate directories one for client side code and the other for server side code.  </p><p>Something simple we decide to do was work on git branches to get us comfortable with them on our local devices. For this project currently to get the code back onto the &quot;main&quot; branch we use merge to make this happen. At some point we hope to start using more of the tools for git branches. </p><h3 id="scope-of-work">Scope of work</h3><p>As we are making an e-commerce platform we wanted to start out with making Merchants (people who control products &amp; storefronts), Storefronts (a page to sell products), and lastly products (items to be sold). </p><p>When we first started coding we only had merchants and products. One of the things we realized, was a &quot;merchant&quot; might have multiple &quot;brands&quot; or &quot;storefronts&quot; to sell different products. So we decided before we made to many significant changes to separate the merchant from the storefront directly and have a merchant have the possibility for multiple storefronts, but every storefront must be associated with at least one merchant. </p><p>The current problems we have with the code is products have yet to be updated so that every product is associated with a storefront. Another issue we learned while developing is while we know that we need to handle authentication/credentials, we actually don&apos;t know <strong><em>how</em></strong> to handle authentication.</p><h2 id="server-side-implementation">Server Side Implementation</h2><p>In the server directory I added npm to the directory using &quot;npm init&quot; and completing the prompts. Once completed I installed the following npm packages:</p><ul><li><strong>Express</strong> for the server framework</li><li><strong>Sequelize</strong> for the Object&#x2013;relational mapping</li><li><strong>Mysql2</strong> for Sequelize to use MySQL</li><li><strong>CORS</strong> for the Cross-origin resource sharing</li><li><strong>Bcrypt</strong> for password-hashing</li><li><strong>Jsonwebtoken</strong> for Authentication/Credentials</li></ul><p>I made a new directory named &quot;src&quot; which is the source directory to store all the code I am developing for the server. Within the &quot;src&quot; directory I added an app.js file to serve as the main server file. This file is used to setup, start, and tell the server what port to listen to. </p><p>In the &quot;src&quot; directory I made three subdirectories; &quot;router&quot;, &quot;controller&quot;, &amp; &quot;models&quot;. The &quot;router&quot; directory defines the routes for the server. The &quot;controller&quot; directory defines the business logic that needs to be performed for a specific route. Lastly the &quot;models&quot; directory stores the sequelize connection and logic, as well as the model definition for the database tables.</p><p>In the merchant controller I salted and hashed the password using bcrypt before storing them in the database. This was because we don&apos;t want to store plain text passwords if someone where to gain access to our database. During register and login for the merchants we use JWT for credentials. Currently we understood the need to set it up, but we are still working out the kinks for properly handling credentials and usage. </p><p>At the moment we have developed a simple CRUD application for the storefronts and products. I need to develop the necessary parts, once we solve the authentication/credentials issue, to update and delete merchants. At this time all  you can do is register and login as a merchant. </p><p>In app.js we had to use the CORS package so we can send data to our frontend app. We had an issue where when we would send data to our frontend we would get CORS issues, so we simply installed the package and enabled it. </p><h2 id="future-plans">Future Plans</h2><p>At the moment one of the simplest things we would like to address would be adding information to our README so others could use our app or modify it as necessary. Looking ahead we want still haven&apos;t completed our current scope for merchants, products, &amp; storefronts. In our next update we want to have solved the following:</p><ul><li>Authentication &amp; credentials</li><li>Products being associated with Storefronts</li><li>Updating &amp; Deleting Merchants</li></ul><p>Past that we still have develop &quot;customers&quot; and the necessary features for that, which will include: </p><ul><li>Completing transactions</li><li>Having an order history </li><li>User authentication. </li></ul><p>Some things that I would like to address is logging and error handling for the backend server. </p><p>If you would like to check out the code or us it for yourself: <a href="https://github.com/JesstinSwadley/vict-ecommerce?ref=jesstin.com" rel="noreferrer">Github Link</a></p>]]></content:encoded></item><item><title><![CDATA[Launching Your Own Personal Website - Setup Guide]]></title><description><![CDATA[<p>Have you ever wondered about starting your own website? Want to make your own blog, personal website, or even if you are bold enough your own store? Want to have some control over your online presence?&#xA0;</p><p>This post is meant to be a simple guide for you to reference</p>]]></description><link>https://www.jesstin.com/launching-your-own-personal-website-setup-guide/</link><guid isPermaLink="false">65b192bf7363e704d1276514</guid><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Fri, 09 Feb 2024 18:00:42 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1461749280684-dccba630e2f6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fHdlYnNpdGV8ZW58MHx8fHwxNzA3NzkyMjU2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1461749280684-dccba630e2f6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDV8fHdlYnNpdGV8ZW58MHx8fHwxNzA3NzkyMjU2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Launching Your Own Personal Website - Setup Guide"><p>Have you ever wondered about starting your own website? Want to make your own blog, personal website, or even if you are bold enough your own store? Want to have some control over your online presence?&#xA0;</p><p>This post is meant to be a simple guide for you to reference in order to setup your own website.</p><p>In my previous post, I explained, even briefly, about the technology:&#xA0;<a href="https://www.jesstin.com/launching-your-own-personal-website-websites-101/" rel="noreferrer">Websites 101</a> </p><h2 id="reasons-to-have-your-own-website">Reasons to have your own website</h2><p>There are many reasons to start your own website. I personally started my website for a few reasons. One of the reasons I wanted to do this, is to build my credibility in order to improve my professional life &amp; prospects. Another reason I wanted to do this is to improve my writing &amp; communication skills.&#xA0;</p><h3 id="common-reasons-to-start-a-website">Common reasons to start a website:</h3><ol><li>It helps you build credibility</li><li>It helps you develop your own ideas</li><li>It helps improve your writing and communication skills</li><li>It helps your professional life - People will search you up, this could be something they find/ puts your best foot forward</li><li>It helps you develop connections (networking)</li><li>You expose yourself to new &amp; interesting opportunities</li><li>You can have a lot more impact on people, communities, &amp; causes</li><li>It helps you develop your own business &amp; brand</li></ol><p>You might start your own website for the reasons I provided or for one reason or another. The most important thing is that you know why&#xA0;<em>you</em>&#xA0;are starting a website.&#xA0;</p><h2 id="what-you-need-to-know">What You Need To Know</h2><p>The good news about setting up a website is that, businesses over the years have made the process of getting a website online incredibly easy. There are only a few things that you need in order to get your website up:- &#xA0;Domain Registrars- Web Hosting- Content Management Systems</p><p>Of the three things you should be paying for the domain of your website &amp; it&apos;s hosting.&#xA0;</p><h3 id="domain-registrar">Domain Registrar</h3><p>The domain registrars are businesses that sell domains to you. This is the name of your website that people enter in the URL. Some of the most common &amp; popular domain registrar are:&#xA0;</p><ol><li>GoDaddy</li><li>BlueHost</li><li>Namecheap</li><li>Hostgator</li></ol><h3 id="web-hosting">Web Hosting</h3><p>Web hosting is a service that allows you to host your website. Most if not all domain registrars now also provide web hosting services, but you can have a separate web hosting service from your domain registrar. Here are the common services for Web Hosting &amp; Cloud Hosting:</p><p><strong>Web Hosting</strong></p><ul><li>Shared Hosting</li><li>Virtual Private Server</li><li>Dedicated Hosting</li></ul><p><strong>Cloud Hosting</strong></p><ul><li>Amazon Web Services</li><li>Microsoft Azure</li><li>Google Cloud Platform</li></ul><h3 id="content-management-systems">Content Management Systems</h3><p>Unless you have a technical knowledge to deliver your own content, you should consider using a content management system in order to deliver your content. When it comes to your options for there are free and paid for options. The paid for options are also web hosting.</p><h4 id="free-open-source-software">Free Open Source Software</h4><ol><li><a href="https://wordpress.org/?ref=jesstin.com">WordPress</a></li><li><a href="https://www.drupal.org/?ref=jesstin.com">Drupal</a></li><li><a href="https://www.joomla.org/?ref=jesstin.com">Joomla</a></li><li><a href="https://ghost.org/?ref=jesstin.com">Ghost</a></li></ol><h4 id="paid-options">Paid-Options</h4><ol><li><a href="https://www.squarespace.com/?ref=jesstin.com">Squarespace</a></li><li><a href="https://www.wix.com/?ref=jesstin.com">Wix</a></li><li><a href="https://www.shopify.com/?ref=jesstin.com">Shopify</a></li><li><a href="https://www.bigcommerce.com/?ref=jesstin.com">BigCommerce</a></li></ol><h2 id="how-to-set-up-a-website">How To Set Up A Website</h2><p>I am going to keep this setup simple, we are going with a domain registrar that also has web hosting services.</p><h3 id="1-figure-out-why-you-want-to-host-a-website">1. Figure out why you want to host a website</h3><p>This will be personal to you &amp; why you want to start a website, but having it written out somewhere either publicly or privately will do you good for you.</p><h3 id="2-go-to-bluehost">2. Go to&#xA0;<a href="https://www.bluehost.com/special/wordpress?utm_source=google&amp;utm_medium=genericsearch&amp;gclid=EAIaIQobChMIw4CDm8vT9QIVdzytBh0D0wU3EAAYBCAAEgIaRPD_BwE&amp;gclsrc=aw.ds&amp;utm_campaign=affiliate-link_searchgenericpromo_PPC">Bluehost</a></h3><figure class="kg-card kg-image-card"><img src="https://jesstin.com/content/images/2022/01/bluehost.png" class="kg-image" alt="Launching Your Own Personal Website - Setup Guide" loading="lazy"></figure><p>While I personally don&apos;t have my website on Bluehost, I would recommend it for someone starting out since the pricing isn&apos;t hefty and they are also a domain registrar.&#xA0;</p><h3 id="3-choose-a-wordpress-plan">3. Choose a WordPress plan</h3><figure class="kg-card kg-image-card"><img src="https://jesstin.com/content/images/2022/01/bluehost_wordpress.png" class="kg-image" alt="Launching Your Own Personal Website - Setup Guide" loading="lazy"></figure><p>To make your process simple and have a content management system to include with your plan is to go with a WordPress plan. If you are unsure or strapped for cash go for the basic plan, if you got some more to give go for the &quot;Choice Plus&quot; plan.&#xA0;</p><h3 id="4-pick-your-domain-name">4. Pick your domain name</h3><figure class="kg-card kg-image-card"><img src="https://jesstin.com/content/images/2022/01/bluehost_domain.png" class="kg-image" alt="Launching Your Own Personal Website - Setup Guide" loading="lazy"></figure><p>Your domain address is what people type into the search bar to get to your website. Enter the domain you want to go with, the domain might get taken it and Bluehost will say that the domain is not available. Keep trying until you find a domain that works for you. Here are some simple tips for your domain:</p><ul><li>Don&apos;t make it complicated</li><li>Make it something people will remember</li><li>Try your best to get it to end with .com</li></ul><h3 id="5-enter-your-account-information">5. Enter your account information</h3><figure class="kg-card kg-image-card"><img src="https://jesstin.com/content/images/2022/01/bluehost_accounts.png" class="kg-image" alt="Launching Your Own Personal Website - Setup Guide" loading="lazy"></figure><p>Once you found your domain you will you setup your account. Go through the account setup process for Bluehost. After this you will get to design your website.</p><h3 id="6-design-your-website">6. Design your Website</h3><p>Since this a WordPress site you are going to need to do a few things to make this site your own.&#xA0;</p><ul><li><strong>Choose a Theme</strong></li></ul><p>Thousands upon thousands of themes are available, ranging from absolutely free to upwards of hundreds of dollars. When selecting your theme, try to ensure that it makes sense for both the topic of your site and the layout and content you plan to publish.</p><p>Themes are the main guidelines for your websites appearance.&#xA0;</p><ul><li><strong>Install Plugins</strong></li></ul><p>A WordPress plugin is a piece of software that &#x201C;plugs into&#x201D; your WordPress site. Plugins can add new functionality or extend existing functionality on your site, allowing you to create virtually any kind of website, from ecommerce stores to portfolios to directory sites.</p><p>Plugins can make small tweaks on your site or massive changes, depending on their features. Add the plugins that best fit the kind of website your are going for.</p><ul><li><strong>Customize Your Theme/Site</strong></li></ul><p>Now that you have your theme for your appearance &amp; plugins for your functions it&apos;s now time to make your site personal. You will want to change the text, images, colors, forms, &amp; pages. This may seem like a lot, but once you do this it will be the website that you want to do.</p><p>GREAT! You have your own website now!&#xA0;</p><p>From here on out you still need to do things to bring traffic to your website such as SEO. You will also need to keep your website up to date as much as possible.&#xA0;</p><p>If this blog was helpful to you please consider subscribing</p>]]></content:encoded></item><item><title><![CDATA[Launching Your Own Personal Website - Websites 101]]></title><description><![CDATA[<p>Step-by-Step Guide to setting up your website from domain purchase to website launching:&#xA0;<a href="https://jesstin.com/launching-your-own-personal-website-setup-guide/?ref=jesstin.com">https://jesstin.com/launching-your-own-personal-website-setup-guide/</a></p><h2 id="what-you-need-to-know-to-get-started">What You Need To Know To Get Started</h2><p>As more &amp; more of our lives shift to a digital medium the need to have a digital presence is growing. While most individuals</p>]]></description><link>https://www.jesstin.com/launching-your-own-personal-website-websites-101/</link><guid isPermaLink="false">65b18de47363e704d12764ec</guid><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Fri, 02 Feb 2024 18:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1467232004584-a241de8bcf5d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE1fHx3ZWJzaXRlfGVufDB8fHx8MTcwNjEzNTYxOHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1467232004584-a241de8bcf5d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE1fHx3ZWJzaXRlfGVufDB8fHx8MTcwNjEzNTYxOHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Launching Your Own Personal Website - Websites 101"><p>Step-by-Step Guide to setting up your website from domain purchase to website launching:&#xA0;<a href="https://jesstin.com/launching-your-own-personal-website-setup-guide/?ref=jesstin.com">https://jesstin.com/launching-your-own-personal-website-setup-guide/</a></p><h2 id="what-you-need-to-know-to-get-started">What You Need To Know To Get Started</h2><p>As more &amp; more of our lives shift to a digital medium the need to have a digital presence is growing. While most individuals will go with a much simpler setup of creating social media accounts on sites such as Facebook, Instagram, Twitter, LinkedIn, etc. One of the things to stand out amongst the crowd in this case is to have your own website.&#xA0;</p><p>Having recently setup &amp; launched my own website, I thought about sharing what I have learned both about the technology itself and the process of launching. In this blog I will be going over the knowledge &amp; technology that go into your website and the wider internet. In a later blog I will go over a proper step-by-step guide in you are interested.</p><p>Here is a quick rundown of the basics that go into how your website operates &amp; why you might want to start your own website:&#xA0;</p><h3 id="domain-overview">Domain Overview</h3><ul>
<li>Domain Name System
<ul>
<li>Domain Registrar</li>
<li>Nameservers</li>
<li>DNS Records</li>
</ul>
</li>
<li>Web Servers</li>
<li>Content Management Systems</li>
</ul>
<p>With that out of the way let&apos;s explore what happens when you make a request, to let&apos;s say&#xA0;<a href="https://www.facebook.com/">https://www.facebook.com</a>. Your computer sends a request to your ISP DNS server if it knows where the website is it will respond to your computer with the IP address of the website. If the server doesn&apos;t know where the website is it will make requests to other DNS servers till the website&apos;s records are located, then it will respond to your computer with the IP address of the website.</p><h2 id="domain-name-system">Domain Name System</h2><p>Now that we know who maintains the protocol that your devices use to access the internet, let&apos;s learn a little about who sells domains.&#xA0;</p><p>For starters you won&apos;t be buying a domain from ICANN or IANA, instead you will be being from an accredited business that IANA recognizes known as a&#xA0;<strong>Domain registrar</strong>. While there are other types of sellers that vast majority of people will be getting their domains from a registrar. To clarify something though, you don&apos;t technically &apos;own&apos; the domain, domains are reserved for you for a period of time &amp; domain registrars hold them for you.&#xA0;</p><p>Now that you have a domain it&apos;s time to get your domain attached to servers. In order to do that you need to have a server that keeps the records of your domain, that server is a Nameserver</p><h3 id="nameservers">Nameservers</h3><p>While domains are the name of a website, a nameserver is the servers that determine the direction that website traffic needs to go to. If you want an analogy; where domains are the location, nameservers are the directions.&#xA0;</p><p>When you purchase a domain, Domain Registrars come with their own nameservers already setup for you. They make it easy for you to modify the records of your nameserver to setup your website how you would like. It is possible to setup your nameserver outside of your domain registrar if you want. For example, I have my nameservers with my web hosting currently as an example.&#xA0;</p><p>There are a multitude of records that configure not only where the traffic goes, but also more specifically what kind of traffic goes where. A nameservers records are known as DNS Records.</p><h3 id="dns-records">DNS Records</h3><p>DNS records hold the information about which IP addresses match which domains. Their several types of records you will enter, each one requiring different information in order for them to work. Here are the most common to use for most people:&#xA0;</p><ul><li>Address Mapping record (A Record)&#x2014;also known as a DNS host record, stores a hostname and its corresponding IPv4 address.</li><li>IP Version 6 Address record (AAAA Record)&#x2014;stores a hostname and its corresponding IPv6 address.</li><li>Canonical Name record (CNAME Record)&#x2014;can be used to alias a hostname to another hostname. When a DNS client requests a record that contains a CNAME, which points to another hostname, the DNS resolution process is repeated with the new hostname.</li><li>Mail exchanger record (MX Record)&#x2014;specifies an SMTP email server for the domain, used to route outgoing emails to an email server.</li><li>Text Record (TXT Record)&#x2014;typically carries machine-readable data such as opportunistic encryption, sender policy framework, DKIM, DMARC, etc.</li></ul><h2 id="web-hosting-servers">Web Hosting Servers</h2><p>You got your domain, you know how DNS works so how do we get a website?</p><p>Servers are simply programs/devices that provide a service that another computer can access. A singular physical server might have multiple program servers on it. To get a website you need to allocate resources, their are services out their that do the allocation for you or you can host it yourself at your place of business. Here is a list of how you can host your website:</p><ol><li>On Premise<ol><li>Small to Medium Business Servers</li><li>Data Warehouses</li></ol></li><li>Web Hosting<ol><li>Shared Hosting</li><li>Virtual Private Server</li><li>Dedicated Hosting</li></ol></li><li>Cloud Hosting<ol><li>Amazon Web Services</li><li>Microsoft Azure</li><li>Google Cloud Platform</li></ol></li></ol><p>For the vast majority of people they will go with getting the allocation done for them. This means they will mostly go with the second option and go from there. If you are a little more confident in you in your technical skills and don&apos;t mind a little grunt work you can go with a Cloud Hosting/Computing option like I did with Amazon Web Services.&#xA0;</p><p>To connect your web hosting with your domain you will provide the relevant records to your nameserver.</p><h2 id="content-management-systems">Content Management Systems</h2><p>Your almost done, while having a website is nice, unless you want to build your website from the ground up &amp; how it handles content management, it might be simpler to go with something that already exists.&#xA0;</p><p>Content Management System or&#xA0;<strong>CMS</strong>&#xA0;is software that helps users create, manage, &amp; modify the content on a website without the need for specialized technical knowledge. Instead of building your own system for creating web pages, storing images, and other functions, the content management system handles all that basic infrastructure stuff for you so that you can focus on more forward-facing parts of your website.</p><p>There are free options and paid for options for CMS:</p><h4 id="free-open-source-software">Free Open Source Software</h4>
<ol>
<li><a href="https://wordpress.org/?ref=jesstin.com">WordPress</a></li>
<li><a href="https://www.drupal.org/?ref=jesstin.com">Drupal</a></li>
<li><a href="https://www.joomla.org/?ref=jesstin.com">Joomla</a></li>
<li><a href="https://ghost.org/?ref=jesstin.com">Ghost</a></li>
</ol>
<h4 id="paid-options">Paid-Options</h4>
<ol>
<li><a href="https://www.squarespace.com/?ref=jesstin.com">Squarespace</a></li>
<li><a href="https://www.wix.com/?ref=jesstin.com">Wix</a></li>
<li><a href="https://www.shopify.com/?ref=jesstin.com">Shopify</a></li>
<li><a href="https://www.bigcommerce.com/?ref=jesstin.com">BigCommerce</a></li>
</ol>
<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x2757;</div><div class="kg-callout-text">Beware that some of the free options also have paid for options.For example WordPress &amp; Ghost</div></div><p>Here is how my website is setup if you are curious:</p><p><strong>Domain Registrar:&#xA0;</strong>Namecheap<br><strong>Web Hosting:</strong>&#xA0;Amazon Web Services (AWS)<br><strong>Content Management System:</strong>&#xA0;Ghost</p><hr><p>I know that this was a lot to take in &amp; you probably still have questions, but this was mostly meant to be a quick overview. I also didn&apos;t go over&#xA0;<strong>how</strong>&#xA0;to set it up, this was meant to clarify&#xA0;<strong>what</strong>&#xA0;is happening from a high-level overview when you are setting it up.&#xA0;</p><p>Step-by-Step Guide to setting up your website from domain purchase to website launching:&#xA0;Step-by-Step Guide [LINK]</p><p>If this blog was helpful to you please consider subscribing</p>]]></content:encoded></item><item><title><![CDATA[Launching a Ghost Blog with AWS Lightsail]]></title><description><![CDATA[<p>Over the last year I have spent time here and there dabbling with AWS, so I with the new year I decided to make a more concerted effort with not only AWS, but my journey into the tech industry.</p><p>With my AWS free tier reaching the end of its duration</p>]]></description><link>https://www.jesstin.com/launching-a-ghost-blog-with-aws-lightsail/</link><guid isPermaLink="false">65b08d377363e704d12764dc</guid><category><![CDATA[technology]]></category><dc:creator><![CDATA[Jesstin Swadley]]></dc:creator><pubDate>Fri, 26 Jan 2024 18:00:40 GMT</pubDate><content:encoded><![CDATA[<p>Over the last year I have spent time here and there dabbling with AWS, so I with the new year I decided to make a more concerted effort with not only AWS, but my journey into the tech industry.</p><p>With my AWS free tier reaching the end of its duration of 1 year, I needed to make a decision when it came to choosing a compute option for my own website, how and what to use not only within AWS, but as my CMS (Content Management System). Starting with the available compute options within AWS there&apos;s EC2, Lambda, ECS/EKS, &amp; Lightsail.</p><p>Of the options I chose to go with Lightsail. The issues with other are the following:  ECS/EKS while a useful tool for containerized workloads would be overly complex for what is simply a personal site/blog. The issue with Lambda, would be I either have to do extensive workarounds to get Ghost to work or go with a different CMS entirely. EC2 while free currently would become more expensive than Lightsail once the free tier is done. </p><p>As for the reason I decided to go with Ghost as my CMS, the reasons are actually much simpler. AWS Lightsail comes with a few preconfigured images/snapshots already available so the one I decided to go with was Ghost. </p><p>Moving onto the steps necessary to launch, configure, &amp; display your instance not only online, but with your domain. </p><h2 id="1-launch-an-instance-on-aws-lightsail">1. Launch an Instance on AWS Lightsail</h2><p>Once you log into are in the AWS Console go to the navigate to the compute option and choose Lightsail. If you are unable to find it you can use the search console to find Lightsail at the top.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-24-at-8.35.10-PM.png" class="kg-image" alt loading="lazy" width="1940" height="1764" srcset="https://www.jesstin.com/content/images/size/w600/2024/01/Screenshot-2024-01-24-at-8.35.10-PM.png 600w, https://www.jesstin.com/content/images/size/w1000/2024/01/Screenshot-2024-01-24-at-8.35.10-PM.png 1000w, https://www.jesstin.com/content/images/size/w1600/2024/01/Screenshot-2024-01-24-at-8.35.10-PM.png 1600w, https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-24-at-8.35.10-PM.png 1940w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Where to find Lightsail in the AWS Console</span></figcaption></figure><p>Once you click on Lightsail it will open a new tab and take you to the Amazon Lightsail console. If you haven&apos;t created an instance before, Lightsail will have a screen to create your first instance, if you have created an instance you will have to navigate to &quot;Create instance&quot; button. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-24-at-8.28.16-PM.png" class="kg-image" alt loading="lazy" width="2000" height="369" srcset="https://www.jesstin.com/content/images/size/w600/2024/01/Screenshot-2024-01-24-at-8.28.16-PM.png 600w, https://www.jesstin.com/content/images/size/w1000/2024/01/Screenshot-2024-01-24-at-8.28.16-PM.png 1000w, https://www.jesstin.com/content/images/size/w1600/2024/01/Screenshot-2024-01-24-at-8.28.16-PM.png 1600w, https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-24-at-8.28.16-PM.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Where to click to create instance</span></figcaption></figure><p>In the &quot;Create an instance&quot; console you will be given the following options</p><ul><li>Instance Location</li><li>Instance Image<ul><li>Select Platform/OS</li><li>Select a blueprint</li></ul></li><li>Optional<ul><li>Add a launch script</li><li>Change SSH Key Pair</li></ul></li><li>Instance Plan<ul><li>Network Plan</li><li>Instance Size</li></ul></li><li>Identify you Instance<ul><li>Unique Name</li><li>Key Only Tags</li><li>Key-value Tags</li></ul></li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-28-at-9.20.01-AM.png" class="kg-image" alt loading="lazy" width="2000" height="907" srcset="https://www.jesstin.com/content/images/size/w600/2024/01/Screenshot-2024-01-28-at-9.20.01-AM.png 600w, https://www.jesstin.com/content/images/size/w1000/2024/01/Screenshot-2024-01-28-at-9.20.01-AM.png 1000w, https://www.jesstin.com/content/images/size/w1600/2024/01/Screenshot-2024-01-28-at-9.20.01-AM.png 1600w, https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-28-at-9.20.01-AM.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Options for &quot;Create an Instance&quot; in Lightsail</span></figcaption></figure><p>For this site we will going with the following options</p><ol><li>Instance Location: Virginia</li><li>Select a platform: Linux/Unix</li><li>Select a blueprint: Ghost</li><li>Select a size: $5/$10 option</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-28-at-10.16.46-AM.png" class="kg-image" alt loading="lazy" width="2000" height="1156" srcset="https://www.jesstin.com/content/images/size/w600/2024/01/Screenshot-2024-01-28-at-10.16.46-AM.png 600w, https://www.jesstin.com/content/images/size/w1000/2024/01/Screenshot-2024-01-28-at-10.16.46-AM.png 1000w, https://www.jesstin.com/content/images/size/w1600/2024/01/Screenshot-2024-01-28-at-10.16.46-AM.png 1600w, https://www.jesstin.com/content/images/size/w2400/2024/01/Screenshot-2024-01-28-at-10.16.46-AM.png 2400w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Instance plans in Lightsail</span></figcaption></figure><p>Once you have configured your instance how you like it, click on the &quot;Create Instance&quot; button at the bottom of the page. AWS Lightsail will now go through the process of spinning everything up based on your configuration. It should take a few minutes before everything is ready to go from here. </p><h2 id="2-get-the-default-password">2. Get the Default Password</h2><p>Now that our new instance has been created, one of the things it will do is generate a default password for our Ghost CMS. </p><p>First we are going to navigate to the instance that we just created. Once there, navigate to the connect option, if it doesn&apos;t default you there. The connect tab will provide you with a few options to connect to your instance, one is through the browser, the other is through SSH client/terminal. I decided to use the browser to make it simpler.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-28-at-1.13.43-PM.png" class="kg-image" alt loading="lazy" width="1978" height="832" srcset="https://www.jesstin.com/content/images/size/w600/2024/01/Screenshot-2024-01-28-at-1.13.43-PM.png 600w, https://www.jesstin.com/content/images/size/w1000/2024/01/Screenshot-2024-01-28-at-1.13.43-PM.png 1000w, https://www.jesstin.com/content/images/size/w1600/2024/01/Screenshot-2024-01-28-at-1.13.43-PM.png 1600w, https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-28-at-1.13.43-PM.png 1978w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Connect options for Lightsail Instances</span></figcaption></figure><p>Simply click on the &quot;Connect using SSH&quot; button and a terminal will popup in another browser. Once inside the instance enter the following command:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">cat $HOME/bitnami_application_password</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to collect the default password </span></p></figcaption></figure><p>With the password collected, put it somewhere to copy-and-paste it later such as a txt file.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-28-at-1.19.41-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1461" srcset="https://www.jesstin.com/content/images/size/w600/2024/01/Screenshot-2024-01-28-at-1.19.41-PM.png 600w, https://www.jesstin.com/content/images/size/w1000/2024/01/Screenshot-2024-01-28-at-1.19.41-PM.png 1000w, https://www.jesstin.com/content/images/size/w1600/2024/01/Screenshot-2024-01-28-at-1.19.41-PM.png 1600w, https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-28-at-1.19.41-PM.png 2018w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Browser Terminal with password command</span></figcaption></figure><h2 id="3-create-a-static-ip-for-the-instance">3. Create a Static IP for the Instance</h2><p>The next thing to do is add a static IP to instance. The reason for this is every time you stop and start the instance a new public IP is assigned to the instance. Later when we connect the domain to the instances we will use the static IP so we don&apos;t have to keep updating the DNS records. </p><p>You can create a static IP in two locations within the instance or the networking tab on the left. I choose to do it through the instance, to immediately attach the static IP to the instance, rather than go through the process of creating, then attaching the static IP. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-28-at-3.13.52-PM.png" class="kg-image" alt loading="lazy" width="1948" height="986" srcset="https://www.jesstin.com/content/images/size/w600/2024/01/Screenshot-2024-01-28-at-3.13.52-PM.png 600w, https://www.jesstin.com/content/images/size/w1000/2024/01/Screenshot-2024-01-28-at-3.13.52-PM.png 1000w, https://www.jesstin.com/content/images/size/w1600/2024/01/Screenshot-2024-01-28-at-3.13.52-PM.png 1600w, https://www.jesstin.com/content/images/2024/01/Screenshot-2024-01-28-at-3.13.52-PM.png 1948w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Where to create &amp; attach the static IP</span></figcaption></figure><h2 id="4-connect-static-ip-to-the-instance">4. Connect Static IP to the Instance</h2><p>Once the static IP is created you can find it where the public IP used to be. Make note or copy-and-paste it. If you haven&apos;t closed the browser terminal head back over to it, but if you did close it go back and open a new terminal. Enter the following command in the terminal.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">sudo /opt/bitnami/configure_app_domain --domain &lt;StaticIP&gt;</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to let instance know to monitor traffic for Static IPs</span></p></figcaption></figure><p>With the above bash command your instance should now aware of the static IP. </p><h2 id="5-login-to-ghost">5. Login to Ghost</h2><p>We&apos;re now going to make sure that we have Ghost installed, and our instance has updated to the static IP. Using your static IP go to the following URL</p><blockquote>http://&lt;Static IP&gt;/ghost</blockquote><p>Use the following to gain access to the Admin Dashboard:</p><blockquote>Username: user@example.com<br>Password: [Default Password]</blockquote><p>From here you can either customize your Ghost site how you see fit or you can continue with the setup.</p><h2 id="6-update-dns-records">6. Update DNS Records</h2><p>So far we have launched two AWS Lightsail resources, gained access to our AWS Lightsail server, modified the server, and gained access to our Ghost CMS. While we can direct traffic with an IP Address, most people will struggle to remember how to get to your site so let&apos;s connect a domain to our site. </p><p>AWS recommends that you transfer DNS Management to Lightsail if you are going to be working Lightsail, however I will be keeping my domain separate from AWS Lightsail, so I will add the records necessary to connect the instance to the domain.</p><p>As I am using this blog for my domain name (Secondary-Level Domain + Top-Level Domain) I will add two records to my DNS to make this work.</p><ul><li>Record Name: @/example.com</li><li>Record Type: A</li><li>Value: &lt;AWS Lightsail Static IP&gt;</li><li>TTL: 300</li></ul><p>The record name I used denotes to use either the domain itself or the origin of the record. The record type &quot;A&quot; is the address record type. We set the value to our static IP and lastly set the TTL to 300 meaning a record persists for 300 seconds. </p><h2 id="7-connect-instance-with-domain">7. Connect Instance with Domain</h2><p>With the DNS records added we will now connect our instance with our new domain. Connect to the instance through SSH, once your are connected we will be doing something similar, if not exactly the same as what we did in Step 3 for Static IPs</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">sudo /opt/bitnami/configure_app_domain --domain &lt;DomainName&gt;</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to let instance know to monitor traffic for Domains</span></p></figcaption></figure><h2 id="8-encrypt-traffic-for-https">8. Encrypt traffic for HTTPS</h2><p>The issue with our current configuration is we are using HTTP instead of HTTPS, due to not encrypting the traffic from our server. Since this is a Bitnami Image we will be using the&#xA0;Bitnami HTTPS Configuration Tool (<code>bncert-tool</code>). Enter the following command: </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">sudo /opt/bitnami/bncert-tool</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to begin the configuration for Bitnami HTTPS Configuration Tool</span></p></figcaption></figure><p>Once the tool begins enter the primary and secondary domain you want to use. Make sure that you have a space to separate the domains. You can you use either domains top level domains or subdomains. Examples of domains being: example.com, www.example.com or blog.example.com, www.blog.example.com</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/02/bncert-domain-names.png" class="kg-image" alt loading="lazy" width="754" height="163" srcset="https://www.jesstin.com/content/images/size/w600/2024/02/bncert-domain-names.png 600w, https://www.jesstin.com/content/images/2024/02/bncert-domain-names.png 754w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Add Domains to your Bitnami HTTPS Configuration Tool</span></figcaption></figure><p>We will now handle the traffic redirection for the site we want to:</p><ul><li> Confirm HTTP to HTTPS redirection</li><li>Confirm non-www to www redirection</li><li>Reject www to non-ww redirection</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/02/bncert-enable-disable-redirection.png" class="kg-image" alt loading="lazy" width="742" height="296" srcset="https://www.jesstin.com/content/images/size/w600/2024/02/bncert-enable-disable-redirection.png 600w, https://www.jesstin.com/content/images/2024/02/bncert-enable-disable-redirection.png 742w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Traffic redirection</span></figcaption></figure><p>Enter the email to associate for the Let&apos;s Encrypt Certificate, and confirm that you agree to the Let&apos;s Encrypt Subscribe Agreement</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/02/bncert-email-address.png" class="kg-image" alt loading="lazy" width="743" height="172" srcset="https://www.jesstin.com/content/images/size/w600/2024/02/bncert-email-address.png 600w, https://www.jesstin.com/content/images/2024/02/bncert-email-address.png 743w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Email for Let&apos;s Encrypt Certificate</span></figcaption></figure><p>It should issue and validate the certificate for you allow HTTPS traffic for your site.</p><h2 id="9-restart-ghost-apache">9. Restart Ghost &amp; Apache</h2><p>One of the things I had to do in order for the HTTPS traffic redirect to work was to restart both Ghost &amp; Apache. Enter the following commands in order to restart the services.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">sudo /opt/bitnami/ctlscript.sh restart ghost
sudo /opt/bitnami/ctlscript.sh restart apache</code></pre><figcaption><p><span style="white-space: pre-wrap;">Commands to restart both the Ghost &amp; Apache Service</span></p></figcaption></figure><p>Unfortunately, this might cause an issue with Ghost restarting, but it stating if we were to check that the service isn&apos;t running. In order to fix that problem I recommend the next step which is restarting the instance completely. </p><h2 id="10-restart-instance">10. Restart Instance</h2><p>You can either restart the instance in the CLI/SSH or using the AWS console. I prefer to use the AWS Console as when you use the CLI you disconnect from the instance and it will take a few minutes for everything to be up and running again. </p><p>You can find the &quot;Reboot&quot; button in the upper right of the instance. Simply click on it and reboot the instance.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.jesstin.com/content/images/2024/02/Screenshot-2024-02-08-at-6.13.24-PM.png" class="kg-image" alt loading="lazy" width="2000" height="502" srcset="https://www.jesstin.com/content/images/size/w600/2024/02/Screenshot-2024-02-08-at-6.13.24-PM.png 600w, https://www.jesstin.com/content/images/size/w1000/2024/02/Screenshot-2024-02-08-at-6.13.24-PM.png 1000w, https://www.jesstin.com/content/images/size/w1600/2024/02/Screenshot-2024-02-08-at-6.13.24-PM.png 1600w, https://www.jesstin.com/content/images/2024/02/Screenshot-2024-02-08-at-6.13.24-PM.png 2086w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Location for Reboot Button</span></figcaption></figure><h2 id="11-login-to-ghost-using-the-domain">11. Login to Ghost using the Domain</h2><p>Having just changed from HTTP with Static IP to HTTPS with Domain we want to login into Ghost again and make sure that:</p><ol><li>Ghost is running correctly</li><li>HTTPS traffic is used</li></ol><p>Simply go to:</p><blockquote>http://&lt;Domain&gt;/ghost</blockquote><p>Enter the default credentials if you haven&apos;t updated them yet, or enter your updated credentials if you have changed the.</p><h2 id="12-create-a-snapshot-of-the-instance">12. Create a snapshot of the instance</h2><p>Lastly, as a precaution we want to make a snapshot of instance. Navigate to &quot;Snapshot&quot; within your instance. Once there click on &quot;Create snapshot&quot; to manually create a snapshot instance. With that finished you now have a new Ghost site launched, connected to a domain, and traffic encrypted for your site. </p><p></p>]]></content:encoded></item></channel></rss>