Categories
Next.js

Next.js — Dynamic API Routes and Middlewares

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at dynamic API routing and middleware with Next.js.

Dynamic API Routes

We can create dynamic API routes by following the usual file naming convention.

In the pages/api folder, we can create a file with a name that’s surrounded with brackets.

For example, we can create a pages/api/post/[pid].js and write:

export default (req, res) => {
  const {
    query: { pid },
  } = req
  res.end(`Post: ${pid}`)
}

Then we can make a request by going to http://localhost:3000/api/post/foo.

And we’ll get Post: foo displayed as a result.

We can set up our routes with common REST patterns.

For example, we can use GET api/posts/ to get a list of posts.

And GET api/posts/1 gets a single post.

Catch-All API Routes

We can create a catch all API route by using ... in the file name.

For example, we can create the pages/api/post/[...slugs].js file and write:

export default (req, res) => {
  const {
    query: { slugs },
  } = req
  res.end(`Post: ${slugs.join(', ')}`)
}

Then when we go to http://localhost:3000/api/post/foo/bar, we get:

Post: foo, bar

returned.

Optional Catch-all API Routes

Also, we can create catch-all API routes that don’t always expect URL parameters to be passed in.

To do that, we wrap our file name with 2 square brackets around its name.

So we can create a file called pages/api/post/[[...slugs]].js and add:

export default (req, res) => {
  const {
    query: { slugs },
  } = req
  res.end(`Post: ${Array.isArray(slugs) && slugs.join(', ')}`)
}

We have a file with its name wrapped with a square brackets.

In the code, we check if slugs is an array and then return the array entries joined together.

API Middlewares

API roiutes provide built in middleware to parse incoming requests.

They include:

  • req.cookies to parse cookies
  • req.query to parse query strings
  • req.body to parse request bodies.

Custom Config

Every API route can export a config object to change default configs.

For example, we can write:

export default (req, res) => {
  res.end(`Post: ${req.body}`)
}

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '1mb',
    },
  },
}

to create an API route with a size limit imposed on the request body.

Also, we can disable body parsing with:

export default (req, res) => {
  res.end(`Post: ${req.body}`)
}

export const config = {
  api: {
    bodyParser: false,
  },
}

We set bodyParser to false to disable the body parser.

The externalResolver property is a flag that tells the server that the route is being handled by an external resolver.

It’ll disable warnings for unresolved requests.

We can use it by writing:

export default (req, res) => {
  res.end(`Post: ${req.body}`)
}

export const config = {
  api: {
    externalResolver: true,
  },
}

Conclusion

We can add dynamic routes and route middleware with Next.js.

Categories
Next.js

Next.js — Styling Components

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at how to style pages with Next.js.

Built-In CSS Support

Next.js lets us import CSS from JavaScript files.

It extends import beyond the basic JavaScript usage to let us import CSS files.

We can add a global stylesheet to our project.

To do this, we just create a CSS file and import it to pages/_app.js .

So we can write:

body {
  padding: 20px 20px 60px;
  max-width: 680px;
  margin: 0 auto;
}

in styles.css .

Then in pages/_app.js , we write:

import '../styles/globals.css'
import './styles.css'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

We added the styles.css import.

The rest of the code is generated when we created the project.

If we want to import from the node_modules folder, then we must do so inside pages/_app.js .

Adding Component-Level CSS

We can add component-level CSS in addition to global CSS.

To do this, we follow the naming convention of [name].module.css , where [name] is the component’s file name.

For example, given that we have pages/yesno.js :

function YesNo({ data }) {
  return <p>{data.answer}</p>
}

export async function getServerSideProps() {
  const res = await fetch(`https://yesno.wtf/api`)
  const data = await res.json()
  return { props: { data } }
}

export default YesNo

We can create a file called yesno.module.js with:

.yesno {
  padding: 20px 20px 60px;
  max-width: 680px;
  margin: 0 auto;
}

and then we can import it into yesno.js :

import styles from './yesno.module.css';

function YesNo({ data }) {
  return <p className={styles.yesno}>{data.answer}</p>
}

export async function getServerSideProps() {
  const res = await fetch(`https://yesno.wtf/api`)
  const data = await res.json()
  return { props: { data } }
}

export default YesNo

The yesno class in CSS is converted into the yesno property when we import it.

We use it to populate the className prop to set the class names of the elements.

CSS modules are an optional features and it’s only enabled for files with the .module.css extension.

This means we need the .module part of the file name if we want to import them as we did.

We can also use link tags as always to add our CSS files.

CSS modules are automatically concatenated and minified and code-split.

Sass Support

Next.js lets us import SASS files with the .scss and .sass extensions.

We can also use them as CSS modiles with the .module.scss and .module.sass extensions.

To use SASS in our Next.js app, we’ve install the sass package by running:

npm install sass

The SCSS syntax is an extension of the CSS syntax, SASS has an indentation syntax that’s different from CSS.

