-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Demo using Next.js to hide the API key on the server side.
- Loading branch information
Showing
20 changed files
with
6,401 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,9 @@ docs/_autosummary | |
|
||
# Notebook checkpoints. | ||
.ipynb_checkpoints | ||
|
||
# Node modules. | ||
node_modules | ||
|
||
# Builds | ||
build/ |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.* | ||
.yarn/* | ||
!.yarn/patches | ||
!.yarn/plugins | ||
!.yarn/releases | ||
!.yarn/versions | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
.pnpm-debug.log* | ||
|
||
# env files (can opt-in for committing if needed) | ||
.env* | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# REM React Demo | ||
|
||
This demonstration illustrates how to use [react](https://react.dev/) to call | ||
Rewiring America's Residential Electrification(REM) API. It constructs a web page | ||
that allows users to type in their address and current heating fuel and get an | ||
estimate of how much they could save by switching to a heat pump. | ||
|
||
To the user, the final result looks a lot like our [HTML/JavaScript demo](../www) | ||
and our [React Demo](../react). | ||
|
||
The difference is that this version is written in [Next.js](https://nextjs.org) | ||
and takes advantage of it's unique `'use client'` and `'use server'` annotations | ||
to make sure that part of the code runs on the server and part runs on the client. | ||
The advantage of this is that the API key we use to call the REM API is never | ||
visible on the client side. So nobody can see it simply by loading our page the | ||
way they could with the [React Demo](../react). | ||
|
||
To run this project in a development server on port 3000 of `localhost`, use | ||
T | ||
```bash | ||
npm run dev | ||
# or | ||
yarn dev | ||
# or | ||
pnpm dev | ||
# or | ||
bun dev | ||
``` | ||
|
||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. |
Binary file not shown.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
@tailwind base; | ||
@tailwind components; | ||
@tailwind utilities; | ||
|
||
:root { | ||
--background: #ffffff; | ||
--foreground: #171717; | ||
} | ||
|
||
@media (prefers-color-scheme: dark) { | ||
:root { | ||
--background: #0a0a0a; | ||
--foreground: #ededed; | ||
} | ||
} | ||
|
||
html, body { | ||
height: 100%; | ||
margin: 16pt; | ||
color: #333; | ||
font-family: 'Balsamiq Sans'; | ||
} | ||
|
||
#titlebar { | ||
margin-left: auto; | ||
margin-right: auto; | ||
width: 90%; | ||
position: relative; | ||
} | ||
|
||
#titlebar h1 { | ||
text-align: center; | ||
font-size: 36px; | ||
padding: 0px; | ||
margin: 10px; | ||
} | ||
|
||
#titlebar h2 { | ||
text-align: center; | ||
font-size: 24px; | ||
padding: 0px; | ||
margin: 10px; | ||
} | ||
|
||
#content { | ||
margin-left: auto; | ||
margin-right: auto; | ||
width: 50%; | ||
min-width: 540px; | ||
max-width: 640px; | ||
position: relative; | ||
} | ||
|
||
#mainform { | ||
margin-left: auto; | ||
margin-right: auto; | ||
width: 80%; | ||
font-size: 14pt; | ||
} | ||
|
||
#address { | ||
margin-left: auto; | ||
margin-right: auto; | ||
width: 460px; | ||
height: 2em; | ||
border: 2px solid #333; | ||
padding: 2px; | ||
margin-bottom: 1em; | ||
} | ||
|
||
#ok { | ||
width: 80px; | ||
background-color: #eee; | ||
border: 2px solid #333; | ||
} | ||
|
||
.fuel { | ||
margin: 0.25em; | ||
} | ||
|
||
#results { | ||
margin-top: 40px; | ||
} | ||
|
||
#savings { | ||
font-size: 18pt; | ||
} | ||
|
||
#addapi { | ||
margin-top: 200px; | ||
float: right; | ||
} | ||
|
||
p.label { | ||
margin-block-start: 1em; | ||
margin-block-end: 0.25em; | ||
} | ||
|
||
.wait { | ||
cursor: wait; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import type { Metadata } from "next"; | ||
import { Balsamiq_Sans } from "next/font/google"; | ||
import "./globals.css"; | ||
|
||
const balsmiqSans = Balsamiq_Sans({ | ||
variable: "--font-balsamiq-sans", | ||
subsets: ["latin"], | ||
weight: [ "400", "700" ] | ||
}) | ||
|
||
export const metadata: Metadata = { | ||
title: "REM API Demo", | ||
description: "Demonstrate using a next.js server-side function to avoid exposing API keys.", | ||
}; | ||
|
||
export default function RootLayout({ | ||
children, | ||
}: Readonly<{ | ||
children: React.ReactNode; | ||
}>) { | ||
return ( | ||
<html lang="en"> | ||
<body | ||
className={`${balsmiqSans.variable} antialiased`} | ||
> | ||
{children} | ||
</body> | ||
</html> | ||
); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
'use client'; | ||
// import Image from "next/image"; | ||
|
||
import Head from 'next/head' | ||
|
||
import { useState } from 'react'; | ||
import './globals.css'; | ||
|
||
import serverSavings from './serverSavings'; | ||
|
||
async function clientSavings(address : string, currentFuel : string) { | ||
console.log("On the client side."); | ||
|
||
const expectedSavings = await serverSavings(address, currentFuel); | ||
|
||
console.log(`Client savings: ${expectedSavings}`); | ||
|
||
return expectedSavings | ||
} | ||
|
||
function AddressForm() { | ||
// The three values we need to call the REM API are | ||
// | ||
// - the address of the home; | ||
// - the current heating fuel; | ||
// - the upgrade to be performed. | ||
// | ||
// We will make states for the first two, whose values | ||
// will be managed by a form. For the third one, we will | ||
// just use a constant value. | ||
const [address, setAddress] = useState(""); | ||
const [currentFuel, setCurrentFuel] = useState("natural_gas"); | ||
const [savings, setSavings] = useState("") | ||
const [hidden, setHidden] = useState(true) | ||
|
||
const onFuelChange = (event) => { | ||
setCurrentFuel(event.target.value) | ||
setHidden(true) | ||
} | ||
|
||
/** | ||
* Callback when the form is submitted. | ||
* | ||
* This is where we make the API call, and then, | ||
* when it returns, update the results in the DOM | ||
* based on the result. | ||
* | ||
* @param {Event} event - the change event. | ||
*/ | ||
const handleSubmit = (event) => { | ||
event.preventDefault(); | ||
|
||
const expectedSavings = clientSavings(address, currentFuel); | ||
|
||
console.log(`Handler Savings: ${expectedSavings}`) | ||
|
||
setSavings(expectedSavings) | ||
setHidden(false) | ||
}; | ||
|
||
return ( | ||
<> | ||
<div id="mainform"> | ||
<form onSubmit={handleSubmit}> | ||
<label> | ||
<p className="label"> | ||
Address: | ||
</p> | ||
<input | ||
type="text" | ||
id="address" | ||
value={address} | ||
onChange={ | ||
e => { | ||
setAddress(e.target.value) | ||
setHidden(true) | ||
} | ||
} | ||
/> | ||
</label> | ||
<label> | ||
<p className="label"> | ||
Current Heating Fuel: | ||
</p> | ||
<input type="radio" className="fuel" name="fuel" value="fuel_oil" onChange={onFuelChange} /> | ||
Fuel Oil | ||
<input type="radio" className="fuel" name="fuel" value="natural_gas" onChange={onFuelChange} defaultChecked={true} /> | ||
Natural Gas | ||
<input type="radio" className="fuel" name="fuel" value="propane" onChange={onFuelChange} /> | ||
Propane | ||
<input type="radio" className="fuel" name="fuel" value="electricity" onChange={onFuelChange} /> | ||
Electricity | ||
</label> | ||
<br/><br/> | ||
<label> | ||
<input type="submit" value="OK" id="ok"/> | ||
</label> | ||
</form> | ||
</div> | ||
<div id="results" hidden={hidden}> | ||
<p>Expected annual savings: <span id="savings">{savings}</span></p> | ||
<p>Call us at 1-800-LEC-TRIC to get your job started today!</p> | ||
</div> | ||
</> | ||
); | ||
} | ||
|
||
export default function Home() { | ||
return ( | ||
<div> | ||
<Head> | ||
<title>REM Demo with Secrets</title> | ||
</Head> | ||
<div id="titlebar"> | ||
<h1 id="title"> | ||
Electrification Nation | ||
</h1> | ||
<h2 id="subtitle">Your Heat Pump People</h2> | ||
</div> | ||
<div id="content"> | ||
<div> | ||
We're here to help you save money by electrifying your home. Enter your address and select the fuel you | ||
currently use to heat your home and we'll tell you how much you can save by having us install a heat pump! | ||
</div> | ||
<AddressForm> | ||
</AddressForm> | ||
</div> | ||
</div> | ||
); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
'use server'; | ||
|
||
import axios from "axios"; | ||
|
||
export default async function serverSavings(address : string, currentFuel: string) { | ||
|
||
console.log("On the server side.") | ||
|
||
const upgrade = 'high_eff_hp_elec_backup'; | ||
|
||
// This is the URL for the REM API. | ||
const remApiURL = "https://api.rewiringamerica.org/api/v1/rem/address"; | ||
|
||
// If you don't have an API key, please register at | ||
// https://rewiring.link/api-signup to get one. | ||
const apiKey = "INSERT_YOUR_API_KEY_HERE"; | ||
|
||
let expectedSavings = "123"; | ||
|
||
await axios | ||
.get( | ||
remApiURL, | ||
{ | ||
params: {address: address, heating_fuel: currentFuel, upgrade: upgrade}, | ||
headers: {Authorization: "Bearer " + apiKey} | ||
} | ||
) | ||
.then( | ||
(response) => { | ||
const rawSavings = -Number(response.data.fuel_results.total.delta.cost.mean.value) | ||
const roundedSavings = (Math.round(rawSavings * 100) / 100).toFixed(2); | ||
expectedSavings = "$" + roundedSavings | ||
} | ||
) | ||
|
||
return expectedSavings; | ||
} |
Oops, something went wrong.