Skip to content

Commit 49e5a7d

Browse files
author
swyx
committed
init
1 parent 3e54b80 commit 49e5a7d

11 files changed

+404
-311
lines changed

README.md

+3-91
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,5 @@
1-
This project is based on [Create React App v2](https://github.com/facebookincubator/create-react-app) and [netlify-lambda v1](https://github.com/netlify/netlify-lambda). (For more information about Create react App, check their full [documentation](https://github.com/facebookincubator/create-react-app#create-react-app).)
1+
this is a demo of using Netlify Identity by wrapping the goTrue API in a React Hook.
22

3-
The main addition is a new folder: `src/lambda`. Each JavaScript file in there will automatically be prepared for Lambda function deployment.
3+
⚠️Make sure Netlify Identity is enabled!!! or demo wont work
44

5-
As an example, we've included a small `src/lambda/hello.js` function, which will be deployed to `/.netlify/functions/hello`. We've also included an async lambda example using async/await syntax in `async-chuck-norris.js`.
6-
7-
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/netlify/create-react-app-lambda)
8-
9-
## Babel/webpack compilation
10-
11-
All functions are compiled with webpack using the Babel Loader, so you can use modern JavaScript, import npm modules, etc., without any extra setup.
12-
13-
## Local Development
14-
15-
Before developing, clone the repository and run `yarn` from the root of the repo to install all dependencies.
16-
17-
### Option 1: Starting both servers at once
18-
19-
Most people should be able to get up and running just by running:
20-
21-
```bash
22-
yarn start
23-
```
24-
25-
This uses [npm-run-all](https://github.com/mysticatea/npm-run-all#readme) to run the functions dev server and app dev server concurrently.
26-
27-
### Option 2: Start each server individually
28-
29-
**Run the functions dev server**
30-
31-
From inside the project folder, run:
32-
33-
```
34-
yarn start:lambda
35-
```
36-
37-
This will open a local server running at `http://localhost:9000` serving your Lambda functions, updating as you make changes in the `src/lambda` folder.
38-
39-
You can then access your functions directly at `http://localhost:9000/{function_name}`, but to access them with the app, you'll need to start the app dev server. Under the hood, this uses `react-scripts`' [advanced proxy feature](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#configuring-the-proxy-manually) with the `setupProxy.js` file.
40-
41-
**Run the app dev server**
42-
43-
While the functions server is still running, open a new terminal tab and run:
44-
45-
```
46-
yarn start:app
47-
```
48-
49-
This will start the normal create-react-app dev server and open your app at `http://localhost:3000`.
50-
51-
Local in-app requests to the relative path `/.netlify/functions/*` will automatically be proxied to the local functions dev server.
52-
53-
<details>
54-
<summary>
55-
<b id="typescript">Typescript</b>
56-
</summary>
57-
You can use Typescript in both your React code (with `react-scripts` v2.1+) and your lambda functions )with `netlify-lambda` v1.1+). Follow these instructions:
58-
59-
1. `yarn add -D typescript @types/node @types/react @types/react-dom @babel/preset-typescript @types/aws-lambda`
60-
2. convert `src/lambda/hello.js` to `src/lambda/hello.ts`
61-
3. use types in your event handler:
62-
63-
```ts
64-
import { Handler, Context, Callback } from 'aws-lambda';
65-
66-
interface HelloResponse {
67-
statusCode: number;
68-
body: string;
69-
}
70-
71-
const handler: Handler = (event: any, context: Context, callback: Callback) => {
72-
const response: HelloResponse = {
73-
statusCode: 200,
74-
body: JSON.stringify({
75-
msg: `Hello world ${Math.floor(Math.random() * 10)}`
76-
})
77-
};
78-
79-
callback(undefined, response);
80-
};
81-
82-
export { handler };
83-
```
84-
85-
rerun and see it work!
86-
87-
You are free to set up your `tsconfig.json` and `tslint` as you see fit.
88-
89-
</details>
90-
91-
## Service Worker
92-
93-
The service worker does not work with lambda functions out of the box. It prevents calling the function and returns the app itself instead ([Read more](https://github.com/facebook/create-react-app/issues/2237#issuecomment-302693219)). To solve this you have to eject and enhance the service worker configuration in the webpack config. Whitelist the path of your lambda function and you are good to go.
5+
Reach Router is used to show authentication as per [Ryan Florence](https://twitter.com/ryanflorence/status/1060361144701833216).

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
"version": "0.4.0",
44
"private": true,
55
"dependencies": {
6+
"@reach/router": "^1.2.1",
7+
"gotrue-js": "^0.9.22",
68
"node-fetch": "^2.3.0",
7-
"react": "^16.6.3",
8-
"react-dom": "^16.6.3",
9+
"react": "^16.7.0-alpha.2",
10+
"react-dom": "^16.7.0-alpha.2",
911
"react-scripts": "^2.1.1"
1012
},
1113
"scripts": {

src/App.js

+135-43
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,146 @@
1-
import React, { Component } from 'react';
2-
import logo from './logo.svg';
1+
import React from 'react';
2+
import { Router, Link, navigate } from '@reach/router';
33
import './App.css';
4+
import { useNetlifyIdentity } from './useNetlifyIdentity';
45

5-
class LambdaDemo extends Component {
6-
constructor(props) {
7-
super(props);
8-
this.state = { loading: false, msg: null };
9-
}
6+
let IdentityContext = React.createContext();
107

11-
handleClick = api => e => {
12-
e.preventDefault();
8+
function PrivateRoute(props) {
9+
const identity = React.useContext(IdentityContext);
10+
let { as: Comp, ...rest } = props;
11+
return identity.user ? (
12+
<Comp {...rest} />
13+
) : (
14+
<div>
15+
<h3>You are trying to view a protected page. Please log in</h3>
16+
<Login />
17+
</div>
18+
);
19+
}
1320

14-
this.setState({ loading: true });
15-
fetch('/.netlify/functions/' + api)
16-
.then(response => response.json())
17-
.then(json => this.setState({ loading: false, msg: json.msg }));
21+
function Login() {
22+
const { loginUser, signupUser } = React.useContext(IdentityContext);
23+
const formRef = React.useRef();
24+
const [msg, setMsg] = React.useState('');
25+
const signup = () => {
26+
const email = formRef.current.email.value;
27+
const password = formRef.current.password.value;
28+
signupUser(email, password)
29+
.then(user => {
30+
console.log('Success! Signed up', user);
31+
navigate('/dashboard');
32+
})
33+
.catch(err => console.error(err) || setMsg('Error: ' + err.message));
1834
};
35+
return (
36+
<form
37+
ref={formRef}
38+
onSubmit={e => {
39+
e.preventDefault();
40+
const email = e.target.email.value;
41+
const password = e.target.password.value;
42+
loginUser(email, password)
43+
.then(user => {
44+
console.log('Success! Logged in', user);
45+
navigate('/dashboard');
46+
})
47+
.catch(err => console.error(err) || setMsg('Error: ' + err.message));
48+
}}
49+
>
50+
<div
51+
style={{ textAlign: 'left', margin: '0 auto', display: 'inline-block' }}
52+
>
53+
<div>
54+
<label>
55+
Email:
56+
<input type="email" name="email" />
57+
</label>
58+
</div>
59+
<div>
60+
<label>
61+
Password:
62+
<input type="password" name="password" />
63+
</label>
64+
</div>
65+
<div>
66+
<input type="submit" value="Log in" />
67+
<button onClick={signup}>Sign Up </button>
68+
{msg && <pre>{msg}</pre>}
69+
</div>
70+
</div>
71+
</form>
72+
);
73+
}
74+
75+
function Home() {
76+
return (
77+
<div>
78+
<h3>Welcome to the Home page!</h3>
79+
<p>this is not behind an authentication wall</p>
80+
</div>
81+
);
82+
}
83+
84+
function About() {
85+
return <div>About</div>;
86+
}
87+
88+
function Dashboard() {
89+
const props = React.useContext(IdentityContext);
90+
console.log({ props });
91+
const { isConfirmedUser, authedFetch } = props;
92+
const [msg, setMsg] = React.useState('Click to load something');
93+
const handler = () => {
94+
authedFetch.get('/.netlify/functions/authEndPoint').then(setMsg);
95+
};
96+
return (
97+
<div>
98+
<h3>This is a Protected Dashboard!</h3>
99+
{!isConfirmedUser && <pre>You have not confirmed your email.</pre>}
100+
<hr />
101+
<div>
102+
You can try pinging our authenticated API here. If you are logged in,
103+
you should be able to see a `user` info here.
104+
<button onClick={handler}>Ping authenticated API</button>
105+
<pre>{JSON.stringify(msg, null, 2)}</pre>
106+
</div>
107+
</div>
108+
);
109+
}
110+
function Nav() {
111+
const { isLoggedIn } = React.useContext(IdentityContext);
112+
return (
113+
<nav>
114+
<Link to="/">Home</Link> |<Link to="dashboard">Dashboard</Link> |
115+
<span>
116+
{isLoggedIn ? <Logout /> : <Link to="login">Log In/Sign Up</Link>}
117+
</span>
118+
</nav>
119+
);
120+
}
121+
function Logout() {
122+
const { logoutUser } = React.useContext(IdentityContext);
123+
return <button onClick={logoutUser}>You are signed in. Log Out</button>;
124+
}
19125

20-
render() {
21-
const { loading, msg } = this.state;
22-
23-
return (
24-
<p>
25-
<button onClick={this.handleClick('hello')}>
26-
{loading ? 'Loading...' : 'Call Lambda'}
27-
</button>
28-
<button onClick={this.handleClick('async-chuck-norris')}>
29-
{loading ? 'Loading...' : 'Call Async Lambda'}
30-
</button>
31-
<br />
32-
<span>{msg}</span>
33-
</p>
34-
);
35-
}
36-
}
37-
38-
class App extends Component {
39-
render() {
40-
return (
126+
function App() {
127+
const identity = useNetlifyIdentity(
128+
'https://optimistic-dubinsky-dacadd.netlify.com'
129+
);
130+
return (
131+
<IdentityContext.Provider value={identity}>
41132
<div className="App">
42-
<header className="App-header">
43-
<img src={logo} className="App-logo" alt="logo" />
44-
<p>
45-
Edit <code>src/App.js</code> and save to reload.
46-
</p>
47-
<LambdaDemo />
48-
</header>
133+
<h1>Netlify Identity + Reach Router demo</h1>
134+
<Nav />
135+
<Router>
136+
<Home path="/" />
137+
<About path="/about" />
138+
<Login path="/login" />
139+
<PrivateRoute as={Dashboard} path="/dashboard" />
140+
</Router>
49141
</div>
50-
);
51-
}
142+
</IdentityContext.Provider>
143+
);
52144
}
53145

54146
export default App;

src/App.test.js

-9
This file was deleted.

src/index.js

-6
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,5 @@ import React from 'react';
22
import ReactDOM from 'react-dom';
33
import './index.css';
44
import App from './App';
5-
import * as serviceWorker from './serviceWorker';
65

76
ReactDOM.render(<App />, document.getElementById('root'));
8-
9-
// If you want your app to work offline and load faster, you can change
10-
// unregister() to register() below. Note this comes with some pitfalls.
11-
// Learn more about service workers: http://bit.ly/CRA-PWA
12-
serviceWorker.unregister();

src/lambda/async-chuck-norris.js renamed to src/lambda/authEndPoint.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33

44
import fetch from 'node-fetch';
55
export async function handler(event, context) {
6+
if (!context.clientContext && !context.clientContext.identity) {
7+
return {
8+
statusCode: 500,
9+
body: JSON.stringify({
10+
msg:
11+
'No identity instance detected. Did you enable it? Also, Netlify Identity is not supported on local dev yet.'
12+
}) // Could be a custom message or object i.e. JSON.stringify(err)
13+
};
14+
}
15+
const { identity, user } = context.clientContext;
616
try {
717
const response = await fetch('https://api.chucknorris.io/jokes/random');
818
if (!response.ok) {
@@ -13,7 +23,7 @@ export async function handler(event, context) {
1323

1424
return {
1525
statusCode: 200,
16-
body: JSON.stringify({ msg: data.value })
26+
body: JSON.stringify({ identity, user, msg: data.value })
1727
};
1828
} catch (err) {
1929
console.log(err); // output to netlify function log

src/lambda/hello.js

-11
This file was deleted.

0 commit comments

Comments
 (0)