├── .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 |
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 |
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 |
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 |
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 |
52 |
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 |
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------