<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Femi Akinyemi</title>
    <description>The latest articles on DEV Community by Femi Akinyemi (@femi_akinyemi).</description>
    <link>https://dev.to/femi_akinyemi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F363773%2Ff660be1b-b50b-44cb-b4ff-cdeba2da79ca.jpg</url>
      <title>DEV Community: Femi Akinyemi</title>
      <link>https://dev.to/femi_akinyemi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/femi_akinyemi"/>
    <language>en</language>
    <item>
      <title>Why PageSpeed says reduce unused JavaScript</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Sat, 10 Oct 2026 10:51:36 +0000</pubDate>
      <link>https://dev.to/femi_akinyemi/why-pagespeed-says-reduce-unused-javascript-39kf</link>
      <guid>https://dev.to/femi_akinyemi/why-pagespeed-says-reduce-unused-javascript-39kf</guid>
      <description>&lt;p&gt;PageSpeed watched the page open. It listed scripts the browser downloaded and barely ran. That is the whole warning. Here is how to read the list without breaking the page.&lt;/p&gt;

&lt;p&gt;You already know the line. Reduce unused JavaScript. Under it, a stack of file addresses, and a size next to each one. The top file is the largest. It is easy to read that as “delete these.” That reading is how pages get broken.&lt;/p&gt;

&lt;p&gt;The line is still worth taking seriously. A phone on a slow connection has to fetch every one of those files before it can get on with the picture, the words, and the button the person came for. Code that never runs is still a download.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the line actually measured
&lt;/h2&gt;

&lt;p&gt;PageSpeed Insights loads one address, in a lab, the way a first visit might look on a phone. While that load is happening it keeps a record of which script code actually ran. Code that was downloaded and did not run on that pass is marked unused.&lt;/p&gt;

&lt;p&gt;That record stops when the page has settled. It does not sit there and use the site the way a person would. It does not open a menu, send a form, start a chat, or scroll to a map. Anything that waits for those moments can look unused and still be the reason the page works.&lt;/p&gt;

&lt;p&gt;So “unused” here means “not run during this one load.” It does not mean “safe to throw away,” and it does not mean “this file is why the site feels slow.” It means the browser fetched code it did not need yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a file that never ran still costs the visit
&lt;/h2&gt;

&lt;p&gt;The wait happens before the code runs. The phone has to ask for the file, receive it, and read it. A large script takes time on a weak connection even if most of the code sits idle after that. While that transfer is going on, the picture the person is waiting for is sharing the same connection.&lt;/p&gt;

&lt;p&gt;This is why a chat box you never opened can still show up on the list, and why the page can feel late before anyone taps anything. The file arrived. The feature did not. The visitor paid for both.&lt;/p&gt;

&lt;p&gt;A small unused file is rarely the story. PageSpeed puts the largest waste at the top for that reason. A tiny helper is not the same problem as a script hundreds of kilobytes long that came along for one button.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three kinds of files on the list
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Yours, and this page never needs it.&lt;/strong&gt; A slider script on a page with no slider. A booking script on a page with no booking. A theme file that loads on every page because that was easier than deciding. These are the ones worth removing from this page. The rest of the site can keep them where they are actually used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yours, and a person needs it a moment later.&lt;/strong&gt; The menu, the search box, the form, the gallery that opens on tap. PageSpeed did not tap. The file looks idle. Take it off the page and the first real visitor finds a dead control. If the file is huge, the useful question is whether that control can load when the person asks for it, not whether the control should disappear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Someone else’s file.&lt;/strong&gt; Ads, chat, a tag that loads other tags, a map, a review widget. The address is on their server. You cannot edit the file. You can take their tool off your page, or you can keep it because the tool is doing a job you want. Those are the only two honest choices. A speed plugin that claims to “fix unused JavaScript” often just delays these files. The download still happens. It happens a little later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to leave alone
&lt;/h2&gt;

&lt;p&gt;Do not clear the list in one afternoon. I have watched people turn plugins off in a pile, run PageSpeed again, and treat whatever number comes back as proof. The score also moves when nothing changed. A real cut can look small on the grade, and a lucky second run can look like a victory. One file, then another look. I wrote about that wobble in &lt;a href="https://pafcore.site/why-your-lighthouse-score-changes" rel="noopener noreferrer"&gt;Why your Lighthouse score changes when you did nothing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Do not treat the homepage as the whole site. The homepage can be quiet while the page people actually use, a product, a booking form, a long post, pulls a much larger script. Test the address that felt slow.&lt;/p&gt;

&lt;p&gt;And do not assume this line is the main wait. A huge picture, or a server that is slow to answer, can be what people feel while this list sits underneath it. On a WordPress site those show up together, because themes and plugins are happy to add scripts to every page. I wrote that up in &lt;a href="https://pafcore.site/what-usually-slows-a-wordpress-site" rel="noopener noreferrer"&gt;What usually slows a WordPress site&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to read one row
&lt;/h2&gt;

&lt;p&gt;Take the top row only.&lt;/p&gt;

&lt;p&gt;Read the address. If you recognise the name, a plugin, a theme, a tool you installed, you know who put it there. If the name is a hash of letters and you do not recognise it, it is often a file the site built, or a tag. The folder in the address usually gives it away: a plugin folder, a theme folder, or another company’s domain.&lt;/p&gt;

&lt;p&gt;Then ask one question. Does this page need this file before anyone taps? If the answer is no, that file does not belong on this load. If the answer is yes, leave it and look at the next row. If the answer is “only after they tap,” the file can wait for the tap, but only if you can make that change without losing the control.&lt;/p&gt;

&lt;p&gt;Chrome can show the same thing in more detail. Open the page, open the Coverage panel, and reload. It colours the code that ran and the code that did not. Use that when you already know the file and you want to see how much of it was idle. You do not need it to decide which row to open first. The top row is enough for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;Stay on the page that felt slow. Open the top file if it is yours. Leave the rest of the list until you know whether that file was the wait. If the top file belongs to a tool you do not care about, remove the tool, not five other plugins.&lt;/p&gt;

&lt;p&gt;Then run the check again on the same address. Read the new score next to how far the score moves when you change nothing. If the file is gone from the list and the page still works when you use it, that was the right cut. If the score jumped and the page feels the same, you measured noise.&lt;/p&gt;

&lt;p&gt;A free check on &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;PafCore&lt;/a&gt; is that first look without the long list. Paste the public address. No account. You get the score, how far that score moves when nothing changed, the slow file when it can be named, and a few of the problems. Sign in for the full report, including what to try. PageSpeed remains a good snapshot of the warning. Use it for the list. Use a named file when the list is too long to trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://pafcore.site/#scan" rel="noopener noreferrer"&gt;Check my site&lt;/a&gt;. Paste a public address. Free check. No account.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why a website feels slow on a phone</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Sun, 27 Sep 2026 14:25:35 +0000</pubDate>
      <link>https://dev.to/femi_akinyemi/why-a-website-feels-slow-on-a-phone-1m1f</link>
      <guid>https://dev.to/femi_akinyemi/why-a-website-feels-slow-on-a-phone-1m1f</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5bithy0zelmle712wshr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5bithy0zelmle712wshr.png" alt="Same website: score 91 on a computer and 38 on a phone" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A website feels slow on a phone when the page asks a small screen and a weaker radio to do the same work your computer already finished. Large pictures. Busy scripts. Files the phone has not saved yet. You sit on fast wifi with a wide window. They sit on a phone. They feel a blank screen, a late picture, or taps that do nothing.&lt;/p&gt;

&lt;p&gt;This is common. It is not a mystery. You checked the site on your computer and it felt fine. A client or a friend opened it on a phone and said it was late. Both can be true.&lt;/p&gt;

&lt;p&gt;This piece is the plain version of that gap, and a free way to check the page the way a visitor does. &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;PafCore&lt;/a&gt; loads a public URL like a slow phone. When it can, the report names the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  What people feel on a phone
&lt;/h2&gt;

&lt;p&gt;They do not talk about scores. They talk about the wait.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The screen stays blank after they tap the link.&lt;/li&gt;
&lt;li&gt;The first picture shows up late.&lt;/li&gt;
&lt;li&gt;Buttons ignore the first tap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those moments decide if they stay. A number on a lab report can miss them. A score is still useful. It is not the whole story. I wrote about that in &lt;a href="https://pafcore.site/why-a-performance-score-is-not-enough" rel="noopener noreferrer"&gt;Why a performance score is not enough&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why your computer hides the wait
&lt;/h2&gt;

&lt;p&gt;Your computer is a kind visit. A phone is a harder one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The screen is smaller, the file is not.&lt;/strong&gt; A large picture can look sharp on your computer. On a phone it is often bigger than the space it fills. The phone still downloads the whole file. The visitor waits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The radio is weaker.&lt;/strong&gt; Home wifi is usually fast. A phone on mobile data is not. The same scripts and pictures take longer to arrive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You already had the files.&lt;/strong&gt; You open the site every day. Your computer saved pictures and scripts from last time. A new visitor starts from scratch. Their first load is the one that matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The page stays busy.&lt;/strong&gt; Chat widgets, booking tools, and tracking scripts keep the page working after it looks ready. On a phone that extra work shows up as taps that do nothing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep it honest: a phone is not always slower. A lean page can feel sharp on both. Guessing which file hurt wastes a day. Measure the page the way a visitor opens it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why the usual check misses it
&lt;/h2&gt;

&lt;p&gt;People test on the same computer they build on. Fast wifi. A wide window. Files already saved. The page feels fine. They stop.&lt;/p&gt;

&lt;p&gt;Google PageSpeed Insights is useful. It can show a phone view. Many people still paste once, glance at the number, and close the tab. A single run also wobbles. The number moves even when you changed nothing. See &lt;a href="https://pafcore.site/why-your-lighthouse-score-changes" rel="noopener noreferrer"&gt;Why your Lighthouse score changes when you did nothing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Home can look fine on a phone too. A product page or checkout can still wait. That is a different trap. I wrote it up in &lt;a href="https://pafcore.site/a-fast-homepage-can-still-hide-a-slow-site" rel="noopener noreferrer"&gt;A fast homepage can still hide a slow site&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a useful phone check does
&lt;/h2&gt;

&lt;p&gt;A useful check should do four simple jobs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load the page like a slow phone, not like your computer.&lt;/li&gt;
&lt;li&gt;Run it more than once so you see a middle score, not one lucky result.&lt;/li&gt;
&lt;li&gt;Say what the person felt: blank screen, late picture, taps ignored.&lt;/li&gt;
&lt;li&gt;Name the slow file when the measurement can find it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is what &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;PafCore&lt;/a&gt; is built for. Paste a public URL. You get a free report. No signup. Three runs. A middle score. The quiet gap. The file when we can see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to check a page today
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pick a real page. The home page, or a page people pay on.&lt;/li&gt;
&lt;li&gt;Open it once on your phone on mobile data. Notice the feel.&lt;/li&gt;
&lt;li&gt;Paste the full address at &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;pafcore.site&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Read the middle score first. Then open the top finding. If a file is named, start there.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use the live public URL. A page behind a login will not load the same way for a free public check. Start with what visitors already see.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you send sites to clients
&lt;/h2&gt;

&lt;p&gt;Clients live on a phone. You live on a computer. That is how arguments start. “It feels fine here.” “It feels late to me.”&lt;/p&gt;

&lt;p&gt;Send a report that names the feel and the file. Talk in plain words. What they saw. Which file hurt. What you will change next. Run one quiet check after the fix so you do not celebrate noise.&lt;/p&gt;

&lt;p&gt;If you manage many sites and need schedules, that is &lt;a href="https://pafcore.site/pro" rel="noopener noreferrer"&gt;Pro Watch&lt;/a&gt;. The free report stays free for a one off paste.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why is my website slow on a phone but fine on my computer?
&lt;/h3&gt;

&lt;p&gt;Your computer has a wide screen, fast wifi, and files already saved from the last visit. A phone has a smaller screen, a weaker radio, and often starts from scratch. The same large picture and busy scripts cost more there. People feel a blank screen, a late picture, or taps that do nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I test website speed on a phone?
&lt;/h3&gt;

&lt;p&gt;Yes. Most visitors use a phone. A check on your computer can hide the wait they feel. Use a phone on mobile data, or a report that loads the page like a slow phone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does a high score on my computer mean the site is fast?
&lt;/h3&gt;

&lt;p&gt;No. A high score on a computer only describes that visit. The same page can feel late on a phone. Check the page the way a visitor opens it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does PafCore test on a phone?
&lt;/h3&gt;

