Skip to content

Commit

Permalink
chore(functions): added functions examples to examples folder. (#31806)
Browse files Browse the repository at this point in the history
* chore: added functions examples to examples folder.

* chore: format

* Fix linting

Co-authored-by: gatsbybot <[email protected]>
Co-authored-by: Sidhartha Chatterjee <[email protected]>
  • Loading branch information
3 people authored Jun 7, 2021
1 parent 72d795c commit ce8e39d
Show file tree
Hide file tree
Showing 74 changed files with 2,280 additions and 0 deletions.
67 changes: 67 additions & 0 deletions examples/functions-airtable-form/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<p align="center">
<a href="https://www.gatsbyjs.com/?utm_source=starter&utm_medium=readme&utm_campaign=gatsby-functions-beta">
<img alt="Gatsby" src="https://www.gatsbyjs.com/Gatsby-Monogram.svg" width="60" />
</a>
</p>
<h1 align="center">
Gatsby Functions Airtable Form Example
</h1>

## 🚀 Quick start

1. Setup Airtable

Create a new base named `Submissions` and create a table with three columns, "Name", "Email", and "Message".

2. **Get Airtable Credentials.**

There are **2** environment variable you'll need to add your project to properly run the example:

- `AIRTABLE_KEY`: Get Airtable API Key. [Airtable Docs](https://support.airtable.com/hc/en-us/articles/219046777-How-do-I-get-my-API-key-)
- `AIRTABLE_DB`: Get the ID for the "Submissions" Base in interactive Airtable API docs. [Airtable Docs](https://airtable.com/api)

You'll want to add these as environment variables when deploying to Gatsby Cloud. Don't forget to add them to the Preview variables if you plan to add a CMS preview integration.

3. **Start developing.**

To get started, run `yarn` to add all necessary packages.

When developing locally, you'll want to include the ENV variables in your `.env.development`. Read more about how Gatsby handles `.env` files and environment variables in the [Gatbsy Docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/)

```shell
cd airtable-form
yarn
yarn run develop
```

4. **Open the code and start customizing!**

Your site is now running at http://localhost:8000! You can use the UI on the index page to test the functions or directly access them at http://localhost:8000/api/airtable

For this route, hitting the route with a POST request with the following body should submit a form response to your Airtable base:

```json
{
"name": "Sample Name",
"email": "[email protected]",
"message": "Hello, World!"
}
```

Edit `src/pages/index.js` to see your site update in real-time!

5. **Deploy**

You can deploy this example on Gatsby Cloud by copying the example into a new repo and [connecting that to Gatsby Cloud](https://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/deploying-to-gatsby-cloud/#set-up-an-existing-gatsby-site).

<!--- Working on improving deploy now to use subdirectories
4. **Deploy**
You can directly deploy this example by using the Deploy button below and select the directory for the Airtable example. Otherwise, fork this repo and create your own repo and [connect that to Gatsby Cloud](https://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/deploying-to-gatsby-cloud/#set-up-an-existing-gatsby-site).
[<img src="https://www.gatsbyjs.com/deploynow.svg">](https://www.gatsbyjs.com/dashboard/deploynow?url=https://github.com/gatsbyjs/gatsby-functions-beta/)
[<img src="https://www.gatsbyjs.com/deploynow.svg">](https://www.gatsbyjs.com/dashboard/deploynow?url=https://github.com/gatsbyjs/gatsby-functions-beta/tree/main/examples/airtable-form)
-->
9 changes: 9 additions & 0 deletions examples/functions-airtable-form/gatsby-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
flags: {
FUNCTIONS: true,
},
siteMetadata: {
title: "Airtable Form",
},
plugins: ["gatsby-plugin-gatsby-cloud"],
}
24 changes: 24 additions & 0 deletions examples/functions-airtable-form/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "airtable-form",
"version": "1.0.0",
"private": true,
"description": "Airtable Form",
"author": "Joel Smith",
"keywords": [
"gatsby"
],
"scripts": {
"develop": "gatsby develop",
"start": "gatsby develop",
"build": "gatsby build",
"serve": "gatsby serve",
"clean": "gatsby clean"
},
"dependencies": {
"airtable": "^0.10.1",
"gatsby": "^3.4.0",
"gatsby-plugin-gatsby-cloud": "^2.3.0",
"react": "^17.0.1",
"react-dom": "^17.0.1"
}
}
51 changes: 51 additions & 0 deletions examples/functions-airtable-form/src/api/airtable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const Airtable = require("airtable")

Airtable.configure({
endpointUrl: "https://api.airtable.com",
//Your API Key from Airtable
apiKey: process.env.AIRTABLE_KEY,
})

// Your Table ID from Airtable
const db = Airtable.base(process.env.AIRTABLE_DB)

const handler = (req, res) => {
try {
if (req.method !== "POST") {
return res.status(404).json({ message: "This endpoint requires a POST" })
}

const data = req.body

if (!data) {
return res.status(500).json({ error: "There isn't any data." })
}

db("Submissions").create(
[
{
fields: {
Name: data.name,
Email: data.email,
Message: data.message,
},
},
],
(err, records) => {
if (err) {
res.json({
message: "Error adding record to Airtable.",
error: err.message,
})
} else {
res.json({ message: `Successfully submitted message` })
}
}
)
} catch (err) {
console.log(err)
res.json({ message: "There has been a big error.", error: err })
}
}

module.exports = handler
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions examples/functions-airtable-form/src/pages/404.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as React from "react"
import { Link } from "gatsby"

// styles
const pageStyles = {
color: "#232129",
padding: "96px",
fontFamily: "-apple-system, Roboto, sans-serif, serif",
}
const headingStyles = {
marginTop: 0,
marginBottom: 64,
maxWidth: 320,
}

const paragraphStyles = {
marginBottom: 48,
}
const codeStyles = {
color: "#8A6534",
padding: 4,
backgroundColor: "#FFF4DB",
fontSize: "1.25rem",
borderRadius: 4,
}

// markup
const NotFoundPage = () => {
return (
<main style={pageStyles}>
<title>Not found</title>
<h1 style={headingStyles}>Page not found</h1>
<p style={paragraphStyles}>
Sorry{" "}
<span role="img" aria-label="Pensive emoji">
😔
</span>{" "}
we couldn’t find what you were looking for.
<br />
{process.env.NODE_ENV === "development" ? (
<>
<br />
Try creating a page in <code style={codeStyles}>src/pages/</code>.
<br />
</>
) : null}
<br />
<Link to="/">Go home</Link>.
</p>
</main>
)
}

export default NotFoundPage
36 changes: 36 additions & 0 deletions examples/functions-airtable-form/src/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from "react"

export default function AirtableUI() {
return (
<form action="/api/airtable" method="POST">
<h2 style={{ marginBottom: `16px` }}>Add person to Airtable</h2>
<div style={{ marginBottom: `8px` }}>
<label style={{ display: `block`, marginBottom: `4px` }} htmlFor="name">
Name:
</label>
<input name="name" id="name" />
</div>
<div style={{ marginBottom: `8px` }}>
<label
style={{ display: `block`, marginBottom: `4px` }}
htmlFor="email"
>
Email:
</label>
<input name="email" id="email" type="email" />
</div>
<div style={{ marginBottom: `24px` }}>
<label
style={{ display: `block`, marginBottom: `4px` }}
htmlFor="message"
>
Message:
</label>
<textarea name="message" id="message" />
</div>
<div>
<button>Submit new person</button>
</div>
</form>
)
}
6 changes: 6 additions & 0 deletions examples/functions-auth0/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
JWT_ISSUER=https://domain-from-auth0-application.com
JWT_AUDIENCE=https://api/tv-shows
GATSBY_AUTH0_DOMAIN=domain-from-auth0-application.com
GATSBY_AUTH0_CLIENT_ID=Copy from Auth0 application
GATSBY_AUTH0_AUDIENCE=https://api/tv-shows
GATSBY_AUTH0_SCOPE=openid profile read:shows
14 changes: 14 additions & 0 deletions examples/functions-auth0/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
The BSD Zero Clause License (0BSD)

Copyright (c) 2020 Gatsby Inc.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
55 changes: 55 additions & 0 deletions examples/functions-auth0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# gatsby-auth0-functions

This example shows how to use [@serverless-jwt](https://github.com/sandrinodimattia/serverless-jwt) with Gatsby and Gatsby Hosted Functions.

## Inspiration

This code was inspired by this [article](https://sandrino.dev/blog/securing-netlify-functions-with-serverless-jwt) and [repo](https://github.com/sandrinodimattia/serverless-jwt).

## 🚀 Quick start

To test this Example locally you'll need to:

1. Create an application in [Auth0](https://auth0.com/) of type "Single Page Application" and configure `http://localhost:8000` as the Allowed Callback URL, Allowed Logout URL, Allowed Web Origins and Allowed CORS.
2. Create an API in Auth0 (e.g. with the name of `tv-shows` and identifier of `https://api/tv-shows`) and create a permission for that API (e.g. `read:shows`)
3. Rename the `.env-template` file to `.env.development` and update all of the settings there with the domain and clientId from the appplication you made in Auth0.
4. Run `yarn run start` which will run the Gatsby application and the Gatsby functions.

## How does example this work?

### Gatsby

The Gatsby application uses [@auth0/auth0-react](https://github.com/auth0/auth0-react) to authenticate the user. Once the user is authenticated, the Gatsby application will receive an `id_token` and `access_token` from Auth0;

The `access_token` is then provided to our Functions to authenticate the request.

### Gatsby Functions

In the Gatsby Functions we use [@serverless-jwt/jwt-verifier](https://github.com/sandrinodimattia/serverless-jwt/tree/master/packages/jwt-verifier) to secure our functions.

The `JwtVerifier` serves as a way to verify your token. If the token is not valid, the we return an error to the client. If it is valid, it will expose all of the claims to the current function and you'll have the guarantee that the request is authenticated.

```js
const {
JwtVerifier,
getTokenFromHeader,
} = require("@serverless-jwt/jwt-verifier")

const jwt = new JwtVerifier({
issuer: process.env.JWT_ISSUER,
audience: process.env.JWT_AUDIENCE,
})

const shows = async (req, res) => {
const scope = "read:shows"
const token = getTokenFromHeader(req.get("authorization"))
const claims = await jwt.verifyAccessToken(token)

if (!claims || !claims.scope || claims.scope.indexOf(scope) === -1) {
return res.status(403).json({
error: "access_denied",
error_description: `Token does not contain the required '${scope}' scope`,
})
}
}
```
22 changes: 22 additions & 0 deletions examples/functions-auth0/gatsby-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react"
import { navigate } from "gatsby"
import { Auth0Provider } from "@auth0/auth0-react"

import "./src/styles/site.css"

const onRedirectCallback = appState => navigate(appState?.returnTo || "/")

export const wrapRootElement = ({ element }) => {
return (
<Auth0Provider
domain={process.env.GATSBY_AUTH0_DOMAIN}
clientId={process.env.GATSBY_AUTH0_CLIENT_ID}
audience={process.env.GATSBY_AUTH0_AUDIENCE}
scope={process.env.GATSBY_AUTH0_SCOPE}
redirectUri={window.location.origin}
onRedirectCallback={onRedirectCallback}
>
{element}
</Auth0Provider>
)
}
20 changes: 20 additions & 0 deletions examples/functions-auth0/gatsby-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const activeEnv =
process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || "development"

require("dotenv").config({
path: `.env.${activeEnv}`,
})

module.exports = {
flags: {
FUNCTIONS: true,
},
plugins: [
{
resolve: "gatsby-plugin-create-client-paths",
options: { prefixes: ["/*"] },
},
`gatsby-plugin-postcss`,
`gatsby-plugin-gatsby-cloud`,
],
}
27 changes: 27 additions & 0 deletions examples/functions-auth0/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "gatsby-auth0-functions",
"private": true,
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"start": "npm run develop",
"serve": "gatsby serve",
"clean": "gatsby clean"
},
"dependencies": {
"@auth0/auth0-react": "^1.0.0",
"@serverless-jwt/jwt-verifier": "^0.2.1",
"dotenv": "^8.2.0",
"gatsby": "^3.4.0-next.6",
"gatsby-plugin-gatsby-cloud": "^2.3.0",
"node-fetch": "^2.6.1",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"gatsby-plugin-create-client-paths": "^3.3.0",
"gatsby-plugin-postcss": "^4.3.0",
"prettier": "^2.2.1",
"tailwindcss": "^1.5.2"
}
}
Loading

0 comments on commit ce8e39d

Please sign in to comment.