<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Full Stack Fish</title><description>Jon Crowell&apos;s hands-on, opinionated notes on building software with Prisma, Node, Remix, and SQL Server</description><link>https://fullstackfish.com/</link><language>en-us</language><atom:link href="https://fullstackfish.com/rss.xml" rel="self" type="application/rss+xml"/><item><title>The robots moved my cheese</title><link>https://fullstackfish.com/posts/2026-06-29-from-writing-code-to-specifying-it/</link><guid isPermaLink="true">https://fullstackfish.com/posts/2026-06-29-from-writing-code-to-specifying-it/</guid><description>The dev job moved from writing every line of code to specifying what you want and judging the result. That shift made ambitious projects like Periodic Mole possible.</description><pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For most of my career, figuring out what needed to be built, then deciding what was practical to build, and then manually coding it, was how I spent my time. Programming involved typing, reading, researching, and writing tests. The figuring out part is still what I do, but the coding part has changed dramatically, with the majority of the code now being generated by AI models. Side projects have always been important, but they are even more so now. We have to learn to play well with the robots, and there is no substitute for building things. Periodic Mole is a side project that has gone a long way in teaching me what my new job is.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://periodicmole.com&quot;&gt;Periodic Mole&lt;/a&gt; is a study site for high-school chemistry students, built on the idea that a kid who knows the elements as cold as their times tables walks into class ahead, and starts to enjoy chemistry instead of dreading it. So it teaches through small games that bake the chemistry in by spaced repetition, with an &lt;a href=&quot;https://periodicmole.com/periodic-table&quot;&gt;interactive periodic table&lt;/a&gt; at the center. The table draws all 118 elements, tints each one by chemical family, and folds from an eighteen-column grid on a laptop down to a readable list on a phone. Tap chlorine and a panel shows how it bonds, where it turns up in the games, and which concepts explain it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/periodic-mole-table.png&quot; alt=&quot;The interactive periodic table on Periodic Mole, every element tinted by chemical family&quot; title=&quot;Periodic Mole&apos;s interactive periodic table. Tap any element for how it bonds, where it turns up, and the concepts behind it.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A few years ago I would have hand-built every piece of that. The grid math, the family colors, the breakpoint where the layout gives up on columns, the panel that loads when you tap an element. Each was its own small pile of code to write, run, and fix. Multiply that across a whole app and a project this size would once have stalled at seventy percent, the interesting idea buried under weeks of plumbing. Now I describe the pieces instead. I tell the model what the table holds, the states it can be in, and what should happen when someone taps an element on a phone versus a wide screen. It produces the components, and I evaluate if they do what they should and if they are built as they should be.&lt;/p&gt;
&lt;p&gt;Describing software well turns out to be harder than producing it by hand, not easier. Vague instructions get vague code. The questions that used to settle themselves while my fingers moved now have to be answered out loud, before anything exists: how a screen reader should announce a selection, whether the family colors carry meaning or are just decoration, what the phone layout does when the grid runs out of room. The skill moved up a level, from &quot;how do I write this in TypeScript&quot; to &quot;what, exactly, should this do, in every case I can think of and a few I can&apos;t.&quot; Being able to articulate what I want, and why, has become a superpower.&lt;/p&gt;
&lt;p&gt;And the model is confidently wrong often enough to require rigorous review processes and tooling to enforce standards. Ask one to color a periodic table and it will light up every family in a different bright hue, the whole grid glowing like a toy. That is the opposite of what this site needs, where the color has to mean something rather than just decorate, so the palette stays muted and deliberate. The code compiles either way. What it gets wrong is the judgment, and judgment is the part that did not get automated.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/periodic-mole-home.png&quot; alt=&quot;The Periodic Mole landing page: &amp;quot;Walk into chemistry already knowing your way around.&amp;quot;&quot; title=&quot;Periodic Mole: chemistry, learned by playing.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you want to see what &quot;specified, then corrected&quot; actually ships as, the &lt;a href=&quot;https://periodicmole.com/periodic-table&quot;&gt;interactive periodic table&lt;/a&gt; is the place to start, and the rest of &lt;a href=&quot;https://periodicmole.com&quot;&gt;Periodic Mole&lt;/a&gt; is built the same way.&lt;/p&gt;
&lt;p&gt;So the work redistributed rather than vanished. Less production, far more deciding and reviewing, and far more catching the screen that looks right and isn&apos;t. The typing was never the hard part. It only ever hid how much of the job was judgment.&lt;/p&gt;
&lt;p&gt;Our world has changed. Wishing the cheese didn&apos;t move is pointless, we have to embrace what frequently feels like a brand new job. This revolution hasn&apos;t resulted in less work. For most of us, it has resulted in staying up until midnight almost every night, trying new things, seeing the beautiful things we can build. The cheese moved. I&apos;m trying to figure out where it went.&lt;/p&gt;
</content:encoded></item><item><title>Install or Update sqlpackage on a Mac via Zip (No .NET SDK Needed)</title><link>https://fullstackfish.com/posts/2025-05-02-install-or-update-sqlpackage-on-a-mac/</link><guid isPermaLink="true">https://fullstackfish.com/posts/2025-05-02-install-or-update-sqlpackage-on-a-mac/</guid><description>A step-by-step guide to installing or updating sqlpackage on macOS using the direct download zip file, avoiding the need for the full .NET SDK. Includes the Gatekeeper fix.</description><pubDate>Fri, 02 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This guide explains how to install or update &lt;code&gt;sqlpackage&lt;/code&gt; on macOS using the direct download (&lt;code&gt;.zip&lt;/code&gt;) method. &lt;strong&gt;This approach avoids installing the full .NET SDK, which can be overkill if you only need the &lt;code&gt;sqlpackage&lt;/code&gt; command-line tool.&lt;/strong&gt; It includes the necessary steps for installation/update and handling macOS Gatekeeper security prompts for the newly extracted files.&lt;/p&gt;
&lt;p&gt;This guide assumes you want to install &lt;code&gt;sqlpackage&lt;/code&gt; into a directory named &lt;code&gt;sqlpackage&lt;/code&gt; within your home directory (&lt;code&gt;~/sqlpackage&lt;/code&gt;). If you choose a different location (e.g., &lt;code&gt;/usr/local/sqlpackage&lt;/code&gt;), please adjust the paths in the commands accordingly.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;You are comfortable using the Terminal application on macOS.&lt;/li&gt;
&lt;li&gt;You know how to download files from the internet.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How do I install or update sqlpackage on a Mac?&lt;/h2&gt;
&lt;p&gt;Whether installing for the first time or updating an existing zip-based installation, the steps are largely the same.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Download the Latest Version:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to the official Microsoft &lt;code&gt;sqlpackage&lt;/code&gt; download page in your web browser (a web search for &quot;Download sqlpackage Microsoft&quot; should find it).&lt;/li&gt;
&lt;li&gt;Download the latest &lt;code&gt;.zip&lt;/code&gt; archive suitable for macOS (often labeled for Linux/macOS). It will likely be saved to your &lt;code&gt;~/Downloads&lt;/code&gt; folder. Note the filename.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Remove Old Version (If Updating):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If you are updating an existing installation in &lt;code&gt;~/sqlpackage&lt;/code&gt;, open the &lt;strong&gt;Terminal&lt;/strong&gt; application and remove the old directory first.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This command permanently deletes files. Double-check the path is correct before running. Skip this step if installing for the first time.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Adjust path if your installation isn&apos;t in ~/sqlpackage
rm -rf ~/sqlpackage
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extract the New Version:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Open &lt;strong&gt;Terminal&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the directory where you downloaded the new &lt;code&gt;.zip&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd ~/Downloads
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the installation directory (this is safe even if updating, as we removed the old one):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir ~/sqlpackage
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Extract the contents of the new &lt;code&gt;.zip&lt;/code&gt; archive into this directory. &lt;strong&gt;Remember to replace &lt;code&gt;sqlpackage-*.zip&lt;/code&gt; with the actual filename you downloaded&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Example: unzip sqlpackage-osx-x64-170.0.94.3.zip -d ~/sqlpackage
unzip sqlpackage-*.zip -d ~/sqlpackage
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Set Execute Permissions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Grant execute permission to the main &lt;code&gt;sqlpackage&lt;/code&gt; executable file within the new directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chmod +x ~/sqlpackage/sqlpackage
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Handle macOS Gatekeeper:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;macOS security (Gatekeeper) will likely prevent the newly extracted files from running initially because they came from a downloaded archive.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To avoid multiple security prompts for &lt;code&gt;sqlpackage&lt;/code&gt; and its components, run the following command &lt;strong&gt;once&lt;/strong&gt; to remove the quarantine attribute from all files in the installation directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;xattr -r -d com.apple.quarantine ~/sqlpackage
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This command recursively (&lt;code&gt;-r&lt;/code&gt;) deletes (&lt;code&gt;-d&lt;/code&gt;) the &lt;code&gt;com.apple.quarantine&lt;/code&gt; attribute, effectively telling macOS you trust the contents of this specific folder.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add to PATH (Recommended for Convenience):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;To run &lt;code&gt;sqlpackage&lt;/code&gt; from any terminal location without typing the full path, add its directory to your shell&apos;s PATH.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Edit your shell&apos;s configuration file (usually &lt;code&gt;~/.zshrc&lt;/code&gt; for modern macOS):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nano ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add this line at the very end of the file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export PATH=&quot;$PATH:$HOME/sqlpackage&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Save the file (&lt;code&gt;Ctrl+O&lt;/code&gt;, Enter) and exit (&lt;code&gt;Ctrl+X&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Apply the changes to your current terminal session (or open a new terminal window):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;(Note: If you were updating and &lt;code&gt;~/sqlpackage&lt;/code&gt; was already in your PATH, you can skip this step, but verifying doesn&apos;t hurt)&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify the Installation / Update:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Confirm that &lt;code&gt;sqlpackage&lt;/code&gt; runs correctly and check the version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sqlpackage /version
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The output should display the version number corresponding to the &lt;code&gt;.zip&lt;/code&gt; file you just installed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Important Notes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Installation Path:&lt;/strong&gt; If you chose an installation directory other than &lt;code&gt;~/sqlpackage&lt;/code&gt;, ensure you replaced &lt;code&gt;~/sqlpackage&lt;/code&gt; in &lt;em&gt;all&lt;/em&gt; commands above with your chosen path.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Admin Rights (&lt;code&gt;sudo&lt;/code&gt;):&lt;/strong&gt; If your chosen installation directory is in a system location like &lt;code&gt;/usr/local/sqlpackage&lt;/code&gt;, you might need to prefix the &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;mkdir&lt;/code&gt;, &lt;code&gt;unzip&lt;/code&gt;, &lt;code&gt;chmod&lt;/code&gt;, and &lt;code&gt;xattr&lt;/code&gt; commands with &lt;code&gt;sudo&lt;/code&gt; and enter your administrator password.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You have now successfully installed or updated &lt;code&gt;sqlpackage&lt;/code&gt; using the zip file method, without needing the full .NET SDK!&lt;/p&gt;
</content:encoded></item><item><title>Turn Your Prettier Config Into a VS Code Snippet</title><link>https://fullstackfish.com/posts/2022-02-07-prettier-snippet-for-vscode/</link><guid isPermaLink="true">https://fullstackfish.com/posts/2022-02-07-prettier-snippet-for-vscode/</guid><description>Step-by-step guide to turning your prettier settings into a VS Code snippet so that you can easily add them to any new project without looking them up</description><pubDate>Mon, 07 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The settings in your Prettier config keep your code style consistent across every project. Most of us set them the slow way, though — copying a &lt;code&gt;.prettierrc&lt;/code&gt; out of an old repo, or looking the options up yet again every time we start something new. Turning that config into a VS Code snippet you can drop in with a few keystrokes is a micro superpower. It&apos;s a small way to help yourself fall into the pit of success.&lt;/p&gt;
&lt;p&gt;Here&apos;s the short version: save your Prettier settings as a global VS Code snippet once, and from then on you can drop the whole config into any new project by typing a short trigger like &lt;code&gt;prettierconfig&lt;/code&gt;. The rest of this post walks through building that snippet step by step.&lt;/p&gt;
&lt;p&gt;My preferred config looks like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;tabWidth&quot;: 2,
  &quot;printWidth&quot;: 80,
  &quot;endOfLine&quot;: &quot;auto&quot;,
  &quot;arrowParens&quot;: &quot;always&quot;,
  &quot;trailingComma&quot;: &quot;all&quot;,
  &quot;semi&quot;: false,
  &quot;singleQuote&quot;: true,
  &quot;bracketSpacing&quot;: true
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rather than copying these from an existing project, or trying to remember what they are, you can use a snippet to add them to new projects.&lt;/p&gt;
&lt;h2&gt;How do I create a VS Code snippet from a Prettier config?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;In VS Code, launch the command palette &lt;code&gt;CMD+Shift+p&lt;/code&gt; or &lt;code&gt;CTRL+Shift+p&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;user snippets&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;New Global Snippets File&lt;/code&gt; and name it.&lt;/li&gt;
&lt;li&gt;You&apos;ll be presented with a new file with an example snippet that is commented out.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;{
	// Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and 
	// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope 
	// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is 
	// used to trigger the snippet and the body will be expanded and inserted. Possible variables are: 
	// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. 
	// Placeholders with the same ids are connected.
	// Example:
	// &quot;Print to console&quot;: {
	// 	&quot;scope&quot;: &quot;javascript,typescript&quot;,
	// 	&quot;prefix&quot;: &quot;log&quot;,
	// 	&quot;body&quot;: [
	// 		&quot;console.log(&apos;$1&apos;);&quot;,
	// 		&quot;$2&quot;
	// 	],
	// 	&quot;description&quot;: &quot;Log output to console&quot;
	// }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, you could edit the file manually and try to get the syntax right but there is a much better way. Use &lt;a href=&quot;https://snippet-generator.app/&quot;&gt;The Snippet Generator&lt;/a&gt; by &lt;a href=&quot;https://pawelgrzybek.com/&quot;&gt;Pawel Grzybek&lt;/a&gt;. Thanks, Pawel!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/snippets/snippet-generator.png&quot; alt=&quot;Snippet Generator&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The trigger is what you type to trigger the snippet. Enter &lt;code&gt;prettierconfig&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Enter a short description.&lt;/p&gt;
&lt;p&gt;Paste your prettier config settings into the &quot;Your snippet...&quot; box.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/snippets/filled-in-snippet-generator.png&quot; alt=&quot;Snippet Generator&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The snippet generator merges your inputs into a valid snippet.&lt;/p&gt;
&lt;p&gt;Copy the snippet from the right-pane and paste it into VS Code in place of the commented out example and save the file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/snippets/vscode-snippet.png&quot; alt=&quot;Snippet Generator&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now that our snippet is saved, let&apos;s put it to good use.&lt;/p&gt;
&lt;p&gt;Add a .prettierrc file to the project you&apos;re in, or delete the contents of the .prettierrc file if you&apos;ve already got one.&lt;/p&gt;
&lt;p&gt;Start typing the first few letters of your snippet&apos;s trigger, and VS Code will offer to insert the snippet.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/snippets/vscode-snippet-trigger.png&quot; alt=&quot;Snippet Generator&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Press tab to accept the suggestion, and your snippet is inserted.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/snippets/inserted-vscode-snippet.png&quot; alt=&quot;Snippet Generator&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Visual Studio Code Snippets are powerful and useful. Learn more in  the &lt;a href=&quot;https://code.visualstudio.com/docs/editor/userdefinedsnippets&quot;&gt;official Visual Studio Code docs&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Remix, SQLite, and Prisma, Oh my!</title><link>https://fullstackfish.com/posts/2021-12-31-remix-prisma-sqlite/</link><guid isPermaLink="true">https://fullstackfish.com/posts/2021-12-31-remix-prisma-sqlite/</guid><description>Experience the satisfying developer experience of working with Remix and Prisma</description><pubDate>Fri, 31 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Loaders are the part of Remix that finally made server data feel simple to me: you write a function that runs on the server, return its result, and read it in your component with one hook. Pair that with Prisma and SQLite and you get a real database with no API routes to maintain — no server to provision, no connection string to babysit, just a single file sitting on your disk. Let&apos;s build one.&lt;/p&gt;
&lt;h2&gt;Create the Remix app&lt;/h2&gt;
&lt;p&gt;Open up a terminal and create a new Remix app.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx create-remix@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&apos;ll go through a short interview of how you want to set up your app. Pick these options to follow along or modify if you&apos;re in the mood for a little adventure.&lt;/p&gt;
&lt;p&gt;Call the app whatever you like. I&apos;m calling mine &quot;run-rsp&quot;.&lt;/p&gt;
&lt;p&gt;Pick the option to deploy to the Remix App Server for now. This is easy to change later.&lt;/p&gt;
&lt;p&gt;Pick JavaScript.&lt;/p&gt;
&lt;p&gt;Decline the offer to run &lt;code&gt;npm install&lt;/code&gt; as we will be using Yarn today.&lt;/p&gt;
&lt;p&gt;Once the setup process is complete, cd into the project folder.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd run-rsp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/rsp/project-init.png&quot; alt=&quot;Project Init&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Open the project in VS Code, or whatever editor you want to use.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the dependencies.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify that everything is set up correctly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now see the template Remix app running in your browser.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/rsp/remix-is-working.png&quot; alt=&quot;Project Init&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Edit the root route and add styles&lt;/h2&gt;
&lt;p&gt;Now let&apos;s replace the default code with our own. We&apos;re going to be working primarily in the &lt;strong&gt;app&lt;/strong&gt; folder.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/rsp/app-folder.png&quot; alt=&quot;Project Init&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s open up the &lt;strong&gt;root.jsx&lt;/strong&gt; file and examine its contents.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import {
  Links,
  LiveReload,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration
} from &quot;remix&quot;;

export function meta() {
  return { title: &quot;New Remix App&quot; };
}

export default function App() {
  return (
    &amp;lt;html lang=&quot;en&quot;&amp;gt;
      &amp;lt;head&amp;gt;
        &amp;lt;meta charSet=&quot;utf-8&quot; /&amp;gt;
        &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width,initial-scale=1&quot; /&amp;gt;
        &amp;lt;Meta /&amp;gt;
        &amp;lt;Links /&amp;gt;
      &amp;lt;/head&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;Outlet /&amp;gt;
        &amp;lt;ScrollRestoration /&amp;gt;
        &amp;lt;Scripts /&amp;gt;
        {process.env.NODE_ENV === &quot;development&quot; &amp;amp;&amp;amp; &amp;lt;LiveReload /&amp;gt;}
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The content we are seeing on the home page is coming from the &lt;code&gt;&amp;lt;Outlet /&amp;gt;&lt;/code&gt; component. We&apos;ll edit that in  a second.&lt;/p&gt;
&lt;p&gt;Let&apos;s make a small change so we can see LiveReload in action.&lt;/p&gt;
&lt;p&gt;Change the title of the app in the &lt;code&gt;meta()&lt;/code&gt; function to whatever you want. Save the file and notice that the title of your app in the browser changes almost instantly. (Check the browser tab of the app or right-click and view page source)&lt;/p&gt;
&lt;p&gt;The home page content is &lt;code&gt;routes\index.jsx&lt;/code&gt;. Open the file up and delete everything. Save the file. You&apos;ll now see an error in the browser.&lt;/p&gt;
&lt;p&gt;Add the following function to fix the error.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default function Index() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;I&apos;ll soon be connected to SQLite&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may need to refresh your browser to get LiveReload working again. Once you&apos;ve done that, you&apos;ll see your heading rendered with the browser&apos;s default styling. Let&apos;s add a CSS reset and some global styles to our app so that it looks a little better.&lt;/p&gt;
&lt;p&gt;In our &lt;code&gt;root.jsx&lt;/code&gt; file, add a &lt;code&gt;links()&lt;/code&gt; function. We are already importing the &lt;code&gt;Links&lt;/code&gt; component and using it in the head of our document, so whatever we export from our &lt;code&gt;links()&lt;/code&gt; function will be used in the &lt;code&gt;Links&lt;/code&gt; component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// root.jsx
export function links() {
  return [
    {
      rel: &quot;stylesheet&quot;,
      href: &quot;https://unpkg.com/modern-css-reset@1.4.0/dist/reset.min.css&quot;
    }
  ]; 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now add a &lt;code&gt;styles&lt;/code&gt; folder in &lt;code&gt;app&lt;/code&gt; and add a file called &lt;code&gt;global.css&lt;/code&gt; with a rule or two.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/rsp/global-styles.png&quot; alt=&quot;Project Init&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Save the file. Whatever styles you&apos;ve added won&apos;t be applied yet — we haven&apos;t linked the stylesheet to the app.&lt;/p&gt;
&lt;p&gt;Go back to &lt;code&gt;root.jsx&lt;/code&gt;. Import your styles and then add another link to the array in &lt;code&gt;links()&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import globalStyles from &apos;~/styles/global.css&apos;
...
// root.jsx
export function links() {
  return [
    {
      rel: &quot;stylesheet&quot;,
      href: &quot;https://unpkg.com/modern-css-reset@1.4.0/dist/reset.min.css&quot;
    },
    {
      rel: &quot;stylesheet&quot;,
      href: globalStyles
    }
  ]; 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save your changes and you should now see your styles applied in the browser.&lt;/p&gt;
&lt;h2&gt;Prisma and SQLite setup&lt;/h2&gt;
&lt;p&gt;Stop the app and let&apos;s add some dependencies.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn add @prisma/client
yarn add -D prisma
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After they finish installing, initialize prisma with the sqlite provider.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx prisma init --datasource-provider sqlite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will add a prisma folder with a schema.prisma file. It also adds a .env file if you don&apos;t already have one.
&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/rsp/prisma-folder.png&quot; alt=&quot;Project Init&quot; /&gt;&lt;/p&gt;
&lt;p&gt;By including the datasource provider, we don&apos;t need to edit the schema.prisma file to change the default provider to sqlite, and the .env file is populated for us with a connection string to a sqlite file. Nice touch, Prisma.&lt;/p&gt;
&lt;p&gt;Add a model to your &lt;code&gt;schema.prisma&lt;/code&gt; file for Soccer Teams. Install the Prisma extension if you haven&apos;t already to format your Prisma files. Prettier may try to override Prisma&apos;s formatting, so if that&apos;s the case, right-click the file, select format document, and indicate that Prisma should format the file. Now you don&apos;t need to worry about getting your models formatted. We are going to keep things simple here just to show how everything is wired up together. Our database will be a list of soccer teams, or whatever kind of teams you like.&lt;/p&gt;
&lt;p&gt;Add the Team model to your &lt;code&gt;schema.prisma&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;generator client {
  provider = &quot;prisma-client-js&quot;
}

datasource db {
  provider = &quot;sqlite&quot;
  url      = env(&quot;DATABASE_URL&quot;)
}

model Team {
  id        String   @id @default(uuid())
  team      String
  country   String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deploy the model to your database&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx prisma db push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We should now have a dev.db file in the prisma folder next to our schema.prisma file.&lt;/p&gt;
&lt;p&gt;Launch prisma studio to see the results.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx prisma studio
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/rsp/prisma-studio.png&quot; alt=&quot;Project Init&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We can add a few teams directly in Prisma Studio. Click the &lt;strong&gt;Add record&lt;/strong&gt; button to do so.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fullstackfish.com/assets/images/posts/rsp/prisma-studio-records.png&quot; alt=&quot;Project Init&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Add a file (&lt;code&gt;app/lib/db.server.js&lt;/code&gt;) to configure our connections to the database. Note that the &quot;server&quot; in the file tells Remix to run this on the server.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { PrismaClient } from &quot;@prisma/client&quot;;

let prisma;

if (process.env.NODE_ENV === &quot;production&quot;) {
  prisma = new PrismaClient();
} else {
  if (!global.prisma) {
    global.prisma = new PrismaClient();
  }
  prisma = global.prisma;
}

export default prisma;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Load and display the data&lt;/h2&gt;
&lt;p&gt;Now let&apos;s get the teams from the database and display them in our app. We&apos;ll use a &lt;a href=&quot;https://remix.run/docs/en/main/route/loader&quot;&gt;loader function&lt;/a&gt; (this code runs on the server), which will feed our component with the &lt;code&gt;useLoaderData()&lt;/code&gt; hook.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// routes/index.js
import { useLoaderData, Link } from &apos;remix&apos;
import  prisma from &apos;~/lib/db.server&apos;

export const loader = async () =&amp;gt; {
  const data = {
    teams: await prisma.team.findMany(),
  }
  return data
}

export default function Index() {
  const { teams } = useLoaderData()

  return (
    &amp;lt;&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;h1&amp;gt;Soccer Teams around the world&amp;lt;/h1&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;ul&amp;gt;
        {teams.map((team) =&amp;gt; (
          &amp;lt;li key={team.id}&amp;gt;
            &amp;lt;h1&amp;gt;{team.team}&amp;lt;/h1&amp;gt;
            &amp;lt;p&amp;gt;{team.country}&amp;lt;/p&amp;gt;
          &amp;lt;/li&amp;gt;
        ))}
      &amp;lt;/ul&amp;gt;
    &amp;lt;/&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are using the Prisma &lt;a href=&quot;https://www.prisma.io/docs/orm/prisma-client/queries/crud&quot;&gt;&lt;code&gt;findMany()&lt;/code&gt;&lt;/a&gt; function to get all the teams in our database.&lt;/p&gt;
&lt;p&gt;That&apos;s it for now. The app we&apos;ve built isn&apos;t particularly useful as is, but you now have the building blocks to build anything you want and style it however you like.&lt;/p&gt;
&lt;p&gt;It&apos;s been said many times but remains true: there has never been a better time to be a developer. There are so many wonderful tools to choose from. The biggest challenge can be deciding what to include in your toolbelt. Remix and Prisma should be on your short list. The developer experience with both is top notch.&lt;/p&gt;
&lt;p&gt;Enjoy, and Happy New Year!&lt;/p&gt;
</content:encoded></item><item><title>nvm is for barbarians. Manage Node versions with Volta instead.</title><link>https://fullstackfish.com/posts/2021-10-05-use-volta-like-a-sophisticated-dev/</link><guid isPermaLink="true">https://fullstackfish.com/posts/2021-10-05-use-volta-like-a-sophisticated-dev/</guid><description>Volta is a dramatic improvement over nvm for managing multiple versions of Node. Learn how to set it up and use it in all your projects.</description><pubDate>Tue, 05 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There&apos;s a special kind of bug that only shows up when you switch projects: code that ran fine an hour ago suddenly explodes, and the cause is the wrong version of Node quietly sitting in your terminal. I lived like that for years with nvm — typing &lt;code&gt;nvm use&lt;/code&gt; like a password every time I opened a folder, and forgetting to half the time. Volta put a stop to it.&lt;/p&gt;
&lt;p&gt;Under the covers, all the Node version managers are doing the same thing: setting up symlinks to different versions of Node installed on your computer. The difference between them is how they manage those symlinks. If you know the correct version of Node for the project you are currently working on, you can use nvm to set it to the active version, and all is well, as long as you remember to do so. The problem here is that you need to know the correct version of Node. And if you&apos;re switching from a project that has different Node version requirements, you have to remember to switch each time you open one of those projects. As Wes Bos would say, that&apos;s no way to live your life. That&apos;s what barbarians do.&lt;/p&gt;
&lt;p&gt;Volta takes a set-it-and-forget-it approach. You &quot;pin&quot; the version of Node you need for your project and &lt;strong&gt;Volta auto-magically switches to that version anytime you are working on that project&lt;/strong&gt;, without any action on your part. You can have multiple projects open at the same time, each with their own, different versions of Node, running simultaneously.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&quot;Because Volta’s toolchain always keeps track of where you are, it makes sure the tools you use always respect the settings of the project you’re working on. This means you don’t have to worry about changing the state of your installed software when switching between projects.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;— &lt;a href=&quot;https://docs.volta.sh/guide/understanding#managing-your-toolchain&quot;&gt;Understanding Volta&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;h2&gt;How do I install Volta?&lt;/h2&gt;
&lt;p&gt;Go to the getting started page on the &lt;a href=&quot;https://docs.volta.sh/guide/getting-started&quot;&gt;Volta site&lt;/a&gt; and follow the instructions for your operating system.&lt;/p&gt;
&lt;h2&gt;How do I pin a Node version with Volta?&lt;/h2&gt;
&lt;p&gt;Pin the version of Node you want to your project by running the &lt;code&gt;pin&lt;/code&gt; command in the root folder of your project. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;volta pin node@14
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Volta adds an entry to your package.json.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
...
  &quot;volta&quot;: {
    &quot;node&quot;: &quot;14.18.0&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now anyone on your team that has Volta installed will get the benefit of having the correct version of Node pinned. If you don&apos;t have the specified version of Node, Volta will download and install it for you, then switch to that version. For any barbarians that may also be working on your project, the entry won&apos;t affect them, but will allow them to see the version of Node they&apos;ll need to use.&lt;/p&gt;
&lt;p&gt;Volta is written in Rust and is completely cross-platform, working in Windows and in all Unix shells.&lt;/p&gt;
&lt;p&gt;As web developers, we have more than enough things to worry about. Using Volta means that managing Node versions is no longer one of them.&lt;/p&gt;
</content:encoded></item><item><title>SQL Server to React in 60 Seconds With Prisma</title><link>https://fullstackfish.com/posts/2021-09-04-sql-server-to-react-with-prisma/</link><guid isPermaLink="true">https://fullstackfish.com/posts/2021-09-04-sql-server-to-react-with-prisma/</guid><description>Wire up an Azure SQL Database to your React app in 60 seconds. Experience the joy that Prisma delivers.</description><pubDate>Sat, 04 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Connecting SQL Server to React with Prisma is a straightforward and satisfying experience. This post will guide you through the process of getting your Azure SQL Server data onto a screen in a React app in less than a minute. Really. (As long as you pause your timer while &lt;code&gt;npm&lt;/code&gt; is downloading files.)&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Of course, there are a few prerequisites before you can get it connected that quickly. Prisma supports working with existing databases (which we are doing in this post) as well as defining the database in your app and then creating the database objects through migrations (which will be covered in other posts).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You must have an existing Azure SQL Server with a database that you have credentials for. You must also have your ip whitelisted in Azure.&lt;/li&gt;
&lt;li&gt;The database must have at least one table with data that we can query.&lt;/li&gt;
&lt;li&gt;You must have up-to-date versions of Node.js (12.6 or higher) and Visual Studio Code installed.&lt;/li&gt;
&lt;li&gt;You must have &lt;code&gt;create-next-app&lt;/code&gt; installed globally. (&lt;code&gt;npm global install create-next-app&lt;/code&gt; if you don&apos;t)&lt;/li&gt;
&lt;li&gt;You must have an empty folder on your hard-drive for this project.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And that&apos;s it. Let&apos;s do this.&lt;/p&gt;
&lt;h2&gt;Create the Next.js app&lt;/h2&gt;
&lt;p&gt;Note that I&apos;m using JavaScript instead of TypeScript in this post. I&apos;ll have a TypeScript version up soon. I take no sides in that holy war. I love them both. Or maybe I hate them both. It really depends on the day.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open your empty project folder in VS Code&lt;/li&gt;
&lt;li&gt;Open a terminal and run the following command to initialize a new next app:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;npx create-next-app .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The dot (.) will create the next app in the current path.&lt;/p&gt;
&lt;p&gt;Once the app is initialized, you should see a confirmation. Run the app to verify it compiles and runs correctly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;a href=&quot;http://localhost:3000&quot;&gt;http://localhost:3000&lt;/a&gt; in a browser to verify.&lt;/p&gt;
&lt;p&gt;Now stop the app and let&apos;s get some data.&lt;/p&gt;
&lt;h2&gt;Add and configure Prisma&lt;/h2&gt;
&lt;p&gt;You&apos;ll need to add the following dependencies, &lt;code&gt;prisma&lt;/code&gt; as a dev dependency and &lt;code&gt;@prisma/client&lt;/code&gt; as a regular dependency.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm i -D prisma
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;npm i @prisma/client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check your package.json file to verify that you have version 3.0.1 or greater of prisma in the dev dependencies — that&apos;s the release where Prisma&apos;s SQL Server support reached production-ready &lt;a href=&quot;https://www.prisma.io/blog/prisma-microsoft-sql-server-azure-sql-production-ga&quot;&gt;General Availability&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Initialize prisma in your app. This will add a prisma folder in root with a schema.prisma file. It will also add a .env file if you don&apos;t already have one, which you shouldn&apos;t at this point. For future reference, if you do have a .env file, this step will add a new entry into the file without harming anything else you may have in there.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx prisma init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The default connection that is configured in the schema.prisma file is for Postgres. Let&apos;s fix that.&lt;/p&gt;
&lt;p&gt;Edit the file to look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// schema.prisma
generator client {
  provider        = &quot;prisma-client-js&quot;
}

datasource db {
  provider = &quot;sqlserver&quot;
  url      = env(&quot;DATABASE_URL&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the constant DATABASE_URL that is referenced. We&apos;ll update the value of that constant in the .env file now.&lt;/p&gt;
&lt;p&gt;Set the connection string in the .env file to point to your Azure SQL Server database. I&apos;m using a username and password for this example. Explaining all the variations on connection strings is beyond the scope of this post, but I&apos;m assuming you can figure that out.&lt;/p&gt;
&lt;p&gt;My connection string, with the sensitive bits masked to protect the innocent:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DATABASE_URL=&quot;sqlserver://[SERVERNAME].database.windows.net:1433;database=[DATABASENAME];user=[USERNAME];password=[PASSWORD];encrypt=true&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Connection strings can be long, so you&apos;ll have to scroll to the right to see the whole thing.&lt;/p&gt;
&lt;h2&gt;Pull in your database schema&lt;/h2&gt;
&lt;p&gt;Now we&apos;re ready to introspect the database. This step will populate the Prisma schema based on your database schema. Make sure you have your schema.prisma file open before you run the following. It&apos;s fun to watch the magic happen.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx prisma db pull
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Boom! Our app and database are now friends.&lt;/p&gt;
&lt;p&gt;Assuming your connection string is correct and you have access to your database, you should now see a list of all the tables in the dbo schema generated as models in your schema.prisma file. This functionality is currently limited to tables in the &lt;a href=&quot;https://www.prisma.io/docs/orm/overview/databases/sql-server&quot;&gt;dbo schema&lt;/a&gt;. You won&apos;t have views or functions, or stored procs in the schema file, which is true for all databases Prisma works with, so don&apos;t get your SQL Server feelings hurt. You can still interact with those objects in your app. Stay tuned for a future post explaining how to do that.&lt;/p&gt;
&lt;p&gt;Now that we have a valid schema, let&apos;s run the command to generate the Prisma client.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx prisma generate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next is a fabulous tool that keeps your backend and frontend code safely separate but extremely easy to work with in the same project, among many other benefits.&lt;/p&gt;
&lt;p&gt;Add a lib folder in root with a prisma.js file, and copy/paste or type the following into that file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// lib/prisma.js
import { PrismaClient } from &quot;@prisma/client&quot;;

let prisma;

if (process.env.NODE_ENV === &quot;production&quot;) {
  prisma = new PrismaClient();
} else {
  if (!global.prisma) {
    global.prisma = new PrismaClient();
  }
  prisma = global.prisma;
}

export default prisma;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the best practice defined by Prisma and Next for the integration. You&apos;ll be able to import a single instance of the prisma client throughout your app (something we&apos;ll do shortly), which will handle connection pools for you and protect you from memory leaks. See &lt;a href=&quot;https://www.prisma.io/docs/orm/more/troubleshooting/nextjs&quot;&gt;Best practice for instantiating PrismaClient with Next.js&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h2&gt;Build the API route and the page&lt;/h2&gt;
&lt;p&gt;Let&apos;s define an API route that our front-end will consume.&lt;/p&gt;
&lt;p&gt;Add a file in pages/api called products.js. Replace &quot;products&quot; with whatever table you want to work with from your database. I&apos;m using Adventure Works for this example.&lt;/p&gt;
&lt;p&gt;Add this to the products.js:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// pages/api/products.js
import prisma from &quot;../../lib/prisma&quot;;

export default async function handle(req, res) {
  const products = await prisma.product.findMany();
  res.json(products);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now delete the contents of the pages/index.js file and replace it with the following as a guide. You&apos;ll need to use your React skills to create a component that incorporates your data.&lt;/p&gt;
&lt;p&gt;You&apos;ll need to replace &lt;code&gt;products&lt;/code&gt; with whatever table you want from your database, and use fields from that table instead of the ones I&apos;m using below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// pages/index.js
export const getServerSideProps = async () =&amp;gt; {
  const res = await fetch(&quot;http://localhost:3000/api/products&quot;);
  const products = await res.json();
  return {
    props: { products },
  };
};

export default function Product({ products }) {
  return (
    &amp;lt;div&amp;gt;
      {products.map((p) =&amp;gt; {
        return (
          &amp;lt;div key={p.ProductID}&amp;gt;
            &amp;lt;p&amp;gt;
              {p.Name} — ${p.ListPrice}
            &amp;lt;/p&amp;gt;
          &amp;lt;/div&amp;gt;
        );
      })}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Run it&lt;/h2&gt;
&lt;p&gt;That&apos;s it. Let&apos;s see if it worked.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;a href=&quot;http://localhost:3000&quot;&gt;http://localhost:3000&lt;/a&gt; in a browser and behold your data!&lt;/p&gt;
&lt;p&gt;Hit me up on twitter &lt;a href=&quot;https://twitter.com/jonrcrowell&quot;&gt;@jonrcrowell&lt;/a&gt; to let me know what you think or if you ran into any issues.&lt;/p&gt;
&lt;p&gt;Prisma released the SQL Server connector for production on September 7, 2021, in version 3.0.1.&lt;/p&gt;
</content:encoded></item></channel></rss>