RedwoodJS

中国镜像

# Disable API/Database

Did you know you could deploy your Redwood app without an API layer or database? Maybe you have a simple static site that doesn't need any external data, or you only need to digest a simple JSON data structure that changes infrequently. So infrequently that changing the data can mean just editing a plain text file and deploying your site again.

Let's take a look at these scenarios and how you can get them working with Redwood.

# Assumptions

We assume you're deploying to Netlify in this recipe. Your mileage may vary for other providers or a custom build process.

# Remove the /api directory

Just delete the /api directory altogether and your app will still work in dev mode:

rm -rf api

You can also run yarn install to cleanup those packages that aren't used any more.

# Turn off the API build process

When it comes time to deploy, we need to let Netlify know that it shouldn't bother trying to look for any code to turn into AWS Lambda functions.

Open up netlify.toml. We're going to comment out one line:

[build]
  command = "yarn rw build"
  publish = "web/dist"
  # functions = "api/dist/functions"
[dev]
  command = "yarn rw dev"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

If you just have a static site that doesn't need any data access at all (even our simple JSON file discussed above) then you're done! Keep reading to see how you can access a local data store that we'll deploy along with the web side of our app.

# Local JSON Fetch

Let's display a graph of the weather forecast for the week of Jan 30, 2017 in Moscow, Russia. If this seems like a strangely specific scenario it's because that's the example data we can quickly get from the OpenWeather API. Get the JSON data here or copy the following and save it to a file at web/public/forecast.json:

{
  "cod": "200",
  "message": 0,
  "city": {
    "geoname_id": 524901,
    "name": "Moscow",
    "lat": 55.7522,
    "lon": 37.6156,
    "country": "RU",
    "iso2": "RU",
    "type": "city",
    "population": 0
  },
  "cnt": 7,
  "list": [
    {
      "dt": 1485766800,
      "temp": {
        "day": 262.65,
        "min": 261.41,
        "max": 262.65,
        "night": 261.41,
        "eve": 262.65,
        "morn": 262.65
      },
      "pressure": 1024.53,
      "humidity": 76,
      "weather": [
        {
          "id": 800,
          "main": "Clear",
          "description": "sky is clear",
          "icon": "01d"
        }
      ],
      "speed": 4.57,
      "deg": 225,
      "clouds": 0,
      "snow": 0.01
    },
    {
      "dt": 1485853200,
      "temp": {
        "day": 262.31,
        "min": 260.98,
        "max": 265.44,
        "night": 265.44,
        "eve": 264.18,
        "morn": 261.46
      },
      "pressure": 1018.1,
      "humidity": 91,
      "weather": [
        {
          "id": 600,
          "main": "Snow",
          "description": "light snow",
          "icon": "13d"
        }
      ],
      "speed": 4.1,
      "deg": 249,
      "clouds": 88,
      "snow": 1.44
    },
    {
      "dt": 1485939600,
      "temp": {
        "day": 270.27,
        "min": 266.9,
        "max": 270.59,
        "night": 268.06,
        "eve": 269.66,
        "morn": 266.9
      },
      "pressure": 1010.85,
      "humidity": 92,
      "weather": [
        {
          "id": 600,
          "main": "Snow",
          "description": "light snow",
          "icon": "13d"
        }
      ],
      "speed": 4.53,
      "deg": 298,
      "clouds": 64,
      "snow": 0.92
    },
    {
      "dt": 1486026000,
      "temp": {
        "day": 263.46,
        "min": 255.19,
        "max": 264.02,
        "night": 255.59,
        "eve": 259.68,
        "morn": 263.38
      },
      "pressure": 1019.32,
      "humidity": 84,
      "weather": [
        {
          "id": 800,
          "main": "Clear",
          "description": "sky is clear",
          "icon": "01d"
        }
      ],
      "speed": 3.06,
      "deg": 344,
      "clouds": 0
    },
    {
      "dt": 1486112400,
      "temp": {
        "day": 265.69,
        "min": 256.55,
        "max": 266,
        "night": 256.55,
        "eve": 260.09,
        "morn": 266
      },
      "pressure": 1012.2,
      "humidity": 0,
      "weather": [
        {
          "id": 600,
          "main": "Snow",
          "description": "light snow",
          "icon": "13d"
        }
      ],
      "speed": 7.35,
      "deg": 24,
      "clouds": 45,
      "snow": 0.21
    },
    {
      "dt": 1486198800,
      "temp": {
        "day": 259.95,
        "min": 254.73,
        "max": 259.95,
        "night": 257.13,
        "eve": 254.73,
        "morn": 257.02
      },
      "pressure": 1029.5,
      "humidity": 0,
      "weather": [
        {
          "id": 800,
          "main": "Clear",
          "description": "sky is clear",
          "icon": "01d"
        }
      ],
      "speed": 2.6,
      "deg": 331,
      "clouds": 29
    },
    {
      "dt": 1486285200,
      "temp": {
        "day": 263.13,
        "min": 259.11,
        "max": 263.13,
        "night": 262.01,
        "eve": 261.32,
        "morn": 259.11
      },
      "pressure": 1023.21,
      "humidity": 0,
      "weather": [
        {
          "id": 600,
          "main": "Snow",
          "description": "light snow",
          "icon": "13d"
        }
      ],
      "speed": 5.33,
      "deg": 234,
      "clouds": 46,
      "snow": 0.04
    }
  ]
}

