Thanks!

@saltnburnem

#CommitYourCode | #DeveloperCommunity

🛸

Commit Your Code

#CommitYourCode 

@saltnburnem

🛸

#CommitYourCode | #DeveloperCommunity

#DeveloperCommunity

@saltnburnem

🛸

#CommitYourCode | #DeveloperCommunity

Chris DeMars

@saltnburnem

        @saltnburnem | 🦋 @chrisdemars.net

Senior Developer Advocate

REDACTED

REDACTED

🛸

#CommitYourCode | #DeveloperCommunity

Chris DeMars

@saltnburnem

        @saltnburnem | 🦋 @chrisdemars.net

Senior Developer Advocate

🛸

#CommitYourCode | #DeveloperCommunity

Close Encounters

@saltnburnem

#MichiganDevFest | #DeveloperCommunity

of the

Data Kind

Exploring UFO Sightings with MongoDB and NextJS

@saltnburnem

First

#CommitYourCode | #DeveloperCommunity

@saltnburnem

UFO/UAP History

Sightings & Abductions

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

July 1947

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Betty & Barney Hill (1961)

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Betty & Barney Hill

First Documented Abduction Case

#CommitYourCode | #DeveloperCommunity

@saltnburnem

March 20th, 1966

Dexter, MI

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Tech Time!

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

The React Framework for the Web

Built-in Optimizations

Dynamic HTML Streaming

React Server Components

Route Handlers

Server Actions

npx create-next-app@latest

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

The world's most popular document database

Document Model

Flexible Schema

Distributed and Resilient

 

Horizontally Scale

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

A cloud for your entire journey

GPU Droplets: Simple, powerful virtual machines for AI/ML

Managed MongoDB, Kafka, MySQL, PostgreSQL

Fully-managed PaaS solution

Spaces object storage and Volumes block storage 

DigitalOcean Kubernetes a.k.a. DOKS

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

👽

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Install the MongoDB integration into your Next app!

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Install the MongoDB integration into your Next app!

npx create-next-app --example with-mongodb close-encounters

#CommitYourCode | #DeveloperCommunity

@saltnburnem

npx create-next-app --example with-mongodb close-encounters
cd close-encounters

npm install

npm run dev

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Compass is a free interactive tool for querying, optimizing, and analyzing your MongoDB data. Get key insights, drag and drop to build pipelines, and more.

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Components

Pages

#CommitYourCode | #DeveloperCommunity

Types

@saltnburnem

Pages - index.tsx

import Head from "next/head";
import clientPromise from "../lib/mongodb";
import type { InferGetServerSidePropsType, GetServerSideProps } from "next";
import Container from "@mui/material/Container";
import Box from "@mui/material/Box";
import Header from "../components/Header/Header";
import EncounterCard from "../components/EncounterCard/EncounterCard";
import { useState } from "react";
import { IndexProps } from "../types";
import { Encounter } from "../types/encounter";
import { SearchBar } from "../components/SearchBar/SearchBar";

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Pages - index.tsx

export const getServerSideProps: GetServerSideProps<IndexProps> = async () => {
  try {
    await clientPromise;
    const client = await clientPromise;
    const db = client.db("close-encounters");
    const encounters = await db
      .collection("encounters")
      .find({})
      .limit(30)
      .toArray();

    return {
      props: {
        isConnected: true,
        encounters: JSON.parse(JSON.stringify(encounters)),
      },
    };
  } catch (e) {
    console.error(e);
    return {
      props: { isConnected: false, encounters: [] },
    };
  }
};

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Pages - index.tsx