&lt;p&gt;PafCore loads the public page three times on a slow phone connection. You get a middle score, how far that score moves on its own, and the slow file when the report can name it. Free. No signup.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://pafcore.site/#scan" rel="noopener noreferrer"&gt;Check my site&lt;/a&gt;. Paste a public URL. Free report. No signup.&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>A fast homepage can still hide a slow site</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Fri, 25 Sep 2026 12:57:50 +0000</pubDate>
      <link>https://dev.to/femi_akinyemi/a-fast-homepage-can-still-hide-a-slow-site-32b4</link>
      <guid>https://dev.to/femi_akinyemi/a-fast-homepage-can-still-hide-a-slow-site-32b4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published at &lt;a href="https://pafcore.site/a-fast-homepage-can-still-hide-a-slow-site" rel="noopener noreferrer"&gt;pafcore.site/a-fast-homepage-can-still-hide-a-slow-site&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdyt85pzzjrgkfad4eboy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdyt85pzzjrgkfad4eboy.png" alt="A shop homepage score of 88 next to a checkout score of 41" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A homepage score is the speed score for the home URL only. It is not a score for the whole site. I have seen a shop home sit near 88 and checkout sit near 41. Same host. Same theme. Different files. People still share the home screenshot and call the job done.&lt;/p&gt;

&lt;p&gt;This piece is the plain version of that mistake, and a free way to check a real page with &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;PafCore&lt;/a&gt;. When we can, we name the slow file.&lt;/p&gt;

&lt;h2&gt;
  
  
  What people feel that you miss
&lt;/h2&gt;

&lt;p&gt;You live on the home page. Visitors do not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They tap a product and wait on a blank screen.&lt;/li&gt;
&lt;li&gt;They reach checkout and the first tap does nothing.&lt;/li&gt;
&lt;li&gt;They open a campaign page and the big picture arrives late.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Home can feel fine. The wait shows up one click later. A score on home will not warn you. I wrote about why a number on its own is thin in &lt;a href="https://pafcore.site/why-a-performance-score-is-not-enough" rel="noopener noreferrer"&gt;Why a performance score is not enough&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the usual tools stop at one URL
&lt;/h2&gt;

&lt;p&gt;Google PageSpeed Insights checks the address you paste. One address. Lighthouse does the same. That is useful. It is also easy to misread.&lt;/p&gt;

&lt;p&gt;People paste the home page because it is the one they remember. The report looks serious. The number is high enough. They stop. The rest of the site never gets a turn.&lt;/p&gt;

&lt;p&gt;Each page loads its own pictures and scripts. A chat widget on a help page. A big gallery on a product page. A payment script on checkout. Home never asked for those files. So home never paid for them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep it honest: a strong homepage score is still good news. It is not a pass for the site. Treat it as one page you measured, then measure the next one people use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Pages that hide the wait
&lt;/h2&gt;

&lt;p&gt;These are the ones I would open after home.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkout or a form.&lt;/strong&gt; Extra scripts. Extra fields. The page that should feel sharp often feels late.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A product page or a busy post.&lt;/strong&gt; Large pictures. Reviews. Related items. The template looks like home. The files do not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Search results.&lt;/strong&gt; A list that builds itself after the page opens. The first screen can sit empty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A campaign landing page.&lt;/strong&gt; Heavy video. Heavy animation. Built for a click, then forgotten in the speed check.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft19tuam9c6cni83591u5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft19tuam9c6cni83591u5.png" alt="Homepage score 88 beside checkout score 41" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Same site. Different wait. Start with the page that costs the most points.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What a useful check does
&lt;/h2&gt;

&lt;p&gt;A useful check does not stop at home.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run a page more than once so you see a middle score, not one lucky screenshot.&lt;/li&gt;
&lt;li&gt;Show how far the score moves on its own (the quiet gap).&lt;/li&gt;
&lt;li&gt;Name the slow file when the measurement can find it.&lt;/li&gt;
&lt;li&gt;Let you compare pages so the worst one is obvious.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is what &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;PafCore&lt;/a&gt; is built for. Paste a public URL. Free report. No signup. When we can see the cost, we name the file. When we cannot, we still tell you what to try first.&lt;/p&gt;

&lt;p&gt;You can also scan a whole site. Up to 20 pages. Worst first. That pack costs 20 credits and emails you when it finishes. Start with the free one page check if you have not used credits yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to check more than your homepage
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Paste the home URL at &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;pafcore.site&lt;/a&gt;. Note the middle score.&lt;/li&gt;
&lt;li&gt;Open a page people actually use. Checkout. A product. A form. A busy post.&lt;/li&gt;
&lt;li&gt;Paste that full address. Wait for the middle score.&lt;/li&gt;
&lt;li&gt;Fix the worst page first. If a file is named, start there.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use the live public URL. A page behind a login will not load the same way for a free public check. Start with what visitors already see. Scores also move when you change nothing. Learn that quiet gap before you celebrate. See &lt;a href="https://pafcore.site/why-your-lighthouse-score-changes" rel="noopener noreferrer"&gt;Why your Lighthouse score changes when you did nothing&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  For freelancers and agencies
&lt;/h2&gt;

&lt;p&gt;Clients send a home screenshot. They ask if the site is fast. The honest answer is “I have not checked the page they pay on.”&lt;/p&gt;

&lt;p&gt;Send two links. Home and the page that matters. Talk in plain words: what the visitor felt, which file hurt, what you will fix next. A named file is easier to explain than a long tip list. More on that split in &lt;a href="https://pafcore.site/when-pagespeed-insights-is-not-enough" rel="noopener noreferrer"&gt;When PageSpeed Insights is enough (and when it is not)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PafCore can carry your name on the report when white label is on. If you manage many sites and need schedules, that is &lt;a href="https://pafcore.site/pro" rel="noopener noreferrer"&gt;Pro Watch&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does PageSpeed Insights check my whole website?
&lt;/h3&gt;

&lt;p&gt;No. It checks one URL at a time. A homepage score is not a score for every page on the site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is my homepage fast but checkout is slow?
&lt;/h3&gt;

&lt;p&gt;Those pages load different files. Checkout often pulls extra scripts, forms, and pictures the home page never uses. Measure checkout on its own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I only test my homepage?
&lt;/h3&gt;

&lt;p&gt;No. Test the pages people actually use. Home, a product or post, and the page where they pay or send a form.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can PafCore scan a whole site?
&lt;/h3&gt;

&lt;p&gt;Yes. It can read up to 20 pages and show the worst first. That pack costs 20 credits and emails you when it finishes. A one page check is still free.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What usually slows a WordPress site</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Sun, 20 Sep 2026 11:29:18 +0000</pubDate>
      <link>https://dev.to/femi_akinyemi/what-usually-slows-a-wordpress-site-d02</link>
      <guid>https://dev.to/femi_akinyemi/what-usually-slows-a-wordpress-site-d02</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published at &lt;a href="https://pafcore.site/what-usually-slows-a-wordpress-site" rel="noopener noreferrer"&gt;pafcore.site/what-usually-slows-a-wordpress-site&lt;/a&gt;. Prefer the canonical link for sharing and SEO.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmqm31qsobx86oo7eiktw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmqm31qsobx86oo7eiktw.png" alt="WordPress site speed: from a long tip list to a named file" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Blank screen. Late picture. Taps that feel ignored. Then a long tip list that still does not name the file. Here is the plain version, and a free way to find what to fix first.&lt;/p&gt;

&lt;p&gt;WordPress runs a large share of the public web. That success also means a lot of sites feel slow for the same reasons. Owners open Google PageSpeed Insights. They get a score and a scroll of advice. They still leave unsure what to change today.&lt;/p&gt;

&lt;p&gt;This piece is not a plugin hall of fame. It is what people usually feel, what a tip list misses, and how a free &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;PafCore&lt;/a&gt; report helps you name the slow file when it can.&lt;/p&gt;

&lt;h2&gt;
  
  
  What people feel first
&lt;/h2&gt;

&lt;p&gt;Before anyone talks about scores, visitors notice the site.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The screen stays blank longer than they expect.&lt;/li&gt;
&lt;li&gt;The big picture shows up late.&lt;/li&gt;
&lt;li&gt;Buttons and menus ignore the first tap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those moments matter more than a grade on a lab report. A score is useful. It is also incomplete on its own. I wrote about that gap in &lt;a href="https://pafcore.site/why-a-performance-score-is-not-enough" rel="noopener noreferrer"&gt;Why a performance score is not enough&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What usually slows WordPress
&lt;/h2&gt;

&lt;p&gt;Every site is different. Still, the same patterns show up again and again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large pictures.&lt;/strong&gt; Hero images, galleries, and blog photos that were never resized for the web. The browser waits. The visitor waits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heavy theme and plugin scripts.&lt;/strong&gt; Themes and plugins often load JavaScript and styles on every page, even when that page does not need them. One quiet plugin can cost more than the rest of the theme.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fonts.&lt;/strong&gt; Several font files, or fonts that block text until they finish loading. The page looks empty even when the words are already there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ads and embeds.&lt;/strong&gt; YouTube, maps, chat widgets, and ad scripts pull work from other servers. Your page waits on them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weak hosting.&lt;/strong&gt; Cheap shared hosting can make every request slower. It is real. It is not always the first place to look. Measure the page before you buy a bigger plan.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep it honest: none of these is always the villain. A lean plugin can be fine. A big image can be fine if it is the right size. Guessing wastes a week. Measuring names the file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why a tip list is hard to use
&lt;/h2&gt;

&lt;p&gt;PageSpeed Insights and Lighthouse are free and useful. The tip list is long on purpose. Many tips do not move the score much. Some are already fine for your page.&lt;/p&gt;

&lt;p&gt;People change three things. They run the check again. The number moves. They still cannot tell if the fix worked, or if the page just wobbles from one run to the next. That quiet swing is common. See &lt;a href="https://pafcore.site/why-your-lighthouse-score-changes" rel="noopener noreferrer"&gt;Why your Lighthouse score changes when you did nothing&lt;/a&gt; and &lt;a href="https://pafcore.site/why-faster-barely-moves-your-lighthouse-score" rel="noopener noreferrer"&gt;Why getting faster barely moves your Lighthouse score&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frp1tcrypfwsdcz7c1dks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frp1tcrypfwsdcz7c1dks.png" alt="Side by side: a long tip list versus a named file and what to fix first" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Left: a scroll of tips. Right: a named file and a clear next step.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What a useful report does
&lt;/h2&gt;

&lt;p&gt;A useful speed report for WordPress should do four simple jobs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the page more than once so you see a middle score, not one lucky screenshot.&lt;/li&gt;
&lt;li&gt;Show how far the score moves on its own (the quiet gap).&lt;/li&gt;
&lt;li&gt;Say what to fix first by points, not by how scary the tip sounds.&lt;/li&gt;
&lt;li&gt;Name the slow file when the measurement can find it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is what &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;PafCore&lt;/a&gt; is built for. Paste a public WordPress URL. You get a free report. No signup. When we can see the cost, we name the file. When we cannot, we still tell you what to try first.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to try it on your WordPress site
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pick a real page. The home page, or a busy post or product page.&lt;/li&gt;
&lt;li&gt;Paste the full address at &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;pafcore.site&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Read the middle score and the quiet gap first.&lt;/li&gt;
&lt;li&gt;Open the top finding. If a file is named, start there.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use the live public URL. A staging site behind a login will not load the same way for a free public check. Start with what visitors already see.&lt;/p&gt;

&lt;h2&gt;
  
  
  For freelancers and agencies
&lt;/h2&gt;

&lt;p&gt;You already sell WordPress speed work. Clients ask “did we get faster?” A long tip screenshot is hard to explain. A report that names a file is easier to send.&lt;/p&gt;

&lt;p&gt;PafCore can carry your name on the report when white label is on. Share the link. Talk in plain words: what the visitor felt, which file hurt, what you will fix next. Keep one quiet recheck after the fix so you do not celebrate noise.&lt;/p&gt;

&lt;p&gt;If you manage many sites and need schedules, that is &lt;a href="https://pafcore.site/pro" rel="noopener noreferrer"&gt;Pro Watch&lt;/a&gt;. The free report stays free for a one off paste.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is PageSpeed Insights enough for a WordPress site?
&lt;/h3&gt;

&lt;p&gt;It is a strong free snapshot of one URL. Use it to learn. When you need a named file, a middle score from a few runs, or a report for a client, add a clear report like PafCore. More on that split in &lt;a href="https://pafcore.site/when-pagespeed-insights-is-not-enough" rel="noopener noreferrer"&gt;When PageSpeed Insights is enough (and when it is not)&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is slow WordPress always a hosting problem?
&lt;/h3&gt;

&lt;p&gt;No. Weak hosting can hurt. Large pictures, heavy theme and plugin scripts, fonts, and embeds often cost more than people expect. Measure the page before you buy a bigger plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do plugins always make WordPress slow?
&lt;/h3&gt;