Any files that you put in web/public will be served by Netlify, skipping any build process.

Next let's have a React component get that data remotely and then display it on a page. For this example we'll generate a homepage:

yarn rw generate page home /

Next we'll use the browser's builtin fetch() function to get the data and then we'll just dump it to the screen to make sure it works:

import { useState, useEffect } from 'react'

const HomePage = () => {
  const [forecast, setForecast] = useState({})

  useEffect(() => {
    fetch('/forecast.json')
      .then((response) => response.json())
      .then((json) => setForecast(json))
  }, [])

  return <div>{JSON.stringify(forecast)}</div>
}

export default HomePage

We use useState to keep track of the forecast data and useEffect to actually trigger the loading of the data when the component mounts. Now we just need a graph! Let's add chart.js for some simple graphing:

yarn workspace web add chart.js

Let's generate a sample graph:

import { useState, useEffect, useRef } from 'react'import Chart from 'chart.js'
const HomePage = () => {
  const chartRef = useRef()
  const [forecast, setForecast] = useState({})

  useEffect(() => {
    fetch('/forecast.json')
      .then((response) => response.json())
      .then((json) => setForecast(json))
  }, [])

  useEffect(() => {    new Chart(chartRef.current.getContext('2d'), {      type: 'line',      data: {        labels: ['Jan', 'Feb', 'March'],        datasets: [          {            label: 'High',            data: [86, 67, 91],          },          {            label: 'Low',            data: [45, 43, 55],          },        ],      },    })  }, [forecast])
  return <canvas ref={chartRef} />}

export default HomePage

image

If that looks good then all that's left is to transform the weather data JSON into the format that Chart.js wants. Here's the final HomePage including a couple of functions to transform our data and display the dates properly:

import { useState, useEffect, useRef } from 'react'
import Chart from 'chart.js'

const MONTHS = [
  'Jan',
  'Feb',
  'Mar',
  'Apr',
  'May',
  'Jun',
  'Jul',
  'Aug',
  'Sep',
  'Oct',
  'Nov',
  'Dec',
]

const getDates = (forecast) => {
  return forecast.list.map((entry) => {
    const date = new Date(0)
    date.setUTCSeconds(entry.dt)
    return `${MONTHS[date.getMonth()]} ${date.getDate()}`
  })
}

const getTemps = (forecast) => {
  return [
    {
      label: 'High',
      data: forecast.list.map((entry) => kelvinToFahrenheit(entry.temp.max)),
      borderColor: 'red',
      backgroundColor: 'transparent',
    },
    {
      label: 'Low',
      data: forecast.list.map((entry) => kelvinToFahrenheit(entry.temp.min)),
      borderColor: 'blue',
      backgroundColor: 'transparent',
    },
  ]
}

const kelvinToFahrenheit = (temp) => {
  return ((temp - 273.15) * 9) / 5 + 32
}

const HomePage = () => {
  const chartRef = useRef()

  const [forecast, setForecast] = useState(null)

  useEffect(() => {
    fetch('/forecast.json')
      .then((response) => response.json())
      .then((json) => setForecast(json))
  }, [])

  useEffect(() => {
    if (forecast) {
      new Chart(chartRef.current.getContext('2d'), {
        type: 'line',
        data: {
          labels: getDates(forecast),
          datasets: getTemps(forecast),
        },
      })
    }
  }, [forecast])

  return <canvas ref={chartRef} />
}

export default HomePage

If you got all of that right then you should see:

Chart screenshot

All that's left is to deploy it to the world!

# Wrapping Up

Although we think Redwood will make app developers' lives easier when they need to talk to a database or third party API, it can be used with static sites and even hybrid sites like this when you want to digest and display data, but from a static file at your own URL.