export default function Home ({
  encounters,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  const [search, setSearch] = useState("");

  const filterEncounters = (encounter: Encounter) => {
    return (
      encounter.name.toLowerCase().includes(search.toLowerCase()) ||
      encounter.location.toLowerCase().includes(search.toLowerCase())
    );
  };

  const filteredEncounters = encounters.filter(filterEncounters);

  return (
    <>
      <Head>
        <title>UFO Encounters and Sightings</title>
        <meta
          name="description"
          content="A collection of UFO, UAP, and USO sightings and encounters."
        />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Header />
      <Container maxWidth="lg">
        <SearchBar setSearch={setSearch} />
        <main>
          <Box display="grid" gridTemplateColumns="repeat(3, 1fr)" gap={4}>
            <EncounterCard encounters={filteredEncounters} />
          </Box>
        </main>
      </Container>
      <footer>
        <a
          href="https://www.digitalocean.com/"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{" "}
          <img
            src="/do-blue-h-logo.png"
            alt="DigitalOcean Logo"
            className="logo"
          />
        </a>
      </footer>
    </>
  );
}

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Components - Header.tsx

import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Components - Header.tsx

import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
const Header = () => {
  return (
    <Box sx={{ flexGrow: 1 }}>
      <AppBar position="static" sx={{ backgroundColor: '#00ff60' }}>
        <Toolbar variant="dense">
          <Typography variant="h6" component="div" sx={{ flexGrow: 1, color: 'black' }}>
            UFO Encounters & Abductions
          </Typography>
        </Toolbar>
      </AppBar>
    </Box>
  );
}

export default Header;

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Components - Header.tsx

const Header = () => {
  return (
    <Box sx={{ flexGrow: 1 }}>
      <AppBar position="static" sx={{ backgroundColor: '#00ff60' }}>
        <Toolbar variant="dense">
          <Typography variant="h6" component="div" sx={{ flexGrow: 1, color: 'black' }}>
            UFO Encounters & Abductions
          </Typography>
        </Toolbar>
      </AppBar>
    </Box>
  );
}

export default Header;

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Components - SearchBar.tsx

import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import { Dispatch, SetStateAction } from "react";

export const SearchBar = ({
  setSearch,
}: {
  setSearch: Dispatch<SetStateAction<string>>;
}) => {
  return (
    <Box
      component="form"
      sx={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        flexDirection: "column",
        paddingTop: "5rem",
        "& > :not(style)": { m: 1, width: "50ch", fontWeight: "bold" },
      }}
      noValidate
      autoComplete="off"
    >
      <TextField
        onChange={(e) => setSearch(e.target.value)}
        id="outlined-basic"
        label="Search for UFO Sightings"
        variant="outlined"
        aria-label="Search for UFO Sightings"
      />

      <Typography variant="body2" color="text.secondary">
        *This is a list of some sample data of UFO/UAPUSO abductions and
        sightings in the United States. Search for an encounter by name or
        location.
      </Typography>
    </Box>
  );
};

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Components - SearchBar.tsx

import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import { Dispatch, SetStateAction } from "react";

export const SearchBar = ({
  setSearch,
}: {
  setSearch: Dispatch<SetStateAction<string>>;
}) => {
  return (
    <Box
      component="form"
      sx={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        flexDirection: "column",
        paddingTop: "5rem",
        "& > :not(style)": { m: 1, width: "50ch", fontWeight: "bold" },
      }}
      noValidate
      autoComplete="off"
    >
      <TextField
        onChange={(e) => setSearch(e.target.value)}
        id="outlined-basic"
        label="Search for UFO Sightings"
        variant="outlined"
        aria-label="Search for UFO Sightings"
      />

      <Typography variant="body2" color="text.secondary">
        *This is a list of some sample data of UFO/UAPUSO abductions and
        sightings in the United States. Search for an encounter by name or
        location.
      </Typography>
    </Box>
  );
};

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Components - EncounterCard.tsx

import * as React from "react";
import Card from "@mui/material/Card";
import CardActions from "@mui/material/CardActions";
import CardContent from "@mui/material/CardContent";
import CardMedia from "@mui/material/CardMedia";
import Typography from "@mui/material/Typography";
import Link from "@mui/material/Link";
import { EncounterProps } from "../../types/encounter";

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Set up some Types & Interfaces!

export type IndexProps = {
  isConnected: boolean;
  encounters: Encounter[];
};
export type Encounter = {
  _id: string;
  name: string;
  location: string;
  close_encounter_level: string;
  extraterrestrial_type: string;
  image: string;
  url: string;
};

export interface EncounterProps {
  encounters: Encounter[];
}

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Components - EncounterCard.tsx

export type IndexProps = {
  isConnected: boolean;
  encounters: Encounter[];
};
export type Encounter = {
  _id: string;
  name: string;
  location: string;
  close_encounter_level: string;
  extraterrestrial_type: string;
  image: string;
  url: string;
};

export interface EncounterProps {
  encounters: Encounter[];
}

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

#CommitYourCode | #DeveloperCommunity

@saltnburnem

What did we learn?

NextJS

UFO History

Mongo DB/Compass

DigitalOcean

Storing Data and Deploying in the Cloud

#CommitYourCode | #DeveloperCommunity

@saltnburnem

Thanks!

#CommitYourCode | #DeveloperCommunity