&lt;p&gt;Not always. Some plugins add little. Others load big scripts on every page. The honest answer is to measure and see which file costs the most points.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does PafCore replace WordPress speed plugins?
&lt;/h3&gt;

&lt;p&gt;No. Caching and image plugins can help. PafCore measures the live page, shows how far the score moves on its own, and names the slow file when it can. Use both: measure first, then choose a fix.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://pafcore.site/#scan" rel="noopener noreferrer"&gt;Check my site&lt;/a&gt; — paste a public WordPress URL. Free report. No signup.&lt;/p&gt;




</description>
    </item>
    <item>
      <title>When PageSpeed Insights is enough (and when it is not)</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Wed, 09 Sep 2026 10:31:43 +0000</pubDate>
      <link>https://dev.to/femi_akinyemi/when-pagespeed-insights-is-enough-and-when-it-is-not-64g</link>
      <guid>https://dev.to/femi_akinyemi/when-pagespeed-insights-is-enough-and-when-it-is-not-64g</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;PageSpeed Insights is free. It is official. For one URL, right now, it is often the right tool.&lt;/p&gt;

&lt;p&gt;You paste a link. You wait. You get a Lighthouse performance score, lab timings, and field Core Web Vitals when Google has enough Chrome visits. That is a strong snapshot.&lt;/p&gt;

&lt;p&gt;A snapshot is not a watchman.&lt;/p&gt;

&lt;p&gt;This guide is for people who ship websites and still live in PageSpeed tabs. It says when that habit is fine, when it starts to fail you, and what to add without turning every check into a science project.&lt;/p&gt;

&lt;p&gt;By the end you will know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when a manual PageSpeed Insights run is enough
&lt;/li&gt;
&lt;li&gt;why one lucky score is a weak proof of a fix
&lt;/li&gt;
&lt;li&gt;when scheduled checks and alerts earn their keep
&lt;/li&gt;
&lt;li&gt;a simple hybrid you can run this week
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chrome and PageSpeed Insights stay in the mix. I also run a free three run check at &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;pafcore.site&lt;/a&gt; when you want the quiet swing and what to fix first without babysitting six tabs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What PageSpeed Insights is good at
&lt;/h2&gt;

&lt;p&gt;Use PageSpeed Insights when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you are learning Largest Contentful Paint, layout shift, and the performance score
&lt;/li&gt;
&lt;li&gt;you are checking one page before or after a change
&lt;/li&gt;
&lt;li&gt;you manage one or two sites and can afford to click by hand
&lt;/li&gt;
&lt;li&gt;you need field data from the Chrome UX Report on a busy enough URL
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For those jobs, do not add a tool for the sake of a tool. Open the page. Read the wait in seconds. Read the grade. Move on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a single run fools you
&lt;/h2&gt;

&lt;p&gt;Even on one site, a single PageSpeed Insights screenshot is a weak proof.&lt;/p&gt;

&lt;p&gt;Lab scores move when the code does not. Machine load, third party tags, and plain luck all show up. I have seen a quiet page score 60, then 51, then 57 with nothing deployed. Nine points of weather.&lt;/p&gt;

&lt;p&gt;So before you celebrate a fix:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run three quiet checks with no deploy. Learn the gap between best and worst.
&lt;/li&gt;
&lt;li&gt;Ship the change. Run three more.
&lt;/li&gt;
&lt;li&gt;Compare middle scores. Only call it a clear score win when the rise beats that quiet gap.
&lt;/li&gt;
&lt;li&gt;Write the wait you meant to fix in seconds next to the grade.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That habit works in Chrome DevTools, in PageSpeed Insights, or in a free three run report. Same idea. More than one sample. Seconds and score together.&lt;/p&gt;

&lt;p&gt;I wrote the full quiet gap steps in &lt;a href="https://pafcore.site/why-your-lighthouse-score-changes" rel="noopener noreferrer"&gt;Why your Lighthouse score changes when you did nothing&lt;/a&gt;. Read that when the number moves with no deploy.&lt;/p&gt;

&lt;p&gt;A real wait can also drop while the Lighthouse performance score barely moves. That is the scoring curve, not a broken tool. See &lt;a href="https://pafcore.site/why-faster-barely-moves-your-lighthouse-score" rel="noopener noreferrer"&gt;Why getting faster barely moves your Lighthouse score&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Manual checks fail you twice: at scale, and when you trust one lucky run.&lt;/p&gt;

&lt;h2&gt;
  
  
  When manual checks stop being enough
&lt;/h2&gt;

&lt;p&gt;You have crossed the line when one or more of these are true.&lt;/p&gt;

&lt;h3&gt;
  
  
  You watch more than a handful of pages
&lt;/h3&gt;

&lt;p&gt;Ten sites, a few key URLs each, mobile and desktop: that is a long afternoon of paste, wait, copy, switch. By the time you finish, the first numbers are stale. Miss a week and you have no recent trail when a client asks why search or speed felt worse.&lt;/p&gt;

&lt;h3&gt;
  
  
  A deploy can break speed overnight
&lt;/h3&gt;

&lt;p&gt;A new script, a heavier hero image, a font change. With only weekly PageSpeed Insights checks, you may learn on Friday. Or when the client calls. Scheduled lab checks (daily, or hourly on a few critical pages) shorten that blind spot. An alert should find you, not the other way around.&lt;/p&gt;

&lt;h3&gt;
  
  
  You need history, not only today’s screenshot
&lt;/h3&gt;

&lt;p&gt;PageSpeed Insights does not keep your before and after for you. Trend lines, month to month notes, and “this page got worse after Tuesday’s release” need storage. Screenshots in a folder do not scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  You need budgets and alerts
&lt;/h3&gt;

&lt;p&gt;“LCP should stay under 2.5 seconds on mobile home” is a budget. PageSpeed Insights will not email you when you cross it. A watch with a threshold will.&lt;/p&gt;

&lt;h3&gt;
  
  
  You need something a client can read
&lt;/h3&gt;

&lt;p&gt;Raw PageSpeed output is for builders. Clients want a short story: what got better, what still hurts, what you will do next. That story is easier when history and middle scores already exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  A plain comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;PageSpeed Insights&lt;/th&gt;
&lt;th&gt;Scheduled checks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One URL, right now&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile and desktop&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lab score and timings&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Field Core Web Vitals when available&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Sometimes (depends on the tool)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Many sites without tab hell&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily or hourly runs&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;History and trends&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Budgets and alerts&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Honest before / after (more than one run)&lt;/td&gt;
&lt;td&gt;You must do it by hand&lt;/td&gt;
&lt;td&gt;Still your job to demand it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same Lighthouse family of lab maths in many tools. The workflow is what changes. Scheduling does not make a single run more truthful. It makes misses rarer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use both. Split the jobs.
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PageSpeed Insights or Chrome&lt;/strong&gt; for learning, quick peeks, and one off audits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three quiet runs&lt;/strong&gt; (by hand or a free report) whenever you claim a fix worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scheduled Watch&lt;/strong&gt; when pages matter every week: home, pricing, checkout, key landing pages. Prove you own the domain. Set a budget. Let email or a webhook tap you when the score or a wait crosses the line.&lt;/p&gt;

&lt;p&gt;That is the hybrid. Not “throw away PageSpeed Insights.” Not “buy a dashboard and stop thinking.”&lt;/p&gt;

&lt;h2&gt;
  
  
  A week one checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;List the pages that would hurt if they got slow (home, money pages, signup).
&lt;/li&gt;
&lt;li&gt;For each, run three quiet mobile checks once. Write the gap and the middle score.
&lt;/li&gt;
&lt;li&gt;Keep using PageSpeed Insights for random questions.
&lt;/li&gt;
&lt;li&gt;Put the money pages on a schedule if you already juggle more than a few sites, or if a bad deploy would cost you a client call.
&lt;/li&gt;
&lt;li&gt;After the next speed fix, compare middle scores to the quiet gap, and write Largest Contentful Paint (or your target wait) in seconds.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want the three run spread and “what to fix first” in one paste, use &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;pafcore.site&lt;/a&gt;. If you want daily or hourly checks, history, and alerts on pages you own, that is &lt;a href="https://pafcore.site/pro" rel="noopener noreferrer"&gt;Pro Watch&lt;/a&gt; at $19/mo. Credits on the site buy lab checks on a report. Watch runs are separate.&lt;/p&gt;

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

&lt;p&gt;PageSpeed Insights is enough when you need a clear snapshot of one URL. It is not enough when you manage many pages, when you must catch a bad deploy fast, or when you confuse one lucky grade with proof.&lt;/p&gt;

&lt;p&gt;Automate the watch. Keep the honest measure. Talk in seconds and in scores. That mix scales better than living in twenty PageSpeed tabs, and it stays truer than a dashboard that only stores one run a day.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When is PageSpeed Insights enough?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
One or two sites, learning the metrics, or a one off audit. Once you cannot keep up by hand, or once a missed week would embarrass you, add schedules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does a scheduled tool use different maths than PageSpeed Insights?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Many lab tools sit on the same Lighthouse style checks. Field Core Web Vitals still come from real Chrome visits over time. Do not expect one lab run to match Search Console the same afternoon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why three runs if I already have daily monitoring?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Daily monitoring catches surprise drops. Three runs answer “did this fix beat the quiet weather?” You still need both ideas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I keep PageSpeed Insights for some clients?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes. Low stakes pages stay manual. High stakes pages get schedules, budgets, and a quiet gap written down once.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Why your Lighthouse score changes when you did nothing</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Mon, 07 Sep 2026 06:10:41 +0000</pubDate>
      <link>https://dev.to/femi_akinyemi/why-your-lighthouse-score-changes-when-you-did-nothing-1d8e</link>
      <guid>https://dev.to/femi_akinyemi/why-your-lighthouse-score-changes-when-you-did-nothing-1d8e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you ship websites for a living, you have seen this.&lt;/p&gt;

&lt;p&gt;You change one thing on a page. You open Chrome, run Lighthouse, and the performance score goes up a few points. You take a screenshot. You tell the team the fix worked.&lt;/p&gt;

&lt;p&gt;Later the same day you run the same URL again. No new deploy. The score drops. Sometimes it drops below where you started.&lt;/p&gt;

&lt;p&gt;That is not you failing at your job. Lab scores move even when the code does not. CPU load, network jitter, third party tags, and plain luck all show up in a single run. One number is a sample. It is not a verdict.&lt;/p&gt;

&lt;p&gt;This article gives you a short, repeatable way to read that movement so you stop celebrating noise. By the end you will know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;why a quiet page can still swing several points &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;how to measure that swing in Chrome in about fifteen minutes  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;when a &lt;code&gt;fix&lt;/code&gt; is real enough to put in a pull request  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;how to talk about wins without overselling the score  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chrome DevTools is enough for every step. I also built a free three run check at &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;pafcore.site&lt;/a&gt; when you want that quiet gap without running Lighthouse by hand. The habit below is the same either way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the number moves
&lt;/h2&gt;

&lt;p&gt;Lighthouse loads your page in a controlled browser with throttling. That setup is useful because it is harsh and repeatable enough to compare days. It is not a perfect copy of every visitor.&lt;/p&gt;

&lt;p&gt;Small differences stack up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the machine was busier on one run
&lt;/li&gt;
&lt;li&gt;a third party script answered slower
&lt;/li&gt;
&lt;li&gt;an image raced and won or lost the “largest content” slot
&lt;/li&gt;
&lt;li&gt;the score itself sits on a curve, so the same time change is worth different points at different speeds
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So two runs of an untouched page can land far apart. I have seen a public site score 60, then 51, then 57 with nothing deployed in between. Nine points of weather.&lt;/p&gt;

&lt;p&gt;If you only look at the first run after a deploy, you will misread luck as skill.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will set up
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome
&lt;/li&gt;
&lt;li&gt;the live URL (or a stable preview URL)
&lt;/li&gt;
&lt;li&gt;a note app or the PR description
&lt;/li&gt;
&lt;li&gt;the same Lighthouse mode every time (mobile or desktop). Pick one and keep it
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turn extensions off, or use a clean window. Do not scroll or click while the audit runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1. Measure a quiet page
&lt;/h2&gt;

&lt;p&gt;Run Lighthouse three times on the same URL. Do not deploy between runs. Write down the three performance scores.&lt;/p&gt;

&lt;p&gt;Find the highest and the lowest. Subtract. That gap is how far this page already moves when you change nothing.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Run&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Gap: &lt;strong&gt;9 points&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some pages only move two or three points. Some move more. You cannot guess your gap. You measure it once per important page (home, pricing, checkout) and you refresh it when the page shape changes a lot.&lt;/p&gt;

