├── .env.development ├── .gitignore ├── LICENSE ├── README.md ├── components ├── app │ ├── add-item.jsx │ ├── layout │ │ ├── header.jsx │ │ ├── index.jsx │ │ └── layout.jsx │ └── list-items.jsx └── private-route.jsx ├── jsconfig.json ├── package.json ├── pages ├── _app.js ├── api │ └── hello.js ├── files.jsx ├── index.jsx ├── login.jsx ├── register.jsx └── settings.jsx ├── public ├── favicon.ico └── vercel.svg ├── utils └── nhost.js └── yarn.lock /.env.development: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_GRAPHQL_ENDPOINT=https://hasura-CHANGE.nhost.app/v1/graphql 2 | NEXT_PUBLIC_BACKEND_ENDPOINT=https://backend-CHANGE.nhost.app -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Nhost Examples 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # All examples has moved to [github.com/nhost/nhost](https://github.com/nhost/nhost/tree/main/examples). 2 | -------------------------------------------------------------------------------- /components/app/add-item.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import gql from "graphql-tag"; 3 | import { useMutation } from "@apollo/client"; 4 | 5 | const INSERT_ITEM = gql` 6 | mutation insertItem($item: items_insert_input!) { 7 | insert_items_one(object: $item) { 8 | id 9 | } 10 | } 11 | `; 12 | 13 | export function AddItem() { 14 | const [name, setName] = useState(""); 15 | const [insertItem] = useMutation(INSERT_ITEM); 16 | 17 | async function handleSubmit(e) { 18 | e.preventDefault(); 19 | 20 | try { 21 | await insertItem({ 22 | variables: { 23 | item: { 24 | name, 25 | }, 26 | }, 27 | }); 28 | } catch (error) { 29 | console.error(error); 30 | return alert("Failed adding item"); 31 | } 32 | 33 | setName(""); 34 | alert("Item added"); 35 | } 36 | 37 | return ( 38 |
39 |
Add item
40 | 41 |
42 |
43 | setName(e.target.value)} 47 | /> 48 |
49 |
50 | 51 |
52 |
53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /components/app/layout/header.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "next/link"; 3 | import gql from "graphql-tag"; 4 | import { useQuery } from "@apollo/client"; 5 | 6 | import { auth } from "utils/nhost"; 7 | 8 | const GET_SELF = gql` 9 | query getSelf($user_id: uuid!) { 10 | me: users_by_pk(id: $user_id) { 11 | id 12 | display_name 13 | account { 14 | id 15 | email 16 | } 17 | } 18 | } 19 | `; 20 | 21 | function UserInfoHeader() { 22 | const { loading, error, data } = useQuery(GET_SELF, { 23 | variables: { 24 | user_id: auth.getClaim("x-hasura-user-id"), 25 | }, 26 | context: { 27 | headers: { 28 | "x-hasura-role": "me", 29 | }, 30 | }, 31 | }); 32 | 33 | if (loading && !data) { 34 | return
Loading...
; 35 | } 36 | 37 | if (error) { 38 | console.error(error); 39 | return
Error getting user data
; 40 | } 41 | 42 | const { me } = data; 43 | 44 | return ( 45 |
46 |
47 | User: {me.display_name} ({me.account.email}) 48 |
49 |
auth.logout()}>Logout
50 |
51 | ); 52 | } 53 | 54 | export function Header() { 55 | return ( 56 |
57 |
58 |
59 | 60 | Dashboard 61 | 62 |
63 |
64 | 65 | Settings 66 | 67 |
68 |
69 |
70 | 71 |
72 |
73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /components/app/layout/index.jsx: -------------------------------------------------------------------------------- 1 | import { Layout } from "./layout"; 2 | import { Header } from "./header"; 3 | 4 | export { Layout, Header }; 5 | -------------------------------------------------------------------------------- /components/app/layout/layout.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Header } from "./header"; 3 | 4 | export function Layout({ children }) { 5 | return ( 6 |
7 |
8 |
{children}
9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /components/app/list-items.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import gql from "graphql-tag"; 3 | import { useSubscription } from "@apollo/client"; 4 | 5 | const GET_ITEMS = gql` 6 | subscription getItems { 7 | items { 8 | id 9 | name 10 | } 11 | } 12 | `; 13 | 14 | export function ListItems() { 15 | const { loading, error, data } = useSubscription(GET_ITEMS); 16 | 17 | if (loading) { 18 | return
Loading...
; 19 | } 20 | 21 | if (error) { 22 | console.error(error); 23 | return
Error loading items
; 24 | } 25 | 26 | const { items } = data; 27 | 28 | return ( 29 |
30 | {items.map((item) => { 31 | return
{item.name}
; 32 | })} 33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /components/private-route.jsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from "next/router"; 2 | import { useAuth } from "@nhost/react-auth"; 3 | 4 | export function PrivateRoute(Component) { 5 | return (props) => { 6 | const router = useRouter(); 7 | const { signedIn } = useAuth(); 8 | 9 | // wait to see if the user is logged in or not. 10 | if (signedIn === null) { 11 | return
Checking auth...
; 12 | } 13 | 14 | if (!signedIn) { 15 | router.push("/login"); 16 | return
Redirecting...
; 17 | } 18 | 19 | return ; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "." 4 | } 5 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-apollo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "@apollo/client": "^3.2.5", 12 | "@nhost/react-apollo": "^1.0.4", 13 | "@nhost/react-auth": "^1.0.4", 14 | "graphql": "^15.4.0", 15 | "graphql-tag": "^2.11.0", 16 | "next": "10.0.1", 17 | "nhost-js-sdk": "^2.3.3", 18 | "react": "17.0.1", 19 | "react-dom": "17.0.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import { NhostAuthProvider } from "@nhost/react-auth"; 2 | import { NhostApolloProvider } from "@nhost/react-apollo"; 3 | 4 | import { auth } from "utils/nhost"; 5 | 6 | function MyApp({ Component, pageProps }) { 7 | return ( 8 | 9 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default MyApp; 20 | -------------------------------------------------------------------------------- /pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default (req, res) => { 4 | res.statusCode = 200 5 | res.json({ name: 'John Doe' }) 6 | } 7 | -------------------------------------------------------------------------------- /pages/files.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { PrivateRoute } from "components/private-route"; 3 | import { Layout } from "components/app/layout"; 4 | import { storage } from "utils/nhost"; 5 | 6 | function Files() { 7 | const [file, setFile] = useState(null); 8 | 9 | function addFile(e) { 10 | setFile(e.target.files[0]); 11 | } 12 | 13 | async function upload() { 14 | try { 15 | await storage.put("/public/test.png", file); 16 | // await storage.put(`/public/${uuid}.${extension}`, file); 17 | // await storage.put(`/public/${file.name}`, file); 18 | } catch (error) { 19 | console.log({ error }); 20 | return alert("Upload failed"); 21 | } 22 | alert("Upload successful"); 23 | 24 | // You probably want to save the uploaded file to the database 25 | // You only need to save the `/public/test.png` part 26 | } 27 | 28 | return ( 29 | 30 |
31 |

Files (upload a .png file)

32 |
33 |
34 |
    35 |
  • Only upload .png files
  • 36 |
  • Reload page to see image
  • 37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 | 48 |
49 |
50 | ); 51 | } 52 | 53 | export default PrivateRoute(Files); 54 | -------------------------------------------------------------------------------- /pages/index.jsx: -------------------------------------------------------------------------------- 1 | import { PrivateRoute } from "components/private-route"; 2 | import { Layout } from "components/app/layout"; 3 | import { AddItem } from "components/app/add-item"; 4 | import { ListItems } from "components/app/list-items"; 5 | 6 | function Dashboard() { 7 | return ( 8 | 9 |
10 |

Dashboard

11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 | ); 20 | } 21 | 22 | export default PrivateRoute(Dashboard); 23 | -------------------------------------------------------------------------------- /pages/login.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { useRouter } from "next/router"; 3 | import Link from "next/link"; 4 | 5 | import { auth } from "utils/nhost"; 6 | 7 | export default function Login() { 8 | const [email, setEmail] = useState(""); 9 | const [password, setPassword] = useState(""); 10 | const router = useRouter(); 11 | 12 | async function handleSubmit(e) { 13 | e.preventDefault(); 14 | 15 | try { 16 | await auth.login(email, password); 17 | } catch (error) { 18 | console.log(error); 19 | return alert("login failed"); 20 | } 21 | 22 | router.push("/"); 23 | } 24 | 25 | return ( 26 |
27 |
Login
28 |
29 |
30 |
31 | setEmail(e.target.value)} 35 | placeholder="Email" 36 | autoFocus 37 | /> 38 |
39 |
40 | setPassword(e.target.value)} 45 | /> 46 |
47 |
48 | 49 |
50 |
51 |
52 |
53 | 54 | Register 55 | 56 |
57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /pages/register.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { useRouter } from "next/router"; 3 | import Link from "next/link"; 4 | 5 | import { auth } from "utils/nhost"; 6 | 7 | export default function Register() { 8 | const [displayName, setDisplayName] = useState(""); 9 | const [email, setEmail] = useState(""); 10 | const [password, setPassword] = useState(""); 11 | const router = useRouter(); 12 | 13 | async function handleSubmit(e) { 14 | e.preventDefault(); 15 | 16 | try { 17 | await auth.register(email, password, { 18 | display_name: displayName, 19 | }); 20 | } catch (error) { 21 | console.log(error); 22 | return alert("Registration failed"); 23 | } 24 | 25 | alert("Registration OK. Now login!"); 26 | router.push("/login"); 27 | } 28 | 29 | return ( 30 |
31 |
Register
32 |
33 |
34 |
35 | setDisplayName(e.target.value)} 39 | placeholder="Name" 40 | autoFocus 41 | /> 42 |
43 |
44 | setEmail(e.target.value)} 49 | /> 50 |
51 |
52 | setPassword(e.target.value)} 57 | /> 58 |
59 |
60 | 61 |
62 |
63 |
64 |
65 | 66 | Login 67 | 68 |
69 |
70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /pages/settings.jsx: -------------------------------------------------------------------------------- 1 | import { PrivateRoute } from "components/private-route"; 2 | import { Layout } from "components/app/layout"; 3 | 4 | function Dashboard() { 5 | return ( 6 | 7 |
8 |

Settings

9 |
10 |
11 | ); 12 | } 13 | 14 | export default PrivateRoute(Dashboard); 15 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhost-examples/nextjs-apollo/7711a2419b64b25a519c44be695fbe69472e2d36/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /utils/nhost.js: -------------------------------------------------------------------------------- 1 | import nhost from "nhost-js-sdk"; 2 | 3 | const config = { 4 | base_url: process.env.NEXT_PUBLIC_BACKEND_ENDPOINT, 5 | }; 6 | 7 | nhost.initializeApp(config); 8 | 9 | const auth = nhost.auth(); 10 | const storage = nhost.storage(); 11 | 12 | export { auth, storage }; 13 | --------------------------------------------------------------------------------