Therefore, it’s easier to work with SCSS files.

Customizing Sass Options

We can change the paths of the style files to include within the next.config.js file.

For example, we can write:

const path = require('path')

module.exports = {
  sassOptions: {
    includePaths: [path.join(__dirname, 'styles')],
  },
}

to change the path of the style files we want to include.

Conclusion

We can style our Next.js app with CSS and SASS / SCSS.

Categories
Next.js

Next.js — Route Links and Dynamic Routes

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at routing with Next.js.

Dynamic Route Links

We can add links for dynamic routes.

To do this, we write:

pages/foo.js

import Links from './links';

function Foo() {
  return <div>
    <Links />
    <p>foo</p>
  </div>
}

export default Foo

pages/blog/[slug].js

import { useRouter } from 'next/router'
import Links from '../links';

const Post = () => {
  const router = useRouter()
  const { slug } = router.query

  return <div>
    <Links />
    <p>Post: {slug}</p>
  </div>
}

export default Post

pages/links.js

import Link from 'next/link'

const posts = [
  { slug: 'hello-world', title: 'hello world', id: 1 },
  { slug: 'good-news', title: 'good news', id: 2 },
  { slug: 'great-news', title: 'great news', id: 3 },
]

function Links() {
  return (
    <ul>
      <li>
        <Link href="/foo">
          <a>foo</a>
        </Link>
      </li>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href="/blog/[slug]" as={`/blog/${post.slug}`}>
            <a>{post.title}</a>
          </Link>
        </li>
      ))}
    </ul>
  )
}

export default Links

We have the [slug].js file which displays the slug that we pass in.

router.query has the URL parameters in an object.

It can have as many URL parameters as we pass in.

The useRouter hook has the route data.

We need the brackets to indicate that the file is used for dynamic routes that can accept URL parameters.

The links.js file has the links.

We use the Link component for adding the links.

The href has the URL pattern for the route.

And as has the actual path for the route to fo to.

Also, we have a static link to the foo page.

Therefore, when we click the links, we’ll go to the pages that are mapped to the routes.

We should see the slug when we click on the last 3 links.

Catch All Routes

We can add catch-all routes by adding ... before the file name.

For example, we can write:

pages/blog/[...slug].js:

import { useRouter } from 'next/router'
import Links from '../links';

const Post = () => {
  const router = useRouter()
  const { slug } = router.query

  return <div>
    <Links />
    <p>Post: {slug.join(', ')}</p>
  </div>
}

export default Post

pages/links.js

import Link from 'next/link'

const posts = [
  { slug: 'hello-world/1', title: 'hello world', id: 1 },
  { slug: 'good-news/2', title: 'good news', id: 2 },
  { slug: 'great-news/3', title: 'great news', id: 3 },
]

function Links() {
  return (
    <ul>
      <li>
        <Link href="/foo">
          <a>foo</a>
        </Link>
      </li>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href="/blog/[slug]" as={`/blog/${post.slug}`}>
            <a>{post.title}</a>
          </Link>
        </li>
      ))}
    </ul>
  )
}

export default Links

We added an extra URL parameter to each slug .

They’re rendered as arrays in [...slug].js .

Therefore, we called join to convert the array into a comma-separated string.

Optional Catch-all Routes

We can make catch-all routes optional by adding extra brackets around the file name.

For example, we can write:

pages/blog/[[...slug]].js

import { useRouter } from 'next/router'
import Links from '../links';

const Post = () => {
  const router = useRouter()
  const { slug } = router.query

  return <div>
    <Links />
    <p>Post: {slug && slug.join(', ')}</p>
  </div>
}

export default Post

We wrapped an extra set of brackets around the file name.

Now we can go to the /blog path and we’ll see the same page without anything after Post: .

Conclusion

We can add dynamic route links with Next.js.

All we have to do is to follow the file name convention and use the href and as props with the Link component.

Categories
Next.js

Next.js — Rendering Data

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at how to render data with Next.js

Static Rendering

We can create pages with dynamic routes.

To do that, we add a getStaticPaths function to our component file and export it.

Also, we’ve to add brackets around the name of the file to make it dynamic.

For example, we create a country/[name].js file to add a dynamic route to get the country data and display it with Next.js.

In country/[name].js , we write:

function Country({ country }) {
  return <p>{country.name}</p>
}

export async function getStaticPaths() {
  const res = await fetch('https://restcountries.eu/rest/v2/all')
  const countries = await res.json()
  const paths = countries.map((country) => `/country/${country.name.toLowerCase()}`)
  return { paths, fallback: false }
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://restcountries.eu/rest/v2/name/${params.name}`)
  const [country] = await res.json()
  return { props: { country } }
}

export default Country

We have the getStaticPaths method to return all the paths that we want to prerender.

All we have to do is get the data from the REST countries API and create an array of paths that we want to render.

