sudo npm install gatsby-cli @sanity/cli -g
-- Install Gatsby + Sanity CLI globally on your computer
gatsby --version
sanity --version
- Framework for building modern websites
- Manages data
- Routing strategy
- Build process
- React is a library for rendering out data to HTML very quickly
- Gatsby uses React under the hood
- Adds on additional stuff for building a modern website
- Examples:
- wesbos.com
- Advantages
- Pushes from one page to another
- We aren't going from page to page
- Loading different pieces of the website
- Then updates the URL
- Gatsby is a static site generator
- There is a build-step in your website
- Performs what WordPress does at build time
- HTML is generated when you build the website
- This allows the website to load that much faster
- Smart about CSS
- Figures out what CSS is needed for that page
- Loads before the content
- Avoids the flash of "unstyled" content appearance
- "Rehydration"
- WesBos.com is a React application
- Gatsby will load HTML onto a page...
- Then pick it up and turn it into a fully-featured React application
- This is known as "rehydration" in the browser
- Lazy Loading
- Dont need to download the image until you are about to see it
- Compresses images, converts them to a modern format
- Routing and loading is baked into it
- Plugins
- Thousands
- Offline access
- Code Highighting
- Thousands
- What Gatsby is NOT
- CMS (WordPress, Sanity, etc.)
- No backend that you log into and update your content
- Shortcomings (because of static nature)
- Need to code things in a particular way
- Example: Dynamic pages (will cover in the course)
- Gives a score on website best practices
- By default, makes you code in a way to create a fast, performant, SEO friendly website
node_modules
- Where dependencies are installedpublic
--> Generated by Gatsby (same asgatsby clean
)src
--> Where most of Gatsby will livepages
is the only Gatsby-specific folder- Top level pages (About, Order, etc)
- The others are made up
assets
--> Fonts/imagescomponents
--> Nav, order button, React componentstemplates
--> Similar to pages, but reusable (e.g. pagination)utils
--> Smaller functions (e.g., format money)
static
--> Files that you want to be served up as HTML- TIP: Generally do not put stuffff intno static
- Dont want to reference or import anything into Gatsby by their static path
- Everything must go through Gatsby (imported through Gatsby)
- By importing it, Gatsby knows about your assets and does the cool performance stuff
- Think twice about using the static folder
- Can be dynamically generated or based on file system routing
- If you go into pages directory and make a new file...
- That will be the page that shows up when someone visits
- Need to export a function from the file
- That will be rendered as a page
- If you go into pages directory and make a new file...
- Function must be exported from the file
- All Gatsby files (except a few) are modules
- Can export things from them
- Assumes that the main export is what is rendered when someone visits the page
- Then need to start the gatsby build
- Must be in gatsby folder
- Scripts in
package.json
execute gatsby commands (build, start, clean, etc) develop
puts us into live reload mode
npm start
runsgatsby develop
- GraphQL - How we are going to source our data (remember for later)
- All Gatsby files (except a few) are modules
- Renders a 404 page by default
- Will see 404 page when loaded into production
- Creating the bar bones of all pages
- Component: Reusable pieces inside of pages
- Instead of using anchor links, we use the link tag
- Renders an anchor link
- Loaded with JavaScript needed
{ Link }
- Component exported by Gatsby- Does HTML Push State
- There are times when you want to programatically change pages
- Submit form, click specific button, etc
- Declarative: Use Link
- Imperative: Write code to define what happens as a result of a form submit
- Using react components with children components
- Typically wont see props passed as a huge payload
- Props will be destructured in the function
- Grab children directly from the function
- Place in a top level variable
- When you have a component that expects a prop, you assign prop types
- Ensures that the right things are being passed in development
- Other ways to do props (e.g., TypeScript)
- Gatsby browser + Gatsby SSR Files
- Automaticlly wrap files for us
- Create
gatsby.browser.js
IN THE ROOT FOLDER ofgatsby
- Allows us to hook into different Gatsby APIs
- Wrap page element hook wraps Components for us
- Avoid having to do this on every single page
- By Default, Gatsby does not wrap your page elements
- NOTE: Whenever you modify, you need to quit the build and restart it
- Kill the process and restart via
npm start
- Kill the process and restart via
- Ghost Fragments allow you to return two or more elements
- JSX only allows you to return one element
- Wrap with
<> </>
- Browser runs when the page is loaded and generated in the browser
- Gatsby also generates everything on the server so that it renders fast
- That is what
gatsby-ssr.js
is for
- That is what
- Can copy/paste everything from
gatsby-browser.js
intogatsby-ssr.js
- Gatsby needs to know about everything in project
- Data, pages, links, even CSS
- All CSS dumped into HTML is dumped before any of the content
- Gatsby knows what CSS that was written is the critical CSS for that page to render with a flash of unstyled text
- Instead of linking to CSS, we need to import it through Gatsby
- Import CSS files into gatsby-browser.js
- We are going to use **Styled Components**
- A way to used Scoped CSS within a React application
- Used to scope CSS and make reusable, styled components
- Set global styles in application
- On any website you will have global styles
- Background, scroll bar appearance, max-width on images etc.
- Doesnt make sense to reset them every time you make a new component
- First Step: Normalize entire application
normalize.css
- Normalizes browsers so that you start with the same foundation in each one
- This is an npm package that was already installed by Wes
- Created global styles in styles directory
- Gatsby allows you to import anything, including images and CSS
- It knows not to render it out to JS, but to render out to correct format
- Not being added to assets folder because:
- If so, they would have to go in the static folder which Gatsby doesnt know about
- Stick global styles into a variable and export them
- Gatsby renders out a base64 string before loading onto the server
- Gatsby ships the image as text (20px by 20px)
- You can scale that image up as the larger one is pulled down from the CDN
- Gatsby will take an SVG, compress it and place a random identifier on it
- Never have to do a hard refresh - literally a different filename than the original image - it will be cached
- Create typography styles
- Write CSS
- Keep in a separate file
- Create in the
src/styles
folder
- Import styled
- Define the element you want to style (e.g., .nav)
- Add styles
- Replace tags with the variable name (NavStyles)
- Any styles that we put here will not be applied to anywhere else on our website
- Any link inside of the nav element will be red
- Styles written w/o any selector are applied to the mainn element (nav)
- TIP: Make main components a style
- Use child selectors to grab things inside of them
- If you need to reuse styles multiple times, you can create a new var
- If you unencessarily nest, it gets tricky (for media queries etc)
- Helpful for when you want to redefine a single CSS attribute on an element
- PROBLEM: For properties like
transform
, other attributes will be overwritten - SOLUTION: Assign properties with variables
- For elements that you want to update, you only need to reference the variable
- Sanity
- Headless = No frontend
- No way to view the data in your website with Sanity
- Just the backend
- Front-end is what we are building (Slick Slices)
- That data is pulled in via Gatsby with a Plugin
- Steps are roughly the same as any CMS you use
- Create data --> Inject into site
- Sanity - Pros:
- Balance between up and running quickly + done for you
- Customizable
sanity init --reconfigure
- Prompt you to create a new account
- To relogin, enter
sanity login
- Default dataset is a public DB called "production"
- can have multiple datasets (for dev environments ex)
- Can delete the
yarn.lock
file if it appearspackage-lock.json
is the same thing
npm start
- Fires up a local server that will give us Sanity Studios
Sanity Studios
- UI that we use to configure CRUD operations on datasanity
- CLI
sanity start
Parts
--> pieces of JS that need to be loaded- Only need to touch when doing custom ones
- Goes to the
schema.js
file
- File is preloaded
- Take all data types and concatenate them into our schema
- Schema file - create new file
pizza.js
- Exporting an object with data in it
name
Computer nametitle
Visible title
- Second data type called
topping
- Pizza has many toppings
- "Based on this topping, show me pizzas with this topping"
- Can import Font-Awesome icons, other icons from other librarie
- Just imports that icon, not the entire library
- After creating the
topping.js
file, you need to merge it intoschema.js
-
Need a way to select what toppings to put on the pizza
-
Go into the
pizza.js
and create a new object (toppings) -
Anytime you have a websocket and reload the page, Firefox will complain
- Ignore
- Person schema (to create people)
- Involves pagination
- Settings page
- Want to be able to specify:
- Who is "slicing" pizza
- What pizza is currently by the case
- Want to be able to specify:
- Inputting cents for pizza
- Want to display actual price in the editor
- Good use case for custom input components
- We have full control over the code that runs in Sanity
- Put a veggie symbol next to the pizza name if all of the toppings are vegetarian
- Would need to go into the preview, select all toppings, loop through and select the icon if all are vegetarian
- Create a folder
components
- Create a file
PriceInput.js
- Add a boilerplate React component
- Go into schema
pizza.js
and importPriceInput.js
- NOTE: Styling does not perfectly match Sanity Studio
- Sanity Studio adds classes
- There are docs, but more trouble than its worth
- Going to make it easier
- Anytime you put a
value
into aninput
, you must also supply anonChange
handler - Within
PriceInput.js
, import a helper function calledPatchEvent, { set, unset } from 'part:@sanity/form-builder/patch-event';
- How do you take the data that lives in Sanity and get it into Gatsby?
- Use the GraphQL PlayGround
- http://localhost:8000/\_\_\_graphql
- Allows you to see "GraphQL Queries"
- Can run queries in there and see what it returns
- Use to write queries
- When we build (before deploying to internet), Gatsby collects all the data it needs to run
- Gatsby will go to sanity -- all data, relationships, images, etc. -- and will stick it into a temporary database -- it's memory
- Gatsby then allows us to query that data with GraphQL queries
- GraphQL provides queries that come standard with Gatsby
- allSite, allFile, directory, etc.
- This is not helpful for devs - more for lower-level authors
- Question: How do we surface our data from Sanity and surface it in the GraphQL explorer?
- Answer: Plugins
- These are defined in
Gatsby-config.js
- Used to specify plugins for your website
- Can also specify metadata, URls, titles etc.
- Have lesser-used ones (path-prefix) (eg wesbos.com/blog)
- Will add metadata to show how to add it + how it is sourced to GraphQL explorer
- Will also add plugins
- Create file in root of
gatsby
folder caleldgatsby-config.js
- Gatsby Config API
- Plugins make it easy to work with other packages and libraries
- To specify plugins, do so via a plugins array that live in
gatsby-config.js
- Ways to use a plugin:
- Specify name of the plugin; accept all default options
- Styled component plugin that surfaces the CSS to Gatsby
- Gatsby can then figure out the critical CSS
- If there are settings that need to be set, you pass in an object and add a
resolve
object to the plugin that you are adding
- NOTE: To add your own plugins,
npm install gatsby-plugin-styled-components
etc or whatever you want
- Save in a
.env
file - Import the
dotenv
package - By default, Sanity will surface tokens starting with GATSBY_
- To access ENV vars in Gatsby-config, you need to use
dotenv.config({ path: '.env' });
- By default, Sanity has a 'grok' API
- For GraphQL (built with Grok), you need to enable that
- Docs for Sanity GraphQL
- Command:
sanity graphql deploy production
- NOTE: Production is the name of the dataset
- Sanity API is
- Not working with it directly
- Want to move it from Sanity API to Gatsby API
- Solution: Restart Gatsby server with Ctrl + C // npm start
- Can query as much data as you want
- If you query all pizzas, you can also query allSanityPerson in the same query
- There is only one endpoint, and you can grab all of the data that you want query MyQuery { allSanityPizza { nodes { name price slug { current } } } allSanityPerson { nodes { name } } }
- Can select individual pizzas (grab the ID)
- Can be dynamic with variables
- Can only be run on a top level page
- To specify, export a query from the page
- Gatsby will identify that as a query and fetch the data during the build
- It will then pass that data to the page component
- Can not be dynamic, no variables can be passed in
- Can be run anywhere
- On pizzas page
- We want to query all of the pizzas that have a toppinng, OR - if there is no topping specified, we will query all of the pizzas
- NOTE: If you want to have variables in your query, it has to happen at a page level
- Slight limitation of Gatsby
- If you select a React component and then go to your Console and enter
$r
... - ... It will return the React component as an object
- This is helpful for digging into the props of a component
- Images make your website very slow
- The steps needed to make your site fast are often too hard for regular web devs
- Gatsby makes the progressive loading, resizing, and compression of your images much faster
- Too big
- Not compressed (file size might be the right size, but it could be 50% smaller w/ an algo)
- "Lossless" [no sacrifice on quality] vs "Lossy" compression
- Width / Height --> You want to resize to a perfect square but maybe dont have the right tools
- Poor loading performance. Can load in, but as it downloads you can see nothing
- Not the right format. Lots of different formats (PNG, JPG, WebP)
- Tell Gatsby the info that you want to know
- It will deliver to you a Gatsby image
- Has all of the info that you need
- Takes care of ratios (can be hard in CSS)
- Gives a
data-image
--> image shown while the actual image is loading- (Images are blurry for a moment while the full version is downloaded)
<picture>
element - tries to serve you a WebP version, otherwise it will serve a JPEG- Will serve multiple versions of the image depending on the size of the actual image
- You need something to process those images
- As a dev, you can add a JPEG into Gatsby, but shouldnt load it onto the website
- You need either:
- A service
- A compute
- ... That will manually process the images, make versions of them (small/large, JPEG/WebP etc.)
- To process images, there are two ways:
- Source your images
- If you have images in a directory
- Similar to Sanity
- Pipe them through Gatsby Sharp
- Runs on your computer or Netlify build
- This can take a long time
- Ex: Fresh build for Wes can take up to 20 min (has ~300 images)
- Image processing is very expensive (takes a lot of compute)
- Use Gatsby image, but with a service
- You can upload images directly or feed your image
- Images will be produced as the customer requests them
- Sanity has an image pipeline -- what we will be using
- Compatible with Gatsby Image
- Cloudinary
- Imgix
- NOTE: Gatsby image works the same way regardless of your service
- We can use a custom image element that ships with Gatsby
- Then feed all props.image data to Gatsby
- Fixed: Fixed width/height
- Fluid: Responsive. Can resize the image (suggested)
- Gatsby will only load images on demand as you scroll them into view
- Means you aren't using unecessary bandwidth - both on your server and the customer
- Go to page
pizza.js
(where we are querying the data) - In the GraphQL query, we can:
- Simply update the GraphQL query fixed(width: 200, height: 200) { ...GatsbySanityImageFixed }
- You can then pass these to an
<Img fixed={pizza.image.asset.fixed}>
- Sanity will chop the images for you
- Suppose you wanted a height of 200 & width of 600 (very wide)
- There is also functionality to ensure that the images aren't chopped in a poor way
- Sanity --> Sample Data -->
all-sample-data.gz
: All pizza toppings, slicemasters etc
- In sanity root, run
sanity dataset import ./sample-data/all-sample-data.gz production --replace
--replace
--> overwrite
- If you go to
localhost:3333
, all of the data is immediatelly loaded into Sanity
- Open up
PizzaList.js
- We are going to create a styled component called
PizzaGridStyles
- Want to create a filter that will:
- List all of the toppings
- Pizza that has that topping
- Click on each one and filter for pizzas that have that topping
- Creating a new component called
ToppingsFilter.js
- Creating dynamic pages with
gatsby-node.js
- API for controlling your sites data in the GraphQL layer
- Do things at various points during the Gatsby build cycle
graphql
--> GraphQL queriesactions
--> used for creating pages
- Store in folder
templates
- Create file
Pizza.js
- Because it is reused multiple times, it is a Class and should be capitalized
- We need just enough data in the
await graphql('');
statement to:- Render out the template
- The template itself will go off and fetch the data
- In React Dev tools, there is a prop
pageContext
- If you need to pass data from .createPage method to the template, that can be done with
context
- Displays as
pageContext
within props
- Displays as
- Can use graphql as if it were a standard page
- Interpolate variables into query
- Variables are passed into graphql query as arguments
- Here we are passing the
slug
that is specified in the context ofactions.createPage()
ingatsby-node.js
- Then, when using graphql on
Pizza.js
page, the outputs will be for the $slug variabe fromgatsby-node.js
- Query the entire pizza in gatsby-node.js
- Then pass the entire pizza as context
- You can then skip the graphql query in Pizza.js
- That is valid
- TIP: Prefers to keep actual query in page itself
- Avoids having to jump back into Gatsby-node.js
- PLUS -- you would have to kill the process each time. Allows for faster iterating
- We want name, id, image (asset, etc),
- Now rendering out the page
- Imports:
- React
- graphql
- Img from 'gatsby-image' (NOTE: Not called Img so auto import will not work)
- We are going to reuse pizza.js age, but pass it the ability to insert for a specific topping
- Ex --> Click on roasted garlic --> should see the pizzas that have roasted garlic
- We already have a page that shows what pizzas have what toppings -
pages/pizza.js
- Modify the PizzaQuery to take in an argument for what topping to filter for
- Add a filter for toppings in GraphQL
- Filters are never standard across GraphQL implementations
- We are seeing the way that Gatsby does it
- Use a regex expression
elemMatch
on the name of each topping - The query on the
pizza.js
page also takes in an argument- This arg comes from the
toppings
query in gatsby-node.js - (Same concept as the Pizza query)
- This arg comes from the
- The original query is simply enriched with a filter
- Go to
ToppingsFilter
and pass it theactiveTopping
- Can destructure
pageContext
in thePizzasPage
component
- Can destructure
- Then, in
ToppingsFilter.js
, we can then destructure that valueactiveTopping
- Display output to see that it is working
- Probable that your website has data that must be pulled in from an API
- Ex -- REST API (Tweets, posts, GitHub etc) that you need to pull in?
- Still want to reap all the benefits of server-rendered, pre-rendered, SEO-friendly etc.
https://sampleapis.com/beers
- We are going to be fetching this at build time
- How do we get this data into our Gatsby GraphQL API, query, and pull it in?
- ANSWER: Gatsby-node.js
- We are already hooking into the
createPages
API - There is also an API for Sourcing Nodes
- SOURCING: Putting the data into your Gatsby API
- NODE: Piece of data
- When we do our pizza query, we ask for the nodes (each piece of data is a node)
- We want to put data into the GraphQL API by Sourcing them
- Add an additional
export async function fetchBeersAndTurnIntoNodes
as well asexport async function sourceNodes
ingatsby-node.js
- Await the output of
fetchBeers
within thesourceNodes
hook. Wrap withPromise.all()
- Await the output of
- NOTE: Remember to pass the
params
down into thebeers
funnction fromsourceNodes
- Con of Gatsby: Feedback cycle for
gatsby-node.js
is slow (at the moment)
- Difference between writing a plugin for Gatsby and writing it in your gatsby-node.js is nothing
- If you were to open the sanity-source plugin, they are doing the same thing
- Grabbing some data, looping over data, then putting it into your GraphQL API for you
- Next: queries to pull data into your beers page
- Challenge: Write queries to pull this into our beers page
- Query all the beers and display them (if they have a photo or not, rating, etc)
- Challenge: Write queries to pull this into our beers page
- Query data
- Loop over data
- Modify data in next module for pagination
- Instead of having everything on one page, add next/prev page content
- When doing pagination, need to know the total number of people
- For this we can query the
totalCount
- For this we can query the
- Not going to be querying every person all at once
- Therefore, wont know unless asking GraphQL for the info
- We need to store the number of people to go on each page
- Usually pagination is done on demand
- Because Gatsby is pre-generated at build time, we need to know at build time how many pages there are
- We will go into
gatsby-node.js
and do things like:
- Set environment variables in .env file
- (Can also go in a settings.js file)
- NOTE: For variables to show up in Gatsby, they must be prefixed with
GATSBY_
- Other variables will not be surfaced (e.g., SANITY_TOKEN)
<p>{process.env.GATSBY_PAGE_SIZE}</p>
- The value gets pulled in from your .env file
- TIP: Gatsby is currently testing a new hot reloader w/ the var
GATSBY_HOT_LOADER=fast-refresh
- Go into
gatsby-node.js
; create a new function to turn the Slicemasters into pages
Array.from({ length: 5}).forEach((_, i) => console.log(i));
- query on slicemasters will take the incoming content from
gatsby-node.js
and modify the # of people on the page
- General use pagination
-
Highest level we get is the
Layout.js
- There are no HTML, body, etc tags - all gets added by Gatsby
- Question: How do you stick things into the ?
-
SOLUTION:
React Helmet
-
Known as a side effect - JS takes the component and update it outside of itself
-
You can take the GraphQL results and load them into the head element
-
Allows us to stick tags into React helmet
-
Will transport them out from wherever you put them into the document's head
-
Useful for HTLM attributes, SEO, OpenGraph meta tags, etc.
-
-
Example:
pizza.js
import { Helmet } from 'react-helmet';
- You can stick a helmet wherever you want in your component and then just start putting tags into it
- TIP: Create a reusable SEO component that has a set of default
- Remove Helmet from
Pizza.js
- Go into
gatsby-config.js
- We are going to us a gatsby plugin
- *When we get into server rendering / pre-building, there is a plugin needed for server-rendered, which is very important for seo
- Add
'gatsby-plugin-react-helmet',
to list of plugins
- Go into
components
folder, createSEO.js
- You can see which attributes were added by React Helmet bc they will have
data-react-helmet
attributes on them - After creating the SEO base, we need to go through every single page (everything that lives in pages as well as templates) and add the SEO
- list all the pizzas we have
- Can add pizzas to the order
- Add name, email
- Provide total
- This is a regular form in React
- When someone submits, we take this data and sends it off to an email
- To do this, we need a backend -- going to use serverless functions
- Adding to
order.js
- Loop through menu; loop over everything an
- Can use a page Query (on a page) or a Static query (no specific variable)
- Going to keep styles in a separate file
- In styles, create
OrderStyles.js
- This is for the fieldsets menu and order
- Also want to style the individual pizzas
- Create another page in Styles called
MenuItemStyles.js
- Create another page in Styles called
- When someone clicks one of the buttons, we want to add that to our order
- Going to create a custom hook with custom state to manage
- Creating
usePizza.js
in utils - In components, we are going to make a new component that holds the displaying of order items
- File
PizzaOrder.js
- File
- The state we are saving this in exists in our order page
- When we navigate away, Gatsby will unmount and remount the components - thus any state is lost
- To maintain state across page changes, you need to put that state at the highest level in Gatsby so that it doesn't unmount and remount
- There is a hook called
wrapRootElement
that allows us to wrap the actual Gatsby Root - In React dev tools, can see the Root element
- Only element on the page that stays there
- All other elements mount and remount when you switch pages
- Wrapping around the root element allow us to persist that data
- How to do that:
- We are currently sticking state into a hook at an order level. Need to do it at a root level
- Will stick all of the data into context
- Allows us to store data at a high level and access it at a lower level w/o using props
- Create a new filed called
components/OrderContext.js
- In
gatsby-browser.js
, export a functionwrapRootElement
- Once added to
gatsby-browser.js
, the wrapRootElement must also be added togatsby-ssr.js
- Now, how do we access that state at a deeper level?
- Go to
usePizza.js
hook - Calling the function
useContext(OrderContext)
, which takes in OrderContext as an argument
- Go to
- Data will disappear on a page refresh
- Can stick into localStorage and pull it back in when it mounts
- Need a unique key since there can be identical pizzas
- Use the index
- When you build Gatsby, it uses HTMl, CSS, and JS to handle custom functionality
- Doing things on the backend is challenging
- Sanity handles the backend
- What about sending email? Needs to happen on the server-side
- Gatsby works well with serverless functions (Lambda)
- Similar to running a Node server, except it runs only for the duration of code execution
- Going to be using Netlify functions (hosts serverless functions)
- Can host wherever you want
-
Make a new folder
functions
-
Make a new file
netlify.toml
in the root of your directory- Need to tell Netlify where your serverless functions live
-
Inspect
package.json
- We have been running
npm start
, which runsnpm develop
which then runsgatsby develop
- We now need to run
npm run netlify
which will runnetlify dev
- Both will run
npm run start
under the hook and set itself up properly for running serverless functions
- Both will run
- We have been running
-
From the
gatsby
folder, kill the gatsby build- Run
npm run netlify
- Will provide a URL where we can run the website off of (localhost:8888)
- Netlfy dev will proxy gatsby for us
- PROBLEM: --> Ran into an error when attempting to run
npm run netlify
:- Error: ENOENT: no such file or directory, open '/Users/kevingrimm_/Desktop/LEARN_JAVASCRIPT/GATSBY/master-gatsby/starter-files/gatsby/node_modules/netlify-redirector/lib/redirects.wasm'
- Googled around / tried different options but didn't have any luck with the command.
- SOLUTION: Follow steps in this Blog Post and execute
netlify dev
- Run
-
Make the first serverless function
- For each function we have, we want to create a new folder
- Inside of that, create a .js folder that is the same name as the folder that it is in
- Here we code the handler
-
To visit the URL:
- Creating a function placeorder with nodemailer to connect to an external email service
- Create a new folder and file
placeOrder
- NOTE: Sometimes your serverless functions are so large that you want to have your own package.json for your serverless
- Go into the folder
placeOrder
- Netlify facilitates this with
npm init
to create a package.json (press Enter through options) - Then, execute
npm i nodemailer
--> allows us to send email w/ JS
- Go into the folder
- Now flesh out
placeOrder.js
function
- Set up a new account at https://ethereal.email
- Code functionality
const transporter = nodemailer.createTransport({...})
const info = await transporter.sendMail({
(within handler)
- For the return component, can use
JSON.stringify(info)
to inspect results - View mail at Ethereal --> Messages
- NOTE: Besides swapping out credentials, there is no difference between Ethereal and a transactional email service
- How do we take the users order from the client, send it to the serverless function, and then template out the HTMl that displays what they have in their order
- Go into
usePizza.js
hook- Need to add new state
- handle errors
- handle loading state
- handle returned messages
- Need to add new state
- Back in
order.js
- Destructure the new functionality passed into the hook
- Back in
usePizza.js
- Need to create a handler for when someone clicks on the "Order" button
- Called
submitOrder
- Effect of
submitOrder
- Executes a SyntheticEvent
- Changes the text on the button to reflect the changed state in
loading
- Configure a body var which contains order, name + email info
- Create a new function to modify the
order
object (currently returns ID + pizza size)attachnameAndPrices.js
in utils
- Send the data to the server upon checkout
- Open up
placeOrder.js
and open handler- Adding in logic for required fields. Logging them for initial visiblity
- You should see that the function is running within the netlify terminal
- NOTE: If nothing shows up, kill netlify and restart with
netlify dev
- Within a for loop of required fields we are checking that each is included within the body
- If not included, return an error message
- Go to Network --> placeOrder --> Response --> message from bad results is displayed there
- Making
placeOrder.js
handler take a few more seconds- Can use a package caleld wait or write ann async function
- Add the attribute
disabled={loading}
to each fieldset - This will prevent you from editing any fields while loading is set to True
- For forms that are submittable w/o bot detection, they get submitted with spam
- Deters bots from submitting thousands per second
- Podcast on Syntax.FM - Hasty Treat - Forms, Captchas, Honeypots, Dealing with Malicious Users
- Best way to do it is to add a CAPTCHA
- Other suggestionn is to add a honeypot
- A field in your contact form that a user isn't supposed to fill out
- Cant just use the
hidden
attribute - bot is smarter than that - Give the honeypot a className of pestoSauce
- Go to
OrderStyles.js
- Add
display: none; to .pestoSauce { }
- Add
- Apparently, bots are unable to figure out if an element is visible via CSS or not (comes from ppl working on prod/client sites)
- Can also scale to 0 or 1 pixel, text-indent, etc
- Also want to make sure that you aren't throwing off screen-readers (some CSS attributes will read out loud to screen readers)
- Common thing in a site is to have a settings page
- Control everything from headlines to selecting content that you want to display on the home page
- Otherwise known as Option or One-Off pages
- Not going to be multiple of them, but still need to be stored in databases
- On the home page, we want someone to be able to go into the backenda nd then be able to select:
- Who is currently working
- What pizzas are currently available by the slice
- We want to be able to pull that into our frontend
- kill sanity & restart
- make a schema for that setting
- NOTE: Done the same way as any other data type
- Allows you to create multiple versions of a settings page
- But, we are going to restrict creating multiple versions by modifying the sidebar we have in the Sanity backend
- In Sanity, open
schemas/pizza.js
and copy over tostoreSettings.js
- Create a schema for storeSettings. Fields are an array that references another array, slicemasters
- Import storeSettings into
schema.js
and pass into the function createSchema
- This will now be visible in the Sanity backend and we can create store settings
- We want a feature that goes directly to the single Settings file that is configured in the DB with no potential for them to make any overwrites or new Stores
- In the root of your Sanity folder, create a file called
sidebar.js
- Import a custom plugin for sanity in
- Go to
sanity.json
and add the new part to the parts array
- Pulling currently slicing pizzas + slicemasters
- Downside of Gatsby:
- This data is very time sensitive
- Can change within a second and the website must be updated in a second
- Probably not best suited for running through Gatsby's GraphQL
- People that were slicing + pizzas availalbe are going to be those listed UNTIL the site is regenerated
- There is probably a way to trigger a build, but it makes more sense to fetch this client side rather than at build time
- Gatsby doesnt have an API
- There is only an API at build time
- No GraphQL API that we can hit after
- Must go directly to the source of the data - from the client side on the browser
- This is common
- So, we are going to build client side
- When you load the page, it will go and fetch the data
- In gatsby, open up
index.js
- Fetching the data
- If we cant get it from our Gatsby GraphQL, we have to get it from Sanity
- To get your Sanity endpoint:
- Quit the process and run
sanity graphql list
- This will give you a URL to your graph database
- Quit the process and run
- Difference betweenn Sanity GraphQL + Gatsby GraphQL
- Queries wont be exactly the same
- will be
allPizza
instead ofsanityallPizza
- In gatsby, create a new file in utils called
useLatestData.js
- Create a function w/ the same name
- NOTE: You need to run
sanity graphql deploy production
to add the storeSetting schema to your GrapQL database- As soon as the data changes in the Sanity backend, the updated data will be visible in the browser (next page load)
- Fetching the data
- Means you got data back and tried to turn it into an object, but it wasn't JSON to begin with
- APPROACH: Go into Network, look for the XHR request
- Tried to ping
undefined
as the URL when in reality we need to hit the GraphQL endpoint - Likely need to restart the gatsby process to take into account the additional env value
- Tried to ping
- In order to go to the graphQL endpoint from a URL, you have to allow that URL in Sanity
- Go to sanity.io --> Login --> Project --> Settings --> API
- CORS Origins --> Hosts that can connect to the project API are limited to http://localhost:3333
- Add a new origin
http://localhost:*
- Allow credentials
- Add new origin
- This will return the data to the API
- In
useLatestData.js
:- Set the state of hotSlices and slicemasters to the API results with
setHotSlices
andsetSlicemasters
- Set the state of hotSlices and slicemasters to the API results with
- Now, we want to confirm that this is being set to state
- In
index.js
, log the results and confirm that the state is updated
- In
- NOTE: When checking the console you will see the
Object
is output three times:- First output is the initial state, which has undefined values
- Second output is the updated state from the first
setState
function executed fromuseEffect
- Third output is the updated state from the second
setstate
function - No performance impact to update the state twice
- Take the Data and Pass it into components
- Going to use several grids
- Wes prefers to put all grids into separate files
- In styles, create new file Grids.js
- Because the page loads once you actually load the page, we need something to put there to indicate something is happening
- In
Grids.js
, create a new component called IemsGrid - Add another component called LoadingGrid to hold the items
- On
index.js
, add the LoadingGrid to each component (slicemasters and hotSlices)- Mapping over the count prop and creating both
<p>
and<img>
elements for the page load - Getting an image placeholder while the image loads
- Mapping over the count prop and creating both
- In
Grid.js
, adding styles for a single grid item
- Go to
index.js
- Need to amend GraphQL query to get more data
- TIP If you add
gql
in front of a graphql query (as a string), VSCode will auto format it for you + do syntax highlighting - PROBLEM: We aren't importing a graphQL library into our frontend - isnt necessary
- But, we can fake out VSCODE
- Create a tag gql and assign to
String.raw;
inuseLatestDatajs
- Good for syntax highlighting w/o importing a library
- Add more content to query in
useLatestData.js
- Happens when something goes wrong with the fetch request
- Didnt catch the error --> go back to
useLatestData
and catch the error
- Passing slicemasters and hotSlices into a component ItemGrid
- Create new component
ItemGrid.js
in components - Add a
.tilt {}
style to theGlobalStyles.js
- Backend Deployment
- Need the entire Sanity CMS to be online somewhere
- Santiy Studios has been our backend and you can host it directly on Sanity
sanity deploy
- Studio Name: Enter a hostname
- NOTE: You need a
static
folder in Sanity for this to work
- You can also host it yourself
sanity
-->dist
- If you wanted to host it yourself, you can use these files
- Just add the domain name to the allowed API routes
- Wes suggests hosting on Sanity
- Remember, Gatsby is just a static site generator
- To generate the JS, execute
npm run build
, which will rungatsby build
- Can take anywhere from 15-20S up to 15 minutes
- If you have images that need to be resized on build time, that is what causes a lot of time
- (Wes' personal site takes ~15 min to build w/ 500 images)
- Sanity or any other image hosting service does image resizing on their side
- Gatsby --> public will contain all of the pages that you need
- There are a few options for hosting (to cover next)
- Netflify is the most popular option
- Takes care of making the website fast to load + hosting
- Will also do the build for you
- Can be triggered from (1) changes in data to sanity (2) changes to Git repo
- Also host serverless functions
- Create an account on Netlify
- Go to New site from Git
- Connect to Git provider
- Add site to GitHub
- (You can either put the Gatsby folder or Gatsby + Sanity)
- Initialize git with
git init
- Add
.gitignore
contents (can drop in generic contents from Wes' repo) - Push contents
- Add site to GitHub
- You can allow all access to Git repos or a single one
- Select Branch to deploy
- Build command:
npm run build
- Publish directory:
gatsby/public
- Won't work immediately
- Sanity will look in main repo for your site - not in the
gastby
subdirectory- Option didn't seem to be available when deploying the site
- Now go back to Site settings --> Build & deploy --> edit settings
- Base directory: Directory to change to before starting a build. This is where we will look for package.json/.nvmrc/etc.
- Set to
gastby
- Set to
- Publish directory: Set to
gatsby/ public
- Base directory: Directory to change to before starting a build. This is where we will look for package.json/.nvmrc/etc.
- Now, go back to Site Overview --> Production deploys
- TIP: If you are ever having trouble with your deploy, choose Clear cache and deploy site
- Deploy site will just use the currently cached items
- TIP: If you are ever having trouble with your deploy, choose Clear cache and deploy site
- This will run the same process that we did
- Our site won't load properly because our environment variables are not in Git
- Go to Deploy Settings --> Environment --> Environment variables
- Move variables into page
GATSBY_HOT_RELOADER
isnt necessary -- only for developmentGATSBY_SERVERLESS_BASE
is no longer going to belocahost
- Instead, you can use a relative path
- *This is fine because Netliffy always runs the site on a new domain / sub domain
- Double check to remove any
console.log
statements for a production website
- Because it is now hosted on Netlify, Sanity is not going to allow
fetch
requests - Login to
Sanity.io
--> go to project --> API --> Add new CORS origin- Add the domain name for your site
- You can either put the entire domain name or `*.