&lt;p&gt;Doing those three quiet runs by hand is boring, which is why people skip it. I got tired of skipping it on client work, so I built a free report at &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;pafcore.site&lt;/a&gt; that loads the page three times and shows how far the score moves on its own. Use Chrome if you want full control. Use that report if you want the quiet gap filled in for you. Either way, the idea is the same: learn the swing before you celebrate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2. Ship the change, then measure again
&lt;/h2&gt;

&lt;p&gt;Deploy your fix. Run Lighthouse three more times with the same settings.&lt;/p&gt;

&lt;p&gt;Sort each set of three scores and take the &lt;strong&gt;middle&lt;/strong&gt; one. Compare middle after to middle before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clear win:&lt;/strong&gt; the middle score rose by &lt;strong&gt;more than&lt;/strong&gt; the quiet gap.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Not clear yet:&lt;/strong&gt; the rise is smaller than the gap.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;No win:&lt;/strong&gt; the middle score did not rise, or it fell.&lt;/p&gt;

&lt;p&gt;A four point bump on a page with a nine point quiet gap is not a story. It is still inside the swing you already saw.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3. Write the slow thing in seconds
&lt;/h2&gt;

&lt;p&gt;The score is curved. A real wait can get better while the grade barely moves, especially on a slow page.&lt;/p&gt;

&lt;p&gt;In the same note, track the metric you meant to fix:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before (middle of 3)&lt;/th&gt;
&lt;th&gt;After (middle of 3)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Performance score&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Biggest picture time&lt;/td&gt;
&lt;td&gt;4.8s&lt;/td&gt;
&lt;td&gt;3.1s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now you can say something honest: visitors should feel a clearer first picture, and the score is still noisy. That is better than claiming “we gained four points” when those four points never cleared the quiet gap.&lt;/p&gt;

&lt;p&gt;If both the score and the target time sit inside the quiet swing, do not call it a performance win. Keep the change if the code is cleaner. Leave the victory lap for later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4. Do not drown in the tip list
&lt;/h2&gt;

&lt;p&gt;Lighthouse will hand you a long list of opportunities. Most rows are not direct score inputs. The grade comes from a small set of lab metrics (largest content paint, blocking time, layout shift, first content paint, speed index, and similar). Tips about unused script or cache headers can still help people. They only raise the grade if they improve one of those metrics.&lt;/p&gt;

&lt;p&gt;Before you put a tip on the sprint board, answer out loud:&lt;/p&gt;

&lt;p&gt;“If this works, which score metric gets better?”&lt;/p&gt;

&lt;p&gt;No answer means cleanup. Cleanup is fine. Cleanup is not a score promise.&lt;/p&gt;

&lt;p&gt;If two tips are the same heavy image with two labels, fix the image once. Do not add imaginary points from both rows.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 5. Put numbers in the pull request
&lt;/h2&gt;

&lt;p&gt;Paste something like this so review stays calm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Lighthouse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;same&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;three&lt;/span&gt; &lt;span class="nx"&gt;runs&lt;/span&gt; &lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;three&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nx"&gt;Quiet&lt;/span&gt; &lt;span class="nf"&gt;gap &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt; &lt;span class="nx"&gt;minus&lt;/span&gt; &lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt; &lt;span class="nx"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
&lt;span class="nx"&gt;Middle&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
&lt;span class="nx"&gt;Middle&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
&lt;span class="nx"&gt;Target&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt; &lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
&lt;span class="nx"&gt;Clear&lt;/span&gt; &lt;span class="nx"&gt;win&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;yes&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;sure&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One screenshot of a lucky run starts arguments. This block ends most of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get from this habit
&lt;/h2&gt;

&lt;p&gt;After you use this a few times you will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stop treating a single Lighthouse score as proof.
&lt;/li&gt;
&lt;li&gt;Know your page’s quiet gap, so small bumps stop wasting the team’s week.
&lt;/li&gt;
&lt;li&gt;Separate &lt;code&gt;score went up&lt;/code&gt; from &lt;code&gt;the wait got better,&lt;/code&gt; and report both.
&lt;/li&gt;
&lt;li&gt;Pick tip list work that maps to a real metric, instead of boiling the ocean.
&lt;/li&gt;
&lt;li&gt;Leave a PR trail other people can trust.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the value. Not a new theory. A filter for what you already run.&lt;/p&gt;

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

&lt;p&gt;Your Lighthouse score changes when you did nothing because a lab run is noisy. That is expected.&lt;/p&gt;

&lt;p&gt;Measure three quiet runs. Learn the gap. Only call a fix a win when the new middle score beats that gap, and check the wait in seconds too. Treat the long tip list as optional until it names a metric you can move.&lt;/p&gt;

&lt;p&gt;Do this on your important pages once, then again after real releases. You will ship fewer fake wins and sleep better after you post the screenshot.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://pafcore.site" rel="noopener noreferrer"&gt;pafcore.site&lt;/a&gt; for the days when I did not want to babysit six Lighthouse runs myself. &lt;/p&gt;

&lt;p&gt;Paste a public URL, get the three run spread, see what to try first by points, and keep the habit from this article either way. &lt;/p&gt;

&lt;p&gt;Chrome alone still works. The site is there when you want the quiet check done in one pass.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>frontend</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What AI Still Misses in Production Next.js Apps</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Mon, 22 Jun 2026 07:10:47 +0000</pubDate>
      <link>https://dev.to/femi_akinyemi/what-ai-still-misses-in-production-nextjs-apps-pcg</link>
      <guid>https://dev.to/femi_akinyemi/what-ai-still-misses-in-production-nextjs-apps-pcg</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You ask AI to fix a fetch. It does. Types pass. The component renders. You push.&lt;/p&gt;

&lt;p&gt;Then dev crawls to a stop. Or prod serves a blank page. Or GitHub says conflict on a file you never touched. The model did its job inside the file. It did not see the repo around it.&lt;/p&gt;

&lt;p&gt;That gap is where most Next.js pain actually lives. Not broken JSX. Wrong root folder. Wrong env layer. Wrong deploy model. Stale git ref on the remote.&lt;/p&gt;

&lt;p&gt;This list is the stuff that keeps surviving AI refactors, green CI, and clean local merges.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Next dev watching the whole monorepo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your app sits inside a bigger repo. There is another lockfile above it. You run &lt;code&gt;next dev&lt;/code&gt;, see a warning about multiple lockfiles, and keep going.&lt;/p&gt;

&lt;p&gt;Compile gets slow. CPU stays high. Sometimes the machine feels stuck for no clear reason. AI will suggest faster imports or smaller components. It will not notice Next is indexing the wrong tree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pin Turbopack to the app folder in next.config.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fileURLToPath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appRoot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fileURLToPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;turbopack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appRoot&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This applies to certain monorepo/workspace setups.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is not the solution for every slow Next.js dev environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that lockfile warning shows up, treat it as a bug, not noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Putting server secrets in &lt;code&gt;NEXT_PUBLIC_*&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI puts API URLs and tokens in &lt;code&gt;NEXT_PUBLIC_*&lt;/code&gt; because that is what client fetch examples use. Those values ship in the browser bundle. Anyone can read them in the built JS.&lt;/p&gt;

