Last active
April 26, 2022 05:36
-
-
Save dustinmyers/1a329c7abeff3f85ea35d93e26592bbf to your computer and use it in GitHub Desktop.
Example of how to use serverless functions to fetch data from a CMS for a blog site
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Blog page that fetches data using serverless function | |
import { useEffect, useState } from "react"; | |
import { useRouter } from "next/router"; | |
import ReactMarkdown from "react-markdown"; | |
import Loader from "../../components/Loader"; | |
export default function BlogPost() { | |
const router = useRouter(); | |
const [isLoading, setIsloading] = useState(false); | |
const { pid } = router.query; | |
const [blogPost, setBlogPost] = useState(null); | |
useEffect(() => { | |
async function getPost() { | |
setIsloading(true); | |
const res = await fetch(`/api/get-post-by-id?id=${pid}`); | |
const { entry } = await res.json(); | |
setIsloading(false); | |
setBlogPost(entry); | |
} | |
if (pid) { | |
getPost(); | |
} | |
}, [pid]); | |
if (isLoading) { | |
return <Loader style={{ marginTop: "64px" }} />; | |
} | |
return ( | |
<main className="post-wrapper"> | |
<h1>{blogPost?.fields.title}</h1> | |
<img | |
src={blogPost?.fields.heroImage?.fields.file.url} | |
alt={blogPost?.fields.heroImage?.fields.title} | |
/> | |
<ReactMarkdown>{blogPost?.fields.body}</ReactMarkdown> | |
<hr className="hr-divider" /> | |
<section className="donate"> | |
<h1>Subscribe to Jameson's Blog</h1> | |
<p> | |
The best way to stay up-to-date on Jameson's Journey is to subscribe | |
to his blog. When we add new blog posts you will get an email with the | |
link. And of course you can unsubscribe at anytime. | |
</p> | |
<div id="mc_embed_signup"> | |
<form | |
action="https://jamesons-journey.us19.list-manage.com/subscribe/post?u=83fd03309cf7dca617b33bcba&id=b659e015a4" | |
method="post" | |
id="mc-embedded-subscribe-form" | |
name="mc-embedded-subscribe-form" | |
className="validate" | |
target="_blank" | |
novalidate | |
> | |
<div id="mc_embed_signup_scroll"> | |
<label for="mce-EMAIL">Subscribe</label> | |
<input | |
type="email" | |
// value="" | |
name="EMAIL" | |
className="email" | |
id="mce-EMAIL" | |
placeholder="email address" | |
required | |
/> | |
<div | |
style={{ position: "absolute", left: "-5000px" }} | |
aria-hidden="true" | |
> | |
<input | |
type="text" | |
name="b_83fd03309cf7dca617b33bcba_b659e015a4" | |
tabIndex="-1" | |
value="" | |
/> | |
</div> | |
<div className="clear"> | |
<input | |
type="submit" | |
value="Subscribe" | |
name="subscribe" | |
id="mc-embedded-subscribe" | |
className="button" | |
/> | |
</div> | |
</div> | |
</form> | |
</div> | |
</section> | |
</main> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NextJS React component that calls the serverless functions | |
import { useEffect, useState } from "react"; | |
import Link from "next/link"; | |
import ReactMarkdown from "react-markdown"; | |
import Loader from "../../components/Loader"; | |
import moment from "moment"; | |
export default function BlogList(props) { | |
const [entries, setEntries] = useState([]); | |
const [isLoading, setIsLoading] = useState(false); | |
useEffect(() => { | |
setIsLoading(true); | |
async function getPosts() { | |
const res = await fetch("/api/get-posts"); | |
const { entries } = await res.json(); | |
setIsLoading(false); | |
setEntries(entries.items); | |
} | |
getPosts(); | |
}, []); | |
return ( | |
<main className="blog-posts"> | |
<h1>Jameson's Blog</h1> | |
{isLoading ? ( | |
<Loader style={{ marginTop: "32px" }} /> | |
) : ( | |
<section className="blog-entries"> | |
{entries.map(entry => ( | |
<div key={entry.sys.id}> | |
<Link href="/blog/[pid]" as={`/blog/${entry.sys.id}`}> | |
<h3 className="blog-header">{entry.fields.title}</h3> | |
</Link> | |
<p className="blog-details"> | |
{moment(entry.fields.publishDate).format("MMM Do YYYY")} •{" "} | |
{entry.fields.author.fields.name} | |
</p> | |
<ReactMarkdown>{entry.fields.description}</ReactMarkdown> | |
</div> | |
))} | |
</section> | |
)} | |
</main> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// from /api/get-post-by-id.js | |
// gets posts from Contentful for blog site | |
var contentful = require("contentful"); | |
import getConfig from "next/config"; | |
export default (req, res) => { | |
var client = contentful.createClient({ | |
space: process.env.CTF_SPACE_ID, | |
accessToken: process.env.CTF_ACCESS_TOKEN | |
}); | |
try { | |
client.getEntry(req.query.id).then(function(entry) { | |
res.status(200).json({ entry }); | |
}); | |
} catch (err) { | |
console.log(err); | |
} | |
}; | |
// from /api/get-posts.js | |
export default (req, res) => { | |
var client = contentful.createClient({ | |
space: process.env.CTF_SPACE_ID, | |
accessToken: process.env.CTF_ACCESS_TOKEN | |
}); | |
try { | |
client.getEntries({ content_type: "blogPost" }).then(function(entries) { | |
res.status(200).json({ entries }); | |
}); | |
} catch (err) { | |
console.log(err); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment