40
loading...
This website collects cookies to deliver better user experience
pages
directory, create a new folder called api
. Within that, create a new file - I called it search.ts
. NextJS treats any file within the pages/api
directory as an API endpoint, rather than a page.// pages/api/search.ts
import { NextApiRequest, NextApiResponse } from 'next'
type Data = {
results: string[],
}
export default (req: NextApiRequest, res: NextApiResponse<Data>) => {
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ results: ['post1', 'post2'] }))
}
cache
, that sits in the root directory, and where I've saved this script.
// cache/cache.js
import fs from 'fs'
import { cachedPostData } from '@/lib/utils'
// First step
const blogContent = await cachedPostData('blog')
// Second step
function createBlogCache(filename) {
fs.writeFile(`./cache/${filename}.js`, blogContent, function (err) {
if (err) {
console.log(err)
}
console.log('Blog cache file written')
})
}
createBlogCache('blog')
cachedPostData
function however you think works best for your purpose, but if you're curious, this is what I've done for now. I already use the getAllPostsWithFrontMatter()
function elsewhere in the setup of my NextJS blog (check out this blog post for more info , so I reused this in my newly created cachedPostData()
function.// lib/utils.ts
export async function getAllPostsWithFrontMatter(dataType: string) {
const files = fs.readdirSync(path.join(root, 'data', dataType))
// @ts-ignore
return files.reduce((allPosts, postSlug) => {
const source = fs.readFileSync(path.join(root, 'data', dataType, postSlug), 'utf8')
const { data } = matter(source)
return [
{
frontMatter: data,
slug: postSlug.replace('.md', ''),
},
...allPosts,
]
}, [])
}
export async function cachedPostData(dataType: string) {
const posts = await getAllPostsWithFrontMatter(dataType)
return `export const cachedPosts = ${JSON.stringify(posts)}`
}
next.config.js
. The changes I made were:topLevelAwait
which enables modules to act as async functions. This is still an experimental function at the time of writing in Webpack.next build
and outputs the result to .next/server/queue.js
. This allows us to run the caching script with node .next/server/cache.js
.
module.exports = {
// ...
webpack: (config, { isServer }) => {
// Needed if your cache script is asynchronous
config.experiments = {
topLevelAwait: true,
}
if (isServer) {
return {
...config,
// This is what allows us to add a node script via NextJS's server
entry() {
return config.entry().then((entry) => {
return Object.assign({}, entry, {
cache: './cache/cache.js',
})
})
},
}
}
return config
},
// ...
}
git commit
, read on.package.json
file to actually define the script I want to run on pre-commit (rather than having it hidden away in the .husky
directory). What's then needed is to ensure the husky pre-commit file calls this newly defined pre-commit
command.// package.json
"scripts": {
// ...
"cache-posts": "node .next/server/cache.js",
"pre-commit": "yarn cache-posts && git add cache/blog.js"
},
// Also amend .husky/pre-commit to call pre-commit
npm run pre-commit
pages/api/search.ts
, we now need to amend our API to actually read our cache, and filter out the relevant blog post(s) that match a user's search query.blogPosts
variable, calling it from the saved cache.q
, I defined my results by saying, "If a query is present, filter through my blogPosts
and check whether there's any word(s) in the post title that matches the query. If no user query is present, just give me back all the blog posts".
import { NextApiRequest, NextApiResponse } from 'next'
import { cachedPosts } from '../../cache/blog'
import { CachedPost } from 'types'
type Data = {
results: string[]
}
const blogPosts = cachedPosts as CachedPost[]
export default (req: NextApiRequest, res: NextApiResponse<Data>) => {
const results = req.query.q
? blogPosts.filter((post) => post.frontMatter.title.toLowerCase().includes(req.query.q.toString()))
: blogPosts
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ results }))
}
CachePost
type looks like. You can refer to my previous blog post on how I set up my NextJS blog to get deeper into the weeds on the rest of my types.export type CachedPost = {
frontMatter: BlogFrontMatter
slug: string
}
/api/search?q=${query}
.