fallback: false means that other routes that aren’t rendered should return 404.

Also, we have the getStaticProps function to get the data for the individual pre-rendered routes.

The params object has the URL parameters that we passed in.

In the Country component, we render the result that comes from getStaticProps .

Now when we go to a URL like http://localhost:3000/country/canada, we should see the country name displayed.

This will pre-render the pages statically and use them for all requests.

Static generation is good for pages that don’t change much like marketing pages, blog posts, product listings, and documentation.

Server-side Rendering

An alternative way to display render content with Next.js is with server-side rendering.

To do that, we add the getServerSideProps function to our page.

For example, we can create a pages/yesno.js file and write:

function YesNo({ data }) {
  return <p>{data.answer}</p>
}

export async function getServerSideProps() {
  const res = await fetch(`https://yesno.wtf/api`)
  const data = await res.json()
  return { props: { data } }
}

export default YesNo

We have the getServerSideProps function to get the data.

And then we get the data that’s fetched in the props of the YesNo component and render it.

Conclusion

We can render data in 2 different ways with Next.js.

One way is to pre-render routes statically. It’s faster and it’s recommended

The other way is to render routes with server-side rendering.

Categories
Next.js

Next.js — Environment Variables and Routing

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at how to expose environment variables to the browser and routing.

Exposing Environment Variables to the Browser

We can expose our environment variable to the browser if we prefix our variable with the NEXT_PUBLIC_ prefix.

For example, we can write:

NEXT_PUBLIC_API_KEY=...

Then we can use it by writing:

function Photos({ data }) {
  return <p>{JSON.stringify(data)}</p>
}

export async function getServerSideProps() {
  const headers = new Headers();
  headers.append('Authorization', process.env.NEXT_PUBLIC_API_KEY);
  const res = await fetch(`https://api.pexels.com/v1/search?query=people`, {
    method: 'GET',
    headers,
    mode: 'cors',
    cache: 'default',
  })
  const data = await res.json()
  return { props: { data } }
}

export default Photos

We access the environment variable from the process.env property.

Default Environment Variables

We can add default environment variables in the .env , .env.development , and .env.production files.

The .env.local file will override the values from those files.

And the .env*.local files should be added to .gitignore since this is where we should store secrets.

Environment Variables on Vercel

We can also store environment variables on Vercel’s environment variables section.

We still use .env , .env.development and .env.production to store default values.

The values can be pulled into .env.local by running:

vercel env pull .env.local

Test Environment Variables

Also, we can use the test environment to store environment variables for any test environment.

This is useful for setting up environments for running automated tests.

The test values will be used if the NODE_ENV is set to test .

.env.local won’t be loaded when the app is running in the test environment.

Supported Browsers and Features

Next.js supports IE11 and all modern browsers like Chrome, Firefox, and Edge.

Next.js injects polyfills for IE11 compatibility.

Therefore, we don’t have to worry about missing features on IE11.

Server-side polyfills are also injected.

This way, we can use fetch in the server-side environment without adding our own polyfills.

Next.js makes sure that modern JavaScript features like:

  • Async/await (ES2017)
  • Object Rest/Spread Properties (ES2018)
  • Dynamic import() (ES2020)
  • Optional Chaining (ES2020)
  • Nullish Coalescing (ES2020)
  • Class Fields and Static Properties

all work out of the box.

Routing

Routing is done automatically with Next.js

The folder structure of the pages folder determines the routes that are defined.

For example, if we have page/index.js is mapped to / .

And pages/posts/index.js is mapped to /posts .

We can just nested our files and they’ll be mapped to URLs with the path of the files.

Dynamic Route Segments

We surround our filename with brackets to make them dynamic.

For example, if we have pages/blog/[slug].js , then it’s mapped to the /blog/:slug URL.

If we have pages/post/[…all].js , then we can have any number of URL parameters after /post .

For example, it’ll match /post/2020/id/title .

Linking Between Pages

We can add links between pages with the Link component.

For example, we can write:

pages/links.js

import Link from 'next/link'

function Links() {
  return (
    <ul>
      <li>
        <Link href="/foo">
          <a>foo</a>
        </Link>
      </li>
      <li>
        <Link href="/bar">
          <a>bar</a>
        </Link>
      </li>
    </ul>
  )
}

export default Links

pages/foo.js

import Links from './links';

function Foo() {
  return <div>
    <Links />
    <p>foo</p>
  </div>
}

export default Foo

pages/bar.js

import Links from './links';

function Bar() {
  return <div>
    <Links />
    <p>bar</p>
  </div>
}
export default Bar

to add the Links component with the Link components to add the links to the pages.

And we use Links in the Foo and Bar components to add the routes.

Now we can click on them to jump between routes.

Conclusion

We can use environment variables in the browser environment with Next.js.

Also, we can add links to our pages with the Link component.

Routing is done by following some conventions.