This app is a proof of concept for a bigger project in the works for promoting local business.
The current app is built from a large dataset a collection of Business Licenses awarded in 2018 in the City of Virginia Beach, via data.vb.gov. I am currently storing a copy of the dataset in a MongoDB collection, which is the source for this app. I have a Node.js API for gathering updates to this dataset and transforming for this project.
From this dataset, this app generates a list of categories, create pages from those categories, creates both an infinite scrolling list of businesses as well as a paginated alphabetical list of businesses, and finally a page dedicated to each business. In all, there are over 2400 static pages on this site.
Maps on the site are generated from Google Maps. This site is generated via Gatsby.js and hosted on Netlify here: https://vb-business-licenses.netlify.com/
-
Node/Express/MongoDB Server
I started this project by creating a server via
Node.js
and theexpress
package to gather and transform data from the public dataset. I create a DAO to handle the transformation and insertion of the data into a MongoDB Atlas server. The server allows data to be fetched, inserted, updated, or removed, and can retrieve one or many records and sort by id, name, or category. Storing inMongoDB
allows me to use myNode
server to run chron jobs to fetch new records when the dataset is updated monthly. It also allows me to source my client from a stable environment rather than from the external open-data api. -
Gatsby Plugin Development
I wanted to build this project in React, and initially planned on using
create-react-app
and hosting a complete MERN stack application on Heroku. However, while working on other development projects at work, I discovered Gatsby, which is a static-site generator that extracts data from any number of conceivable sources, and leverages the power ofGraphQL
andReact
to produce fast, optimized JAMstack sites.But There Was a Problem with the
gatsby-source-mongodb
pluginThe
mongodb
source plugin for Gatsby was optimized for older versions. I could not connect to my cloud Atlas server and extract my data. This led me to become an open source contributer to Gatsby, where I fixed the problem I was having and also later solved another issue. I look forward to continuing my contributions to the project.After updating the plugin, I was ready to build my application.
-
React, GraphQL, and Gatsby
The site is built completely in React, using modern React features like
hooks
andcontext
. I only created two pages (an Index and 404 page, respectively), but four templates together with Gatsby'screatePage
API allows this site to generate several thousand individual pages, as well as lists of businesses. It harnesses the power ofgraphql
to match templates with the exact data I need. It's a builtiful system. The site is styled usingemotion
styled components, as well astypography.js
.Challenges during the build include the need to conditionally reference client globals like
window
anddocument
throughout the project. Also, to deploy to Netlify, page nodes cannot contain special characters like hashes and question marks, and environment variables referenced in the client must be prefixed byGATSBY_
. -
Google Maps
The project also incorporates Google maps, which is always fun and easy to include, though adding the appropriate maps Script was a challenge since even using
react-hemlet
the script wasn't loading before the compoent rendered and I received errors. For reference, here is the Map component (credit to Janosh Riebesell):
import React, { useEffect, useRef, useCallback } from 'react'
function Map({ options, onMount, className }) {
const props = { ref: useRef(), className }
const onLoad = () => {
const map = typeof window !== `undefined` ? new window.google.maps.Map(props.ref.current, options) : ''
onMount && onMount(map)
}
useEffect(() => {
if (!window.google) {
const script = document.createElement(`script`)
script.type = `text/javascript`
script.src =
`https://maps.google.com/maps/api/js?key=` +
process.env.GATSBY_GOOGLE_MAPS_API_KEY
const headScript = document.getElementsByTagName(`script`)[0]
headScript.parentNode.insertBefore(script, headScript)
script.addEventListener(`load`, onLoad)
return () => script.removeEventListener(`load`, onLoad)
} else onLoad()
})
return (
<div {...props} style={{height: `50vh`, margin: `1em 0`, borderRadius: `0.5em`, backgroundColor: `#ccc` }} />
)
}
Map.defaultProps = {
options: {
center: { lat: 36.7958618, lng: -76.1530532 },
zoom: 11,
},
}
export default props => useCallback(<Map {...props}/>, []);