&lt;p&gt;Or the opposite: server route handlers need an upstream URL that only exists as a public var pointing somewhere else. Local dev works with a hack. Prod does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Split by runtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Browser code → &lt;code&gt;NEXT_PUBLIC_*&lt;/code&gt; only&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Route handlers / server code → server env vars (no &lt;code&gt;NEXT_PUBLIC_ prefix&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upstream URLs and service tokens stay on the server. Client calls your BFF or the public API surface. Never both through one env var.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. API address missing or duplicating &lt;code&gt;/api/v1&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One backend expects calls to &lt;code&gt;https://api.example.com&lt;/code&gt;. Another expects &lt;code&gt;https://api.example.com/api/v1&lt;/code&gt;. Your code adds paths on top of the base address. Get it wrong by one segment and every request &lt;code&gt;404s&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This often happens when you copy .env.local from another app in the same repo. AI copies the fetch code correctly. It does not check whether the base address matches this app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open that app's &lt;code&gt;.env.example&lt;/code&gt;. See exactly how it builds URLs. Match that. Do not assume all apps in one company use the same address format.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. Adding server API files to an app that deploys as static &lt;code&gt;HTML&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some Next apps are built with output: &lt;code&gt;"export"&lt;/code&gt;. That means the output is plain &lt;code&gt;HTML&lt;/code&gt;, &lt;code&gt;CSS&lt;/code&gt;, and &lt;code&gt;JS&lt;/code&gt; files uploaded to static hosting (like S3 or Amplify static). There is no Node server running your app in production.&lt;/p&gt;

&lt;p&gt;You can add files under &lt;code&gt;app/api/&lt;/code&gt; and they may work in local dev. They will not exist after a static build. The hosting serves files, not your API routes.&lt;/p&gt;

&lt;p&gt;AI often suggests "add an API route to hide the token" without checking how the app is deployed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you deploy as static files, the browser calls your backend directly and your backend must allow that origin &lt;code&gt;(CORS)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you need a server middle layer to hide tokens, you cannot deploy as a pure static export. You need hosting that runs &lt;code&gt;Next.js&lt;/code&gt; server code.&lt;/p&gt;

&lt;p&gt;Pick one. Mixing both without knowing it is a common break.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. Local proxy setup that does not exist in production&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In local dev, teams often add a proxy in next.config so calls to &lt;code&gt;/api/v1/...&lt;/code&gt; get forwarded to a real server. Same website address, no cross-origin browser errors. Works great on your machine.&lt;/p&gt;

&lt;p&gt;A static production build does not include that proxy. The browser still calls &lt;code&gt;/api/v1/...&lt;/code&gt; on your static site address. Those requests never reach your backend.&lt;/p&gt;

&lt;p&gt;AI adds the proxy config. It rarely asks how you deploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use different setup for dev and prod:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Dev: proxy in next.config if you want&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prod: full backend URL in env, &lt;code&gt;CORS&lt;/code&gt; enabled on the backend&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run next build the same way CI does before you call it done. next dev alone is not enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. Server-only code imported in a client component&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next has a hard line between server code and client code. Files marked "use client" run in the browser. Things like reading request headers or secret env vars only work on the server.&lt;/p&gt;

&lt;p&gt;One shared &lt;code&gt;lib/api.ts&lt;/code&gt; file often gets imported everywhere. AI merges duplicate files and accidentally puts server code inside a client component. Build fails or you get strange runtime errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keep two files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;One for the browser (simple fetch, public env only)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One for the server (secret env, used only in app/api routes or server components)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If it reads cookies, headers, or private env, it never goes in a "use client" file.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;7. Login token is valid but for the wrong app&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some apps check two things: is the user logged in, and does their role match this specific product. You paste a login token into .env.local. It is not expired. The app still sends you back to the login page.&lt;/p&gt;

&lt;p&gt;The token belongs to a different dashboard or role. AI tries to fix the redirect logic. The check is working. Your token is for the wrong app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make sure three things match: the role setting in env, the role inside the token, and what the app's auth guard expects. When testing locally, use a token created for this app specifically.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;8. Fake login still turned on while shipping real auth&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many teams add a dev-only flag that skips real login so they can build UI faster. Useful. Easy to forget about.&lt;/p&gt;

&lt;p&gt;You say auth is done. But callback, token refresh, sign out, and role redirect were never tested with real login. AI sees a user object in state and assumes auth works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use the skip-login flag only for UI work. Before merging: turn it off, log in for real, open a protected page, sign out.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;9. Old dev server still running on the port&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You run pnpm dev again without closing the last one. Port &lt;code&gt;3000&lt;/code&gt; is busy. Next starts on &lt;code&gt;3001&lt;/code&gt;. You keep refreshing 3000 and see the wrong app or nothing useful.&lt;/p&gt;

&lt;p&gt;AI will debug your React code. It will not check which process owns the port.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nx"&gt;lsof&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Kill the old process. Start fresh. Confirm which port the terminal says before debugging anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;10. No conflict on your machine, conflict on GitHub&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You updated your branch with the latest main locally. Merge looks clean. The pull request on GitHub still shows conflicts.&lt;/p&gt;

&lt;p&gt;Your local branch and the copy on GitHub are not the same history anymore. GitHub compares the remote branch to main, not your rebased local copy.&lt;/p&gt;

&lt;p&gt;AI says "pull main again." You already did. On your machine only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After rebasing locally, update GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="nx"&gt;push&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;force&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;lease&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Only use this when you intentionally rebased or rewrote branch history.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Check they match:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="nx"&gt;HEAD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No output means local and remote are in sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;11. Expired login token in your env file&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For local development, some apps read a login token from .env.local and attach it to server-side API calls. Tokens expire. Handlers return 401 Unauthorized. The UI shows empty data or error states.&lt;/p&gt;

&lt;p&gt;Looks like the API is broken. AI rewrites error handling. The token is just old.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Decode the token and check the expiry time. Get a fresh one from a real login. Treat dev tokens like passwords with an expiration date.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;12. Public env vars set after the build, not during it&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; values are written into the JavaScript at build time.&lt;/p&gt;

&lt;p&gt;If you set them in your hosting dashboard after the build already ran, the live site still has the old values baked in.&lt;/p&gt;

&lt;p&gt;Works on your machine with .env.local. Wrong API address or app setting in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make sure public env vars exist before next build runs in CI. Some teams write a .env.production file in the build step. Check build logs, not just runtime settings in the host UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13. Copied env from the wrong app in the same repo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two Next apps in one repo look similar. Same folders, same tools. Different env needs. Copy .env.local from one app into another.&lt;/p&gt;

&lt;p&gt;The shell loads. The first API call or redirect fails. AI assumes env is fine because the file is there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every app has its own .env.example. Start from that file every time.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Env&lt;/code&gt; is per app, not per repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;14. Login page and dashboard on the same local port&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Login usually lives on one app. The dashboard on another. In local dev that means different ports (for example &lt;code&gt;3001&lt;/code&gt; for login, &lt;code&gt;3000&lt;/code&gt; for dashboard).&lt;/p&gt;

&lt;p&gt;If both URLs in env point to the same port, redirects get confused. Loops, wrong landing page, sign out going nowhere useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Write down the flow: login address → callback → dashboard address.&lt;/p&gt;

&lt;p&gt;Set &lt;code&gt;env&lt;/code&gt; to match separate apps, even on localhost.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;15. Green code review, nobody ran a production build&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI fixed the component in the prompt. Types pass. Review looks good. Nobody ran a full production build with real login and a real API call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before merge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;next dev starts without warnings&lt;/li&gt;
&lt;li&gt;next build passes with the same config CI uses&lt;/li&gt;
&lt;li&gt;One real login (skip-login flag off)&lt;/li&gt;
&lt;li&gt;One API call that returns real data&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;AI is strong inside a single file. It is weak on repo layout, env setup, static vs server deploy, and whether your GitHub branch matches your local branch.&lt;/p&gt;

&lt;p&gt;Those live outside the chat window. Check them yourself and a lot of "random" Next.js bugs stop being random.&lt;/p&gt;

&lt;h2&gt;
  
  
  Need Help With Your Next.js or AWS Project?
&lt;/h2&gt;

&lt;p&gt;Over the last 5+ years, I've helped build and maintain production systems used by businesses across different industries. If you're looking for help with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js applications&lt;/li&gt;
&lt;li&gt;React architecture&lt;/li&gt;
&lt;li&gt;AWS deployments&lt;/li&gt;
&lt;li&gt;AI integrations&lt;/li&gt;
&lt;li&gt;Performance optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;feel free to reach out.&lt;/p&gt;

&lt;p&gt;LinkedIn: &lt;a href="https://www.linkedin.com/in/femi-akinyemi/" rel="noopener noreferrer"&gt;Femi Akinyemi&lt;/a&gt;&lt;br&gt;
DEV: &lt;a href="https://dev.to/femi_akinyemi"&gt;dev.to/femi_akinyemi&lt;/a&gt;&lt;br&gt;
Portfolio: &lt;a href="https://www.femiakinyemi.xyz/" rel="noopener noreferrer"&gt;Femi Akinyemi&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>frontend</category>
      <category>ai</category>
    </item>
    <item>
      <title>Wedding Memories: The Collaborative Wedding Album!</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Fri, 11 Oct 2024 15:28:15 +0000</pubDate>
      <link>https://dev.to/femi_akinyemi/wedding-memories-the-collaborative-wedding-album-2em3</link>
      <guid>https://dev.to/femi_akinyemi/wedding-memories-the-collaborative-wedding-album-2em3</guid>
      <description>&lt;h1&gt;
  
  
  Wedding Memories
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/pinata"&gt;The Pinata Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Wedding Memories&lt;/strong&gt; is an app designed to capture, share, and cherish every unforgettable moment from your special day! It allows wedding guests to easily upload photos, creating a collaborative album that captures the essence of the celebration. By using &lt;strong&gt;&lt;a href="https://docs.pinata.cloud/api-reference/files" rel="noopener noreferrer"&gt;Pinata's Files API&lt;/a&gt;&lt;/strong&gt;, the app provides a seamless experience for secure and efficient media uploads and storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Check out the live version of the app here: &lt;strong&gt;&lt;a href="https://main.d2kkpc9riv1gcp.amplifyapp.com/" rel="noopener noreferrer"&gt;Wedding Memories&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Screenshots:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Upload Section&lt;/strong&gt; - A user-friendly interface for selecting and uploading media files:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vuvq6lda3z0u3k0p5la.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vuvq6lda3z0u3k0p5la.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gallery View&lt;/strong&gt; - Displays all uploaded memories in a responsive layout:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9j7ongcgov4g723jnp92.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9j7ongcgov4g723jnp92.png" alt=" " width="799" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9himbgy3f6tpfjmlel0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9himbgy3f6tpfjmlel0.png" alt=" " width="799" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mobile-Optimized Design&lt;/strong&gt; - Ensures smooth user experience across all devices:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpy66u8vt5z5qwui9nla8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpy66u8vt5z5qwui9nla8.png" alt=" " width="800" height="715"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Download Options&lt;/strong&gt; - Guests can download their favourite moments to keep memories forever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghtusmmo8mv0r4buk5ry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghtusmmo8mv0r4buk5ry.png" alt=" " width="799" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Code
&lt;/h2&gt;

&lt;p&gt;You can explore the full code for this project on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/femakin/WeddingMemories" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fbadge%2FGitHub-Wedding%2520Memories-blue%3Fstyle%3Dfor-the-badge%26logo%3Dgithub" alt="View on GitHub" width="252" height="28"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More Details
&lt;/h2&gt;

&lt;p&gt;Wedding Memories leverages Pinata’s Files API to build secure and efficient file uploads. Below is a brief breakdown of the integration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File Uploads&lt;/strong&gt;: Guests can easily upload images directly from their devices. This is accomplished using the &lt;code&gt;pinata.upload.file&lt;/code&gt;, which uses JWT-based authentication to ensure secure file handling. Once uploaded, the files are securely stored, and their unique identifiers (CIDs) are generated for retrieval.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Local Previews Before Upload&lt;/strong&gt;: To enhance the guest experience, wedding memories generate local previews of images before uploading them. This feature allows guests to confirm their selections and make adjustments, ensuring only their desired image is submitted.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After successful uploads, the files are rendered using the &lt;a href="https://docs.pinata.cloud/sdk/getting-started" rel="noopener noreferrer"&gt;file API&lt;/a&gt;. This guarantees rapid access to the content, providing guests with a seamless experience when viewing shared memories.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Download Feature&lt;/strong&gt;: Guests can now download their favourite photos directly from the app, making it easy to save cherished memories to their devices. The app includes an API route that facilitates the secure downloading of files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  API Integration Breakdown:
&lt;/h3&gt;

&lt;p&gt;The building includes several key API routes, each designed to handle specific functionalities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File Upload Endpoint &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This accepts file uploads and stores them using Pinata’s upload function. It then generates a signed URL for easy access to the uploaded file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pinata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../../../utils/config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uploadData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gateways&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSignedURL&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uploadData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;expires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Internal Server Error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;API Key Generation &lt;code&gt;(/api/key/route.ts)&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Creates a temporary API key with permissions to pin files to IPFS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="cm"&gt;/* eslint-disable @typescript-eslint/no-unused-vars */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pinata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../../../utils/config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force-dynamic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keyData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;keyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;pinning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;pinFileToIPFS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;maxUses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keyData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error creating API Key:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;List Files &lt;code&gt;(/api/listfiles/route.ts)&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retrieves a list of uploaded files from Pinata, allowing users to view all shared content in the gallery&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force-dynamic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RequestInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_PINATA_JWT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.pinata.cloud/v3/files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error listing files&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error listing files&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error listing files&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Image Proxy &lt;code&gt;(/api/proxy/route.ts)&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Acts as a proxy for fetching images from external sources, ensuring users can easily access and download their images.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Get the image URL from the query string&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Image URL is required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to fetch image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/octet-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Disposition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attachment; filename="downloaded_image"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error fetching image:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error fetching image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Signed URL Creation &lt;code&gt;(/api/sign/route.ts)&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Generates signed URLs for uploaded files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pinata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../../../utils/config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force-dynamic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mimeType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mimeType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gateways&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSignedURL&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;expires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7776000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gateways&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSignedURL&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;expires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7776000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;optimizeImage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;dpr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sharpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error creating signed URL:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The app is built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pinata&lt;/strong&gt;: File API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;: React, Next.js, Framer Motion&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Styling&lt;/strong&gt;: Tailwind CSS for responsive, beautiful layouts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hosting&lt;/strong&gt;: Amplify for deployment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Weddings are joyous occasions filled with love, laughter, and countless memorable moments. Capturing these precious memories is essential, and with the &lt;strong&gt;&lt;a href=""&gt;Wedding Memories&lt;/a&gt;&lt;/strong&gt; app, it's now easier than ever! &lt;/p&gt;

&lt;p&gt;This application allows every guest to take lovely pictures on the event day and share them seamlessly, ensuring that no moment is missed and everyone has access to the collective memories of the celebration.&lt;/p&gt;

&lt;p&gt;The app harnesses the power of &lt;strong&gt;&lt;a href="https://docs.pinata.cloud/sdk/getting-started" rel="noopener noreferrer"&gt;Pinata's Files API&lt;/a&gt;&lt;/strong&gt; to provide a secure and efficient way for guests to upload and share their photos and videos, creating a collaborative album that beautifully encapsulates the essence of the day.&lt;/p&gt;

&lt;p&gt;For more details on how the app integrates with Pinata and to explore its capabilities, please refer to the &lt;a href="https://main.d2kkpc9riv1gcp.amplifyapp.com/" rel="noopener noreferrer"&gt;Pinata documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you.&lt;/p&gt;




</description>
      <category>devchallenge</category>
      <category>pinatachallenge</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>How to Upload Files to Amazon S3 with React and AWS SDK</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Mon, 04 Mar 2024 15:54:14 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-upload-files-to-amazon-s3-with-react-and-aws-sdk-b0n</link>
      <guid>https://dev.to/aws-builders/how-to-upload-files-to-amazon-s3-with-react-and-aws-sdk-b0n</guid>
      <description>&lt;h1&gt;
  
  
  INTRODUCTION
&lt;/h1&gt;

&lt;p&gt;Have you ever needed to upload files from your React application to the cloud?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;Amazon S3 (Simple Storage Service)&lt;/a&gt; provides a robust and scalable solution for storing and managing various file types. &lt;/p&gt;

&lt;p&gt;Using the &lt;a href="https://www.npmjs.com/package/aws-sdk" rel="noopener noreferrer"&gt;AWS SDK&lt;/a&gt;, this article shows how to build a &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt; application that allows users to upload files to S3. &lt;/p&gt;

&lt;p&gt;In the article, you'll learn how to handle file selection, validate file types, and handle the upload process with error handling. &lt;/p&gt;

&lt;p&gt;By the end, you'll have a basic understanding of integrating file uploads to S3 with React and AWS.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisite:
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of React and JavaScript.&lt;/li&gt;
&lt;li&gt;An AWS account and access credentials to configure the S3 connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Getting Started
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Creating S3 Bucket
&lt;/h2&gt;

&lt;p&gt;The first thing to do is start by creating an S3 bucket. Log in to the AWS S3 console and follow the instructions to create a new bucket, as shown in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8jjca9mzxy0834s4a6hm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8jjca9mzxy0834s4a6hm.png" alt="Create Bucket" width="799" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the files publicly accessible
&lt;/h2&gt;

&lt;p&gt;Next is to edit the bucket policy to make the file publicly accessible. &lt;/p&gt;

&lt;p&gt;Here's how:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the bucket: Go to the AWS S3 console and navigate to your specific bucket.&lt;/li&gt;
&lt;li&gt;Access permissions: Click on the 'Permissions' tab.&lt;/li&gt;
&lt;li&gt;Edit policy: Locate the 'Bucket policy' section and click the 'Edit Policy' button.&lt;/li&gt;
&lt;li&gt;Paste JSON code: Copy and paste the following JSON code into the policy editor.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2012-10-17&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Statement&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PublicListGet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Effect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Allow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Principal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s3:List*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s3:Get*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Resource&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:s3:::YOUR_BUCKET_NAME&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:s3:::YOUR_BUCKET_NAME/*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Save changes: Review the policy and click 'Save'.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The policy above makes all objects in the S3 bucket named &lt;em&gt;YOUR_BUCKET_NAME&lt;/em&gt; publicly accessible. Anyone online can list and download all files in the bucket and its subfolders. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The policy should be used cautiously as it can be a security risk.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Editing CORS Policy
&lt;/h2&gt;

&lt;p&gt;Now, you must enable &lt;strong&gt;Cross-origin resource sharing (CORS)&lt;/strong&gt;. &lt;br&gt;
The CORS configuration, written in JSON, allows clients to access resources in a different domain from applications within the same domain.&lt;/p&gt;

&lt;p&gt;Scroll down a bit more, and you will see a section where you can edit CORS settings. &lt;/p&gt;

&lt;p&gt;Paste the below JSON into it to enable the upload of files from the front end, or you will receive a CORS error while uploading files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao4xc1vpoovglxsk5ghd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao4xc1vpoovglxsk5ghd.png" alt="Editing CORS Policy" width="799" height="189"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AllowedHeaders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AllowedMethods&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DELETE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AllowedOrigins&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ExposeHeaders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Uploading files to your AWS S3 bucket is now possible. &lt;/p&gt;

&lt;p&gt;Next, we'll talk about the front end.&lt;/p&gt;

&lt;p&gt;Run the command below to Scaffold your react application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="nx"&gt;filestorage&lt;/span&gt;
&lt;span class="nx"&gt;cd&lt;/span&gt; &lt;span class="nx"&gt;filestorage&lt;/span&gt;
&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing AWS SDK for JavaScript
&lt;/h2&gt;

&lt;p&gt;You must install the AWS SDK for JavaScript, allowing your React JS application to communicate with S3.&lt;/p&gt;

&lt;p&gt;Run the command below to install AWS SDK&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;sdk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next is to replace the default code in the App.js file with the code below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Uncomment one of the following import options:&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Import entire SDK (optional)&lt;/span&gt;
&lt;span class="c1"&gt;// import AWS from 'aws-sdk/global'; // Import global AWS namespace (recommended)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;S3&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-sdk/clients/s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Import only the S3 client&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFile&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;uploading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUploading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allowedTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/quicktime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;audio/mpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;audio/wav&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Add more supported types as needed&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleFileChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectedFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allowedTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectedFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid file type. Only images and PDFs are allowed.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uploadFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setUploading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;S3_BUCKET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_bucket_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your bucket name&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_region&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your region&lt;/span&gt;

    &lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_accesskeyID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_secretAccessKey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S3_BUCKET&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REGION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;putObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setUploading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;File uploaded successfully.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setUploading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error uploading file: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Inform user about the error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleFileChange&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;uploadFile&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;uploading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Uploading...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Upload File&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;



&lt;p&gt;The React code above creates a simple file uploader app. Users can select a file that is uploaded to an Amazon S3 bucket. &lt;/p&gt;

&lt;p&gt;Here's a brief breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;State Management:&lt;/strong&gt; It uses React's useState hook to manage the selected file and uploading state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Allowed File Types:&lt;/strong&gt; Defines an array of allowed file types.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File Change Handler:&lt;/strong&gt; When a file is selected, it checks if the file type is allowed and updates the state with the selected file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Upload Function:&lt;/strong&gt; Constructs S3 parameters and uploads the file to the specified S3 bucket. It handles success and error cases, providing feedback to the user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Interface:&lt;/strong&gt; Provides a file input field and a button for uploading—the button text changes based on the uploading state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AWS SDK:&lt;/strong&gt; Imports necessary modules from the AWS SDK to interact with S3 and configures it with credentials and region.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code offers a basic file uploading functionality to S3 from a React app.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;You've successfully uploaded a file to your S3 bucket with these steps! You can consult the documentation for tailored options that suit your specific requirements.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>s3</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build a simple project management app with Neon, PostgREST, and DigitalOcean</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Tue, 27 Feb 2024 16:29:41 +0000</pubDate>
      <link>https://dev.to/hackmamba/build-a-simple-project-management-app-with-neon-postgrest-and-digitalocean-3iid</link>
      <guid>https://dev.to/hackmamba/build-a-simple-project-management-app-with-neon-postgrest-and-digitalocean-3iid</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://neon.tech/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Neon&lt;/a&gt; &lt;a href="https://neon.tech/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;s&lt;/a&gt;&lt;a href="https://neon.tech/?ref=hm" rel="noopener noreferrer"&gt;erverless Postgres&lt;/a&gt; stands out in the world of databases. It provides a fully managed service for PostgreSQL with unique features like &lt;a href="https://neon.tech/docs/introduction/autoscaling?ref=hm" rel="noopener noreferrer"&gt;autoscaling&lt;/a&gt;, &lt;a href="https://neon.tech/docs/introduction/branching?ref=hm" rel="noopener noreferrer"&gt;branching&lt;/a&gt;, and a great serverless experience. Its architecture makes Neon serverless Postgres different from how users set up Postgres databases by separating storage and compute.&lt;/p&gt;

&lt;p&gt;In this guide, we'll leverage the strengths of Neon serverless Postgres to establish a &lt;a href="https://postgrest.org/en/stable/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;PostgREST&lt;/a&gt; API on &lt;a href="https://www.digitalocean.com/" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt;. With PostgREST, a PostgreSQL database is turned into a RESTful API.&lt;/p&gt;

&lt;p&gt;As a practical illustration, we’ll walk through creating a mini project management application with complete create, read, update, and delete (CRUD) functionality. This application will allow users to create new projects, view existing details, update information, and delete projects as needed. &lt;/p&gt;

&lt;p&gt;Ultimately, you’ll have a fully functional PostgREST API powered by Neon serverless Postgres, seamlessly incorporated into your DigitalOcean environment.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;p&gt;To fully grasp the concepts presented in this tutorial, you’ll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://console.neon.tech/sign_in?ref=?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;A Neon account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.digitalocean.com/registrations/new?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;A DigitalOcean account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A basic understanding of PostgreSQL and REST APIs&lt;/li&gt;
&lt;li&gt;Node.js installed on your local machine&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Create a Neon project
&lt;/h1&gt;

&lt;p&gt;To set up a Neon project, let’s begin by &lt;a href="https://console.neon.tech/sign_in?ref=hm" rel="noopener noreferrer"&gt;registering for an account&lt;/a&gt;, assuming you don't have one already. With that done, we’ll create a new project by selecting a &lt;strong&gt;Project Name&lt;/strong&gt;. We will use (Project Management), &lt;strong&gt;Database name&lt;/strong&gt; (&lt;code&gt;managementdata&lt;/code&gt;), and &lt;strong&gt;Postgres version&lt;/strong&gt;. For this demonstration, we’ll opt for 16 as the Postgres version and choose the region closest to where we would like to deploy our app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fec6lasih9wwqzrnhz0sm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fec6lasih9wwqzrnhz0sm.png" alt="project creation" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon successfully creating the project, we will get a connection string for connecting to our database. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnggp1seqi36sdtdzaxvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnggp1seqi36sdtdzaxvi.png" alt="Connection string" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project directory
&lt;/h2&gt;

&lt;p&gt;To organize our project files and resources, let's create a new project directory using the following terminal command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;mkdir&lt;/span&gt; &lt;span class="nx"&gt;neonprojectappfolder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Loading demo data into the database
&lt;/h2&gt;

&lt;p&gt;With the project directory in place, follow these steps to load the demo data into the database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To connect to our database, we will use the connection string provided by Neon and run it in the terminal:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;psql&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgresql://xxx:xxxx@ep-xxx-xxx-01196450.us-east-2.aws.neon.tech/xxx?sslmode=require&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command connects to the database using the provided connection string.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Now that we’re connected to the database, let’s upload our demo data by running the command below in the terminal:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;psql&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgresql://xxx:xxxx@ep-xxx-xxx-01196450.us-east-2.aws.neon.tech/xxx?sslmode=require &amp;lt; projects.sql
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The demo data (projects.sql) can be downloaded &lt;a href="https://gist.github.com/femakin/f1ecfa87c607744a65b03a6a1c11271a?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The demo data must be downloaded in our project directory before running the command above.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Upon successfully loading demo data into the database, we’ll see a response like the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3gkniset8zudqnmwzm03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3gkniset8zudqnmwzm03.png" alt="Loading Data RESPONSE" width="793" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output &lt;code&gt;CREATE SEQUENCE&lt;/code&gt;, &lt;code&gt;CREATE TABLE&lt;/code&gt;, and &lt;code&gt;INSERT 0 5&lt;/code&gt; indicate that the sequence, the table, and five rows were inserted into the table, respectively.&lt;/p&gt;

&lt;p&gt;Next, we verify the demo data is present using the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, connect to the database again by running the command below:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;psql&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgresql://xxx:xxxx@ep-xxx-xxx-01196450.us-east-2.aws.neon.tech/xxx?sslmode=require&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Just a note that we’re connecting to the database again because we exited the database when we loaded the demo data into the database.)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now, let us run the command below in our terminal:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;FROM&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt; &lt;span class="nx"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SQL command &lt;code&gt;SELECT * FROM projects LIMIT 1&lt;/code&gt; retrieves data from the "projects" table.&lt;/p&gt;

&lt;p&gt;We should get a response like the one in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2ha8fd346isseqk9dg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2ha8fd346isseqk9dg3.png" alt="Data response" width="798" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a result, our demo data has been successfully uploaded to our database.&lt;/p&gt;

&lt;h1&gt;
  
  
  PostgREST setup
&lt;/h1&gt;

&lt;p&gt;Now that we have set up our database, the next step involves setting up PostgREST. First, we install PostgREST by following this installation &lt;a href="https://postgrest.org/en/stable/explanations/install.html#install" rel="noopener noreferrer"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After a successful installation, we can confirm everything works perfectly by typing &lt;code&gt;postgrest -e&lt;/code&gt; in the terminal. We should see a response similar to the image below, indicating a successful operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftdtqafpm4bp7quc3uq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftdtqafpm4bp7quc3uq0.png" alt="PostgREST confirmation" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Granting CRUD access to a database user
&lt;/h2&gt;

&lt;p&gt;Now, we can connect to the database using the connection string above.&lt;/p&gt;

&lt;p&gt;(We’re connecting again because checking the PostgREST installation disconnected us from the database.)&lt;/p&gt;

&lt;p&gt;After a successful connection, execute the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;GRANT&lt;/span&gt; &lt;span class="nx"&gt;SELECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;INSERT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DELETE&lt;/span&gt; &lt;span class="nx"&gt;ON&lt;/span&gt; &lt;span class="nx"&gt;TABLE&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projects&lt;/span&gt; &lt;span class="nx"&gt;TO&lt;/span&gt; &lt;span class="nx"&gt;femakin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command grants &lt;code&gt;femakin&lt;/code&gt; user permissions for &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; operations (CRUD) on the projects table in the public schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgREST connection setup
&lt;/h2&gt;

&lt;p&gt;The next step is to establish a PostgREST connection. To do this, exit the database terminal by typing &lt;code&gt;\q&lt;/code&gt; and then enter the following command: &lt;code&gt;vi projects.config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The command &lt;code&gt;vi projects.config&lt;/code&gt; opens the &lt;code&gt;projects.config&lt;/code&gt; file using the vi text editor.&lt;/p&gt;

&lt;p&gt;We then edit the &lt;code&gt;projects.config&lt;/code&gt; file by pasting the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;psql&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgresql://xxx:xxxx@ep-xxx-xxx-01196450.us-east-2.aws.neon.tech/xxx?sslmode=require&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;anon&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;femakin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration specifies a PostgreSQL database connection string URI with credentials and connection details. It also defines the default database schema as &lt;code&gt;public&lt;/code&gt; and the anonymous role as &lt;code&gt;femakin&lt;/code&gt; for authentication purposes.&lt;/p&gt;

&lt;p&gt;To ensure the proper setup of the PostgREST connection, we can run &lt;code&gt;postgrest projects.config&lt;/code&gt; in the terminal. This command should yield a response similar to the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzbcjbig5n8aixx7nnjg6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzbcjbig5n8aixx7nnjg6.png" alt="PostgREST setup" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the API routes
&lt;/h2&gt;

&lt;p&gt;Now, let us employ a tool such as Postman to conduct tests. In the browser, navigate to the URL &lt;code&gt;localhost:3000/projects&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The connection is working if we find something like this in the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9iz40hdc0pd431f2y1kx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9iz40hdc0pd431f2y1kx.png" alt="API response" width="798" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Otherwise, we must review all the steps and ensure everything is present.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bootstrapping VueJS application
&lt;/h1&gt;

&lt;p&gt;The quickest approach to get started with our VueJS application is to clone this &lt;a href="https://github.com/femakin/Neo-Demo-PostgREST-DigitalOcean-App?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;a href="https://github.com/femakin/Neo-Demo-PostgREST-DigitalOcean-App?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;H&lt;/a&gt;&lt;a href="https://github.com/femakin/Neo-Demo-PostgREST-DigitalOcean-App?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;ub repository&lt;/a&gt;&lt;a href="https://github.com/femakin/Neo-Demo-PostgREST-DigitalOcean-App?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/femakin/Neo-Demo-PostgREST-DigitalOcean-App&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the instructions in the README file: Run &lt;code&gt;npm install&lt;/code&gt; to install dependencies and &lt;code&gt;npm run dev&lt;/code&gt; to start the application. &lt;/p&gt;

&lt;p&gt;The application will appear like this in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7zhvwg9phnus7zgc4ut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7zhvwg9phnus7zgc4ut.png" alt="Home screen" width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code in the &lt;code&gt;src/App.vue&lt;/code&gt; is a project management app using VueJS. It handles project data, loading status, and user interactions. It fetches project data and lets users view, add, update, and delete projects. The focus is on simplicity for managing projects in a web app.&lt;/p&gt;

&lt;h1&gt;
  
  
  Deploying to Digital Ocean
&lt;/h1&gt;

&lt;p&gt;Thus far, we’ve created our database, set up PostgREST, and got our mini-application running in VueJS. Now, we need to deploy everything to DigitalOcean. To do this, &lt;a href="https://cloud.digitalocean.com/login?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;log into&lt;/a&gt; the DigitalOcean dashboard and create a Droplet by following this &lt;a href="https://docs.digitalocean.com/products/droplets/how-to/create/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;DigitalOcean Droplets are Linux-based virtual machines (VMs) that operate on virtualized hardware.&lt;/p&gt;

&lt;p&gt;Now, let’s access our droplet. First, copy the created droplet's &lt;code&gt;IP&lt;/code&gt; address, as shown in the image below, and then use it to &lt;code&gt;SSH&lt;/code&gt; into the droplet.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Keep this IP address handy; we’ll need it for future steps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mhmeui04vux3xsw42q9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mhmeui04vux3xsw42q9.png" alt="Droplet IP" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, log into the droplet by using the copied droplet's &lt;code&gt;IP&lt;/code&gt; and running the command below in the terminal:&lt;/p&gt;

&lt;p&gt;Next, log into the droplet by using the copied droplet's &lt;code&gt;IP&lt;/code&gt; and running the command below in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;ssh&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;copied_droplet_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will prompt us to enter a passphrase for the key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ubqmdtaliud93bag5ku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ubqmdtaliud93bag5ku.png" alt="SSH connection" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we will update the &lt;code&gt;apt cache&lt;/code&gt; to get the latest packages by running the below command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuoryv16fr72w2u6dt7w6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuoryv16fr72w2u6dt7w6.png" alt="Cache update" width="799" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A runtime library, an essential component of the PostgreSQL database system, must be installed. This library is a connection and communication interface for PostgreSQL database servers.&lt;/p&gt;

&lt;p&gt;Let’s run the following command in the terminal to install the PostgreSQL runtime library, &lt;code&gt;libpq5&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;libpq5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will ensure that the necessary runtime components for PostgreSQL are installed on the system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq4e76z9ehe1xnepcy02x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq4e76z9ehe1xnepcy02x.png" alt="libpq5" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we’ll install PostgREST from the release page available &lt;a href="https://github.com/PostgREST/postgrest/releases/tag/v11.2.0?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Choose a version compatible with your operating system; for example, we will install &lt;code&gt;postgrest-v11.2.0-linux-static-x64.tar.xz&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;wget&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://github.com/PostgREST/postgrest/releases/download/v11.2.0/postgrest-v11.2.0-linux-static-x64.tar.xz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbaip4unlefyi6qaokpl6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbaip4unlefyi6qaokpl6.png" alt="Downloading PostgREST" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next is to extract the compressed file to obtain the executable. Run the command below in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;management&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;tar&lt;/span&gt; &lt;span class="nx"&gt;vxf&lt;/span&gt; &lt;span class="nx"&gt;postgrest&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;v11&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;2.1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xz&lt;/span&gt;
    &lt;span class="nx"&gt;postgrest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ensure everything works, let's run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;postgrest&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should expect a response similar to the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1u7bz05v4enhyczrepg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1u7bz05v4enhyczrepg3.png" alt="Starting PostgREST" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring PostgREST on Droplet
&lt;/h2&gt;

&lt;p&gt;Let us configure PostgREST by following the steps below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Move PostgREST to &lt;code&gt;usr/bin&lt;/code&gt; by executing the following command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;mv&lt;/span&gt; &lt;span class="nx"&gt;postgrest&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt; Navigate to &lt;code&gt;/usr/bin&lt;/code&gt; using the command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt; Confirm that everything is in order by running
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;postgrest&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt; Now, create a directory using the command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;mkdir&lt;/span&gt; &lt;span class="nx"&gt;projectsmanagement&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's return to our local machine within the project directory and copy the config file using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;cat&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We previously created the config file in the section above. We’ll see a configuration similar to the one below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;psql&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgresql://xxx:xxxx@ep-xxx-xxx-01196450.us-east-2.aws.neon.tech/xxx?sslmode=require&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;anon&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;femakin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, return to our droplet and create a new config using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;vi&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the copied &lt;code&gt;projects.config&lt;/code&gt;, and save the file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fel85bp045qtzz1a7zpdi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fel85bp045qtzz1a7zpdi.png" alt="projects.config" width="798" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can connect to our database using &lt;code&gt;postgrest postgrest.config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You should now see a response stating that the connection is successful, as shown in the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy4hen1b92vaoc428s63s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy4hen1b92vaoc428s63s.png" alt="Connecting to database" width="798" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can test the API using the command &lt;code&gt;curl localhost:3000/projects&lt;/code&gt;, and observe a response from our database, like the illustration below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rheon8ixoizrc6ejng0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rheon8ixoizrc6ejng0.png" alt="Response" width="800" height="669"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating systemd
&lt;/h2&gt;

&lt;p&gt;Now, let's create a system service for a PostgREST. We need to go back to PostgREST documentation &lt;a href="https://postgrest.org/en/stable/integrations/systemd.html?highlight=daemonizing?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vlrujgxk16wu4vfg7cw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vlrujgxk16wu4vfg7cw.png" alt="systemd" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now go back to our droplet and create the system file using the command below:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vi /etc/systemd/system/postgrest.service&lt;/code&gt; and paste the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;REST&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt; &lt;span class="nx"&gt;PostgreSQL&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;
    &lt;span class="nx"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;postgresql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/usr/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;postgrest&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;postgrest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;
    &lt;span class="nx"&gt;ExecReload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/bin/&lt;/span&gt;&lt;span class="nx"&gt;kill&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;SIGUSR1&lt;/span&gt; &lt;span class="nx"&gt;$MAINPID&lt;/span&gt;
    &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;nobody&lt;/span&gt;
    &lt;span class="nx"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;nogroup&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Install&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;multi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; 

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

&lt;/div&gt;



&lt;p&gt;Here, we’ll reload, start, and check the status of the &lt;code&gt;systemd&lt;/code&gt; using the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;     &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;systemctl&lt;/span&gt; &lt;span class="nx"&gt;daemon&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;reload&lt;/span&gt;
     &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;systemctl&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="nx"&gt;postgrest&lt;/span&gt;
     &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;systemctl&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="nx"&gt;postgrest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F846ktxeh8r4r0h4nind8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F846ktxeh8r4r0h4nind8.png" alt="Reload systemd" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Testing the API using &lt;code&gt;curl localhost:3000/projects&lt;/code&gt; gives us our expected response, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyz1j4wlo2qp8m4vns95y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyz1j4wlo2qp8m4vns95y.png" alt="curl response" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading the Vue app into Droplet
&lt;/h2&gt;

&lt;p&gt;It's time to upload our project management application into the droplet. Return to your local machine to upload the application into the droplet and run &lt;code&gt;npm run build&lt;/code&gt; inside the project directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsf6ubt4nqvos6f15c8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsf6ubt4nqvos6f15c8x.png" alt="App build" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our production Vue App, we can copy it into the droplet using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;scp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="nx"&gt;dist&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;copied_droplet_IP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vueapp&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next is to install a web server to serve the files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9if7m8bwx7ogk0tsoic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9if7m8bwx7ogk0tsoic.png" alt="Installing a web server" width="800" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next is to create a virtual host using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;copied_droplet_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the code below and save.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3ebtck6rzx990agk0k7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3ebtck6rzx990agk0k7.png" alt="Virtual host" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now create a link using.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;ln&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;copied_droplet_IP&lt;/span&gt;  &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reload &lt;code&gt;nginx&lt;/code&gt; using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;systemctl&lt;/span&gt; &lt;span class="nx"&gt;reload&lt;/span&gt; &lt;span class="nx"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing deployment
&lt;/h2&gt;

&lt;p&gt;We can now go to the browser and paste the copied_droplet_IP. We should see our project management app on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrcztp4uqczpbidoal70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrcztp4uqczpbidoal70.png" alt="Rendered app" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, we will see an error if we look at the console. This error is there because we have not set up the API server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0u6fzur0gsmjvfa1ek41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0u6fzur0gsmjvfa1ek41.png" alt="API server error" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To take care of this, we must use &lt;code&gt;nginx&lt;/code&gt; to proxy &lt;code&gt;/API&lt;/code&gt; to the PostgREST instance running on our droplet, as described in this &lt;a href="https://postgrest.org/en/v10.2/admin.html?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;PostgREST documentation.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now edit our &lt;code&gt;nginx&lt;/code&gt; configuration using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;Copied_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, update the configuration with the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;     &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;default_type&lt;/span&gt;  &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;proxy_hide_header&lt;/span&gt; &lt;span class="nx"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;add_header&lt;/span&gt; &lt;span class="nx"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Location&lt;/span&gt;  &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;$upstream_http_content_location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;proxy_set_header&lt;/span&gt;  &lt;span class="nx"&gt;Connection&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;proxy_pass&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:3000/;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnw4y5fay9thginq8qvh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnw4y5fay9thginq8qvh4.png" alt="Update nginx configuration" width="799" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then reload &lt;code&gt;nginx&lt;/code&gt; using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;systemctl&lt;/span&gt; &lt;span class="nx"&gt;reload&lt;/span&gt; &lt;span class="nx"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now return to the browser and paste the copied Droplet IP. Now, we can see our application without any errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydbi7spu56b6r474ildn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydbi7spu56b6r474ildn.png" alt="Complete Page" width="800" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You successfully created a mini-project management application by seamlessly integrating PostgREST and DigitalOcean, backed by Neon serverless Postgres.&lt;/p&gt;

&lt;p&gt;Here are a few recommendations to take it further. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a domain to the Droplet&lt;/li&gt;
&lt;li&gt;Configure HTTPS&lt;/li&gt;
&lt;li&gt;Add more features to the applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The possibilities are endless when it comes to Neon serverless Postgres.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We covered a lot in this tutorial. Let’s recap! You accomplished the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leveraged Neon serverless Postgres to establish a PostgREST API on DigitalOcean.&lt;/li&gt;
&lt;li&gt;Transformed a PostgreSQL database into a functional RESTful API using PostgREST.&lt;/li&gt;
&lt;li&gt;Created a mini-project management application as a practical illustration using VueJS.&lt;/li&gt;
&lt;li&gt;Demonstrated seamless integration between Neon serverless Postgres, PostgREST, and DigitalOcean.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://neon.tech/?ref=hm" rel="noopener noreferrer"&gt;Neon Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://postgrest.org/en/stable/?utm_id=HMBcommunity&amp;amp;utm_medium=blog&amp;amp;utm_source=hackmamba" rel="noopener noreferrer"&gt;PostgREST Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.digitalocean.com/products/droplets/how-to/create/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;How to Create a Droplet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=iHtsALtD5-U" rel="noopener noreferrer"&gt;Building a Contacts List with PostgREST and Vue.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Understanding AWS Amplify Monitoring Metrics Definitions</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Thu, 08 Feb 2024 12:52:21 +0000</pubDate>
      <link>https://dev.to/aws-builders/understanding-aws-amplify-monitoring-metrics-definitions-43c2</link>
      <guid>https://dev.to/aws-builders/understanding-aws-amplify-monitoring-metrics-definitions-43c2</guid>
      <description>&lt;p&gt;One of the App settings provided by AWS Amplify is Monitoring, which allows us to monitor metrics like site traffic, configure alarms, and access logs for our Amplify applications in nearly real time. &lt;/p&gt;

&lt;p&gt;In this article, we will look at the definitions of CloudWatch metrics in the &lt;em&gt;AWS/AmplifyHosting&lt;/em&gt; namespace for monitoring our app traffic, errors, data transfer, and latency.&lt;/p&gt;

&lt;p&gt;When we decide to monitor our app hosted on AWS Amplify, we navigate to the app settings sidebar and click on the Monitoring tab, which takes us to the monitoring page, as shown in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foka9xv4bhdargzsf4e1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foka9xv4bhdargzsf4e1p.png" alt="monitoring page" width="800" height="713"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, what do all these terms mean?&lt;/p&gt;

&lt;p&gt;According to the Monitoring and CloudWatch section of the AWS Amplify Hosting, Here is what they mean.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Requests:&lt;/strong&gt;  This is the total number of viewer requests Amplify Hosting receives for all HTTP methods and HTTP and HTTPS requests, which means it is how often people visit our website hosted on Amplify, including all types of requests like clicking links or loading pages with HTTP or HTTPS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bytes downloaded:&lt;/strong&gt; The total amount of data transferred out of our app (downloaded) in bytes by viewers for GET, HEAD, and OPTIONS requests, which means it shows the total data sent from our app to users when they download something, like images or files, through actions like clicking links or accessing pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BytesUploaded:&lt;/strong&gt; This is the total amount of data transferred into our app (uploaded) in bytes using POST and PUT requests. Therefore, it represents the complete data our app receives when users upload files or submit forms, typically through actions like posting comments or uploading images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4XXErrors:&lt;/strong&gt; The number of requests that returned an error in the HTTP status code 400-499 range. This metric counts the requests that encountered errors on the user's end, like entering incorrect information or trying to access restricted content, resulting in an error response from the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5XXErrors:&lt;/strong&gt; The number of requests that returned an error in the HTTP status code 500-599 range. This metric counts the requests that encountered errors on the user's end, like entering incorrect information or trying to access restricted content, resulting in an error response from the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency:&lt;/strong&gt; This metric measures how quickly Amplify Hosting responds to a request, from when it receives it until it sends the first byte of data back to the network. It excludes the time the response travels to the viewer's device. &lt;br&gt;
Key statistics include the &lt;strong&gt;average&lt;/strong&gt;, &lt;strong&gt;maximum&lt;/strong&gt;, &lt;strong&gt;minimum&lt;/strong&gt;, and various percentiles (&lt;strong&gt;p10&lt;/strong&gt;, &lt;strong&gt;p50&lt;/strong&gt;, &lt;strong&gt;p90&lt;/strong&gt;, &lt;strong&gt;p95&lt;/strong&gt;, &lt;strong&gt;p100&lt;/strong&gt;), providing insights into response times across different scenarios.&lt;/p&gt;

&lt;p&gt;For more details about the metrics, we can access the CloudWatch metrics in the AWS Management Console &lt;a href="https://console.aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudcomputing</category>
      <category>awsamplify</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to add Astro social share to your Astro application</title>
      <dc:creator>Femi Akinyemi</dc:creator>
      <pubDate>Mon, 08 Jan 2024 05:44:36 +0000</pubDate>
      <link>https://dev.to/femi_akinyemi/how-to-add-astro-social-share-to-your-astro-application-4bl1</link>
      <guid>https://dev.to/femi_akinyemi/how-to-add-astro-social-share-to-your-astro-application-4bl1</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://astro.build/?utm_source=hackmamba-community" rel="noopener noreferrer"&gt;Astr&lt;/a&gt;&lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;o&lt;/a&gt; is a modern web framework that combines the best features of static site generators (SSGs) and server-side rendering (SSR) frameworks. It is designed to be fast, flexible, and easy to use, making it an excellent choice for building various websites, including blogs, portfolios, e-commerce sites, and more.&lt;/p&gt;

&lt;p&gt;Astro offers several benefits, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Speed:&lt;/strong&gt; Astro websites are typically very fast, thanks to server-side rendering and component islands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SEO:&lt;/strong&gt; Astro websites are also well-suited for SEO, as they are static and can be pre-rendered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexibility:&lt;/strong&gt; Astro is a very flexible framework, and many websites can be built.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User-friendliness:&lt;/strong&gt; Astro is beginner-friendly and offers a gentle learning curve, making it accessible even for those new to web development.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the benefits of using Astro is that it makes it easy to add social sharing buttons to your website. Social sharing is essential because social sharing is a great way to increase traffic to your website and promote your content.&lt;/p&gt;

&lt;p&gt;Social sharing is essential for web applications because it can help to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Boost website traffic&lt;/li&gt;
&lt;li&gt;Content promotion&lt;/li&gt;
&lt;li&gt;Establish brand presence&lt;/li&gt;
&lt;li&gt;Lead generation and sales&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article explains how to add social sharing buttons to your Astro application.&lt;/p&gt;

&lt;p&gt;In this article, you will build an application that displays random jokes using the &lt;a href="https://icanhazdadjoke.com/?utm_source=hackmamba-community" rel="noopener noreferrer"&gt;random dad joke API&lt;/a&gt; with a social share button using &lt;a href="https://github.com/silent1mezzo/astro-social-share?utm_source=hackmamba-community" rel="noopener noreferrer"&gt;Astro social share&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The source code for the application is readily available here on &lt;a href="https://github.com/femakin/Astrol-Social-Share?utm_source=hackmamba-community" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By the end of this article, you will have a better understanding of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is Astro social share?&lt;/li&gt;
&lt;li&gt;How to install Astro social share&lt;/li&gt;
&lt;li&gt;How to use Astro social share
# Understanding Astro social share&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/silent1mezzo/astro-social-share?utm_source=hackmamba-community" rel="noopener noreferrer"&gt;Astro social share&lt;/a&gt; is your Astro site's social media share button. It's also one of the recently added Astro integrations.&lt;/p&gt;

&lt;p&gt;Astro social share comes with some good features, which are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No dependencies or external scrips&lt;/li&gt;
&lt;li&gt;Share buttons for:

&lt;ul&gt;
&lt;li&gt;Facebook&lt;/li&gt;
&lt;li&gt;Hacker News&lt;/li&gt;
&lt;li&gt;LinkedIn&lt;/li&gt;
&lt;li&gt;Reddit&lt;/li&gt;
&lt;li&gt;Twitter&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Icons included from &lt;a href="https://simpleicons.org/?utm_source=hackmamba-community" rel="noopener noreferrer"&gt;https://simpleicons.org/&lt;/a&gt;
# Prerequisites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To fully grasp the concepts presented in this article, the following are required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed, version v18.14.1 or later.&lt;/li&gt;
&lt;li&gt;Code Editor - Preferably &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt; or another code editor.
# Bootstrapping Astro application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To bootstrap your Astro application, you will use the &lt;code&gt;create astro&lt;/code&gt; setup wizard following the steps below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Astro by running the following command in your terminal:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;npm create astro@latest&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Confirm your choice by typing 'y' to install the &lt;code&gt;create-astro&lt;/code&gt; package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When prompted to specify the location for your new project, enter the folder name where you'd like to create a new project directory. For example, you can type. /&lt;code&gt;Astro-Social-Share&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You'll be presented with a list of starter templates. Use the arrow keys (up and down) to navigate to the "Empty" template, and then press the &lt;code&gt;Return&lt;/code&gt; or &lt;code&gt;Enter&lt;/code&gt; key to select it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When asked if you want to install project dependencies, type &lt;code&gt;y&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you are asked whether you plan on using TypeScript, type &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When prompted about initializing a new git repository, type &lt;code&gt;y&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, you can navigate to your &lt;code&gt;Astro-Social-Share&lt;/code&gt; directory to begin and preview your application by running the command below in your terminal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd cd Astro-Social-Share

npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Click the localhost link within your terminal window to view a real-time preview of your brand-new Astro website!&lt;/p&gt;

&lt;p&gt;This is how the browser preview of the Astro 'Empty Project' starter website should appear:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--dEnO18w7--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhexffud1hawq3v6xwr4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--dEnO18w7--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhexffud1hawq3v6xwr4.png" alt="Astro Preview Page" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Setting up API page
&lt;/h1&gt;

&lt;p&gt;Now, you need to set up the Home Page. The home page will be the entry point for your application's home page. In the &lt;code&gt;src/pages/index.astro&lt;/code&gt;, paste the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
    &lt;span class="o"&gt;---&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../pages/api/fetchdata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TwitterShareButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SocialShare&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro-social-share&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TwitterBUTTON&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;TwitterShareButton&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;---&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h-screen flex flex-col items-center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-3xl font-bold mt-12 mb-12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Share&lt;/span&gt; &lt;span class="nx"&gt;Your&lt;/span&gt; &lt;span class="nx"&gt;Daily&lt;/span&gt; &lt;span class="nx"&gt;Joke&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;
            &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;joke-btn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-blue-500 hover-bg-blue-700 text-white font-bold py-2 px-4 rounded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;Click&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;X&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt; &lt;span class="nx"&gt;below&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;share&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;daily&lt;/span&gt; &lt;span class="nx"&gt;joke&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mt-12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;joke-container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;joke&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SocialShare&lt;/span&gt;
            &lt;span class="nx"&gt;buttons&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;TwitterBUTTON&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;joke&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;via&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;akinyemi_t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;joke&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/body&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/html&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the explanation of the code provided above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The first line imports the GET function from a file at &lt;code&gt;../pages/api/fetchdata&lt;/code&gt;. It suggests that you are making a &lt;code&gt;GET&lt;/code&gt; request to fetch some data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, you import components related to social sharing using Astro. Specifically, you import the &lt;code&gt;TwitterShareButton&lt;/code&gt; and &lt;code&gt;SocialShare&lt;/code&gt; components from the &lt;code&gt;astro-social-share&lt;/code&gt; library.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You define &lt;code&gt;TwitterBUTTON&lt;/code&gt; as an array containing the &lt;code&gt;TwitterShareButton&lt;/code&gt;. This variable plays a role later when configuring social sharing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The code then awaits the result of the &lt;code&gt;GET&lt;/code&gt; request, assigns it to the &lt;code&gt;response&lt;/code&gt; variable, and parses the &lt;code&gt;JSON&lt;/code&gt; content from the response into the data variable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The HTML section starts with the opening &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag and defines the web page's structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag, the content of the web page looks as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element with the class &lt;code&gt;h-screen flex flex-col items-center&lt;/code&gt; is used to center the content vertically in the viewport.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; element with the text &lt;code&gt;Share Your Daily Joke on X&lt;/code&gt; is displayed at the top of the page.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; element with the ID &lt;code&gt;joke-btn&lt;/code&gt; displays a message encouraging the user to click an &lt;code&gt;X&lt;/code&gt; icon below to share a daily joke.&lt;/li&gt;
&lt;li&gt;Another &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element with the ID &lt;code&gt;joke-container&lt;/code&gt; displays the content of the &lt;code&gt;data.joke&lt;/code&gt; property, which contains the daily joke fetched from the &lt;code&gt;API.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&amp;lt;SocialShare&amp;gt;&lt;/code&gt; component enables sharing on social media. Configured with the buttons prop set to &lt;code&gt;TwitterBUTTON&lt;/code&gt; (likely to display a Twitter share button), the description assigned to the &lt;code&gt;data.joke&lt;/code&gt; set to &lt;code&gt;akinyemi_t&lt;/code&gt; (the Twitter username), and the joke's title. Text.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Testing and debugging
&lt;/h1&gt;

&lt;p&gt;Follow the steps below to test your application:&lt;/p&gt;

&lt;p&gt;Navigate into your directory and run&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;Your application is now running at &lt;code&gt;localhost:4321&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To view your homepage, visit &lt;code&gt;localhost:4321&lt;/code&gt; in your browser. You should see the homepage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--rYnSU3JD--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frimfobwd3n6q00fn0h66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--rYnSU3JD--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frimfobwd3n6q00fn0h66.png" alt="Final Demo Page" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can share your joke on social media by clicking the X icon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--baS2VNRy--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvknakxu707ac8xemgj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--baS2VNRy--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvknakxu707ac8xemgj3.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What's next?
&lt;/h1&gt;

&lt;p&gt;There are so many other things you can do with Astro social share. You can integrate other social buttons, like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facebook&lt;/li&gt;
&lt;li&gt;Hacker News&lt;/li&gt;
&lt;li&gt;LinkedIn&lt;/li&gt;
&lt;li&gt;Reddit&lt;/li&gt;
&lt;li&gt;Twitter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, you can make the application more interactive by adding more features like changing the jokes.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Astro is a fast and flexible web framework suitable for various website types. Adding social sharing buttons to your Astro application enhances traffic, content promotion, brand presence, and lead generation.&lt;/p&gt;

&lt;p&gt;This article has shown how to integrate social sharing buttons using the Astro social share component, using a random joke application as an example.&lt;/p&gt;

&lt;p&gt;The provided GitHub source code serves as a practical reference.&lt;/p&gt;

&lt;p&gt;By following the article, you've gained the ability to empower your web applications with Astro social share, making your content more shareable and engaging.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>astro</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
