Compare commits
484 Commits
@nhost/rea
...
@nhost/rea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
050cb288fc | ||
|
|
cd120c61fb | ||
|
|
5ddb5a751b | ||
|
|
92209b6690 | ||
|
|
bdb11c0fa4 | ||
|
|
63c3e7cb2c | ||
|
|
78341491cd | ||
|
|
cd8560e6d3 | ||
|
|
fced43f55d | ||
|
|
47cc5d1562 | ||
|
|
9ab4d49421 | ||
|
|
4bdd29c2ea | ||
|
|
a43fa284b4 | ||
|
|
e68ca9b0c8 | ||
|
|
ad26b8392f | ||
|
|
09ffcb961a | ||
|
|
42879a26da | ||
|
|
cdba9aa5a0 | ||
|
|
b092e13ba0 | ||
|
|
a2343c8ef2 | ||
|
|
31cc558492 | ||
|
|
2cbe8a5045 | ||
|
|
b6817f4c55 | ||
|
|
41823e143f | ||
|
|
a38ddeed5e | ||
|
|
8b69be1baa | ||
|
|
bc11c9e56a | ||
|
|
1fbf990c79 | ||
|
|
b942420b07 | ||
|
|
abbbf0b059 | ||
|
|
6121d2be45 | ||
|
|
6e8a3c9b56 | ||
|
|
601e0237ed | ||
|
|
7ccd051a41 | ||
|
|
7f7eebd75e | ||
|
|
c5a9fc568b | ||
|
|
24833db254 | ||
|
|
6f970ec18d | ||
|
|
fe158a5334 | ||
|
|
05fd36ce75 | ||
|
|
e19818b262 | ||
|
|
dac7e16249 | ||
|
|
4708c36a05 | ||
|
|
a1f4e9146a | ||
|
|
b0d0577800 | ||
|
|
74a7feed45 | ||
|
|
2b2f8e91ae | ||
|
|
a729bc6cde | ||
|
|
0b79b4f492 | ||
|
|
832702ff45 | ||
|
|
397748bd1f | ||
|
|
1026dfccb7 | ||
|
|
e5d3066091 | ||
|
|
9847875752 | ||
|
|
8af9c880c9 | ||
|
|
004c44cb80 | ||
|
|
33358e88f8 | ||
|
|
40facd73f7 | ||
|
|
028793d1a3 | ||
|
|
bc4c20592e | ||
|
|
023a3cbef2 | ||
|
|
a8490b5a52 | ||
|
|
1791452fc2 | ||
|
|
e390f99c43 | ||
|
|
0a9143a5b8 | ||
|
|
4e9a47d229 | ||
|
|
fbec804d9e | ||
|
|
a815678379 | ||
|
|
a2d4530e8a | ||
|
|
c8b0269d6f | ||
|
|
2eed249826 | ||
|
|
88c74cc059 | ||
|
|
858014e42a | ||
|
|
16b15802c7 | ||
|
|
1a6201f327 | ||
|
|
cb1560594b | ||
|
|
6fa70a6202 | ||
|
|
93b6daf486 | ||
|
|
b35936c230 | ||
|
|
45e536a1de | ||
|
|
cb3b9d4c79 | ||
|
|
865f052674 | ||
|
|
401fc2db9b | ||
|
|
00a641d20a | ||
|
|
a64284a028 | ||
|
|
1fd42677a8 | ||
|
|
0851a0a53d | ||
|
|
3122c5e726 | ||
|
|
f38a831405 | ||
|
|
3a18239c71 | ||
|
|
4952cc9d7b | ||
|
|
b13ddd087e | ||
|
|
01e9b609cf | ||
|
|
d1068ea78a | ||
|
|
793672fd3d | ||
|
|
577a108329 | ||
|
|
7e49829d81 | ||
|
|
b64fa32c8a | ||
|
|
c63aaaf5ce | ||
|
|
110ce6414c | ||
|
|
4df37cdefa | ||
|
|
d7a9e5c150 | ||
|
|
c3bede4051 | ||
|
|
e2e87bd0b2 | ||
|
|
2270983eb9 | ||
|
|
c6bc105a6c | ||
|
|
097e304f9f | ||
|
|
17ad0a21cc | ||
|
|
fb34967ea6 | ||
|
|
0f574d4c30 | ||
|
|
20ab24d227 | ||
|
|
efd31f4bce | ||
|
|
30da899832 | ||
|
|
d14b0d4644 | ||
|
|
51d742b12c | ||
|
|
4f9b34a6a0 | ||
|
|
640d4521e2 | ||
|
|
8003dfed8b | ||
|
|
972af7bab1 | ||
|
|
2c35b02c83 | ||
|
|
c8c2f50fca | ||
|
|
3ef786392b | ||
|
|
b4b3c5edc2 | ||
|
|
02fb3eaa91 | ||
|
|
a5c21ed9f8 | ||
|
|
a36843296a | ||
|
|
318b0c8d54 | ||
|
|
2a684d3f84 | ||
|
|
0e870ad971 | ||
|
|
96a2c5f63f | ||
|
|
5dbad5feb2 | ||
|
|
6bb43b2536 | ||
|
|
7a13cb247f | ||
|
|
1521572f5f | ||
|
|
b8c150e6c3 | ||
|
|
f9ad440114 | ||
|
|
da6fab0767 | ||
|
|
4b4181a073 | ||
|
|
1fc001a31a | ||
|
|
e150a6d212 | ||
|
|
083dc4865b | ||
|
|
86c58f62d9 | ||
|
|
a2d31c119b | ||
|
|
50a4c2d9b8 | ||
|
|
29229734f0 | ||
|
|
5e1756681c | ||
|
|
476c732935 | ||
|
|
d8d1423158 | ||
|
|
260c2eb51a | ||
|
|
b2fae7c78f | ||
|
|
ad52223fde | ||
|
|
e95881089b | ||
|
|
8726458df9 | ||
|
|
c317669152 | ||
|
|
7d53883697 | ||
|
|
94105194ff | ||
|
|
433ceb508e | ||
|
|
dec2a83d0b | ||
|
|
e4751470b0 | ||
|
|
6c4233948d | ||
|
|
5300c09f56 | ||
|
|
5c13953a2b | ||
|
|
160ebd9f04 | ||
|
|
c16f630a7b | ||
|
|
688471faf0 | ||
|
|
c42ffe6809 | ||
|
|
f07d17a3e8 | ||
|
|
609681e741 | ||
|
|
ab8d20a354 | ||
|
|
543c85c85e | ||
|
|
4a9fbd6d84 | ||
|
|
24d45a1aed | ||
|
|
4511b7b538 | ||
|
|
cb39f3d9ab | ||
|
|
d49beb72bc | ||
|
|
d5ca9ae2c5 | ||
|
|
eb13606762 | ||
|
|
d0201c8a23 | ||
|
|
80eeea49be | ||
|
|
e92716097e | ||
|
|
54e11430b9 | ||
|
|
7af47ba7d2 | ||
|
|
7b624eae1c | ||
|
|
151871cedc | ||
|
|
4ecde10b99 | ||
|
|
0530bac1f1 | ||
|
|
13e4fa73d5 | ||
|
|
b1c652b550 | ||
|
|
5f980cb810 | ||
|
|
0b58894ef1 | ||
|
|
3ba123dbff | ||
|
|
0ffdeab89f | ||
|
|
df02e25d02 | ||
|
|
c7a407f111 | ||
|
|
91edc67a5e | ||
|
|
d1a7bd7f94 | ||
|
|
62c8c7a27f | ||
|
|
7c8f092667 | ||
|
|
eb36f6698d | ||
|
|
31b0830b91 | ||
|
|
7c39b14fd2 | ||
|
|
16669d98e4 | ||
|
|
0696c108eb | ||
|
|
88f8e5dbed | ||
|
|
78ae8b52d0 | ||
|
|
5a288f52df | ||
|
|
f922c02c08 | ||
|
|
1abb4354e8 | ||
|
|
828bf5bf2d | ||
|
|
d49d7d1ce0 | ||
|
|
b3f68b8748 | ||
|
|
d51389b50d | ||
|
|
8030f91f51 | ||
|
|
9a2afe7d77 | ||
|
|
3e9cf30c40 | ||
|
|
0ee2171754 | ||
|
|
f18b58e2fc | ||
|
|
fa577f5c48 | ||
|
|
8969748d3c | ||
|
|
a112a9a8ad | ||
|
|
589d17968f | ||
|
|
195b8ee4b7 | ||
|
|
44d092a997 | ||
|
|
21a9da792f | ||
|
|
d8983be968 | ||
|
|
fd562b9c78 | ||
|
|
eca4ed92c1 | ||
|
|
adf4d2b997 | ||
|
|
3a724f847d | ||
|
|
99f941b060 | ||
|
|
97392e547f | ||
|
|
685e2dfccc | ||
|
|
1f8dd6dbd0 | ||
|
|
9b9d5def10 | ||
|
|
b4c08c999c | ||
|
|
79425ad8e6 | ||
|
|
a2a6790ae4 | ||
|
|
2ce1579ad6 | ||
|
|
0222d0fa22 | ||
|
|
08e7a8e23a | ||
|
|
24faf32abe | ||
|
|
8662674abe | ||
|
|
a53d57a0e3 | ||
|
|
61df286fe8 | ||
|
|
0dce5d47f0 | ||
|
|
e9a01588da | ||
|
|
17e370e889 | ||
|
|
5929da369f | ||
|
|
23dd5e9414 | ||
|
|
32346f4e5a | ||
|
|
d4450ea0e4 | ||
|
|
f5b86f5865 | ||
|
|
69b3a6ba93 | ||
|
|
a6cfdb67d0 | ||
|
|
645eaf6367 | ||
|
|
02aee323a2 | ||
|
|
5217ffa5e3 | ||
|
|
b08790b7ab | ||
|
|
9737fde711 | ||
|
|
5802feedec | ||
|
|
63f607b8f1 | ||
|
|
c5ed2e0793 | ||
|
|
79f153e627 | ||
|
|
84c5ae1cba | ||
|
|
b5f82d9dd0 | ||
|
|
00e03d44b5 | ||
|
|
15aacc09db | ||
|
|
01b53348c4 | ||
|
|
aa770cc15a | ||
|
|
552790fe3f | ||
|
|
6f12144615 | ||
|
|
4d08a2c1dd | ||
|
|
ee34b9d2aa | ||
|
|
0d87c30a8e | ||
|
|
d7aaeeb8cc | ||
|
|
101f4f502d | ||
|
|
d070680abd | ||
|
|
aa16ba979b | ||
|
|
3f07c33b17 | ||
|
|
f935269a6b | ||
|
|
5f9b2f5b27 | ||
|
|
965f1b26b0 | ||
|
|
08394ffd01 | ||
|
|
802d095044 | ||
|
|
9340e115d1 | ||
|
|
170dbfc930 | ||
|
|
a525409bee | ||
|
|
5f718bf356 | ||
|
|
e44c4b2e93 | ||
|
|
5154d31126 | ||
|
|
913aef1986 | ||
|
|
ce93615c1c | ||
|
|
f5e542e4c1 | ||
|
|
029925d88f | ||
|
|
718a4db33c | ||
|
|
5a7be0cfd4 | ||
|
|
f4dc867242 | ||
|
|
038e279660 | ||
|
|
f671a5a420 | ||
|
|
95dbfd59ee | ||
|
|
8980c99200 | ||
|
|
c0dcdb2410 | ||
|
|
1035639850 | ||
|
|
9be836b036 | ||
|
|
0e96e7329e | ||
|
|
19ccc5ab0d | ||
|
|
e094e682ce | ||
|
|
49cc3cb41b | ||
|
|
e0d81d419f | ||
|
|
74eb71f8f0 | ||
|
|
a931c15073 | ||
|
|
d3028169df | ||
|
|
e04d88b034 | ||
|
|
eca552b931 | ||
|
|
ed30bdd7e1 | ||
|
|
b76bc30fe4 | ||
|
|
2b571ebf23 | ||
|
|
ce2a77a859 | ||
|
|
5bb64ae36b | ||
|
|
79258689ef | ||
|
|
8a1eefeee6 | ||
|
|
ebe9fb0a44 | ||
|
|
25f0d05fc2 | ||
|
|
ace5d89eed | ||
|
|
92cf6ae7bd | ||
|
|
9ae5e485e6 | ||
|
|
2965a7bf5b | ||
|
|
236ce72bb3 | ||
|
|
5ad5832e41 | ||
|
|
a57825e5ad | ||
|
|
648eac45b4 | ||
|
|
47936d4d1a | ||
|
|
1e233b6582 | ||
|
|
9ebd014287 | ||
|
|
6ce2534a36 | ||
|
|
9f8e792f0d | ||
|
|
e1383106d9 | ||
|
|
812d7a8eae | ||
|
|
2887ce0f82 | ||
|
|
8bdfb8fcac | ||
|
|
573436dd87 | ||
|
|
1c82ab5346 | ||
|
|
c7ce66597a | ||
|
|
c82605c4e8 | ||
|
|
479cbbe305 | ||
|
|
888a51ed33 | ||
|
|
ef41ce8bb2 | ||
|
|
864074fba5 | ||
|
|
f58c2bb9ce | ||
|
|
4eac3101c9 | ||
|
|
80bd938336 | ||
|
|
41db6f613a | ||
|
|
ee84bfa098 | ||
|
|
ad1b7b80e2 | ||
|
|
3fcd345cff | ||
|
|
43a3f1dd46 | ||
|
|
3ec745c91e | ||
|
|
92deec4531 | ||
|
|
c7fcc9fe82 | ||
|
|
081377af6c | ||
|
|
23cb207afc | ||
|
|
62b1495a22 | ||
|
|
8a79a7102f | ||
|
|
0f55f6db9b | ||
|
|
7b16a8d790 | ||
|
|
dca8233601 | ||
|
|
a7535b260b | ||
|
|
82520963f1 | ||
|
|
722abd9a19 | ||
|
|
92fbf2b425 | ||
|
|
2aff6c0b4e | ||
|
|
e101915f60 | ||
|
|
0195143fe1 | ||
|
|
e362925041 | ||
|
|
b4f8c7457d | ||
|
|
84f1ab2f61 | ||
|
|
f4c2088bce | ||
|
|
d3107934b0 | ||
|
|
5ae02605b2 | ||
|
|
b927587d75 | ||
|
|
e63c45cdaa | ||
|
|
34532b1a2f | ||
|
|
de3257ca7a | ||
|
|
9d32314065 | ||
|
|
9edfe408e0 | ||
|
|
d3c7930b48 | ||
|
|
4a864a9777 | ||
|
|
50ba5fe2c8 | ||
|
|
05e0c42c82 | ||
|
|
fbc9ff32dd | ||
|
|
67f0450dac | ||
|
|
584976d1ad | ||
|
|
509ed7d864 | ||
|
|
71b92363b4 | ||
|
|
c7aa9f7ea9 | ||
|
|
3e6057b4ed | ||
|
|
e2940d7de3 | ||
|
|
c65c7f5538 | ||
|
|
bb548cd108 | ||
|
|
adb30c537f | ||
|
|
d7d3e8f903 | ||
|
|
8a4064e99f | ||
|
|
937e28116b | ||
|
|
d885fe7b02 | ||
|
|
b0d7217276 | ||
|
|
bfba4ae7ec | ||
|
|
11934f202d | ||
|
|
44b12dc0a0 | ||
|
|
bf6ee5d360 | ||
|
|
1a7007d1cb | ||
|
|
05d25a54af | ||
|
|
9f8bdb504d | ||
|
|
cd2594f66a | ||
|
|
c6a3e9f516 | ||
|
|
1bfb1e6d10 | ||
|
|
35fd7b1b7c | ||
|
|
04c1ed6955 | ||
|
|
0c591daef4 | ||
|
|
6f5729eb45 | ||
|
|
9bc447dbff | ||
|
|
d2d0e7fced | ||
|
|
0ad654226b | ||
|
|
14f0d27c7d | ||
|
|
9d8f2dea22 | ||
|
|
f3a44931a5 | ||
|
|
9933e4389e | ||
|
|
1c1656441b | ||
|
|
5f68f8fe31 | ||
|
|
961103d7a5 | ||
|
|
2bebab3f8e | ||
|
|
0363abbbb1 | ||
|
|
2f3715d02a | ||
|
|
7c101e5226 | ||
|
|
ef943995e2 | ||
|
|
b6032508bc | ||
|
|
a6b00294e7 | ||
|
|
dbfc5ec220 | ||
|
|
c9d474ea6c | ||
|
|
2afa460263 | ||
|
|
776555bdda | ||
|
|
e0e9729884 | ||
|
|
6ffaf31af5 | ||
|
|
8ec18157bb | ||
|
|
396dc554d9 | ||
|
|
92f9576ca6 | ||
|
|
5cf8ace1bc | ||
|
|
cf1518ab75 | ||
|
|
b8c0dba6de | ||
|
|
116c8eba66 | ||
|
|
a9bc698dae | ||
|
|
175ab26e04 | ||
|
|
eb2d064cbe | ||
|
|
17d2c8c3d9 | ||
|
|
95ba1649d5 | ||
|
|
4c1992068e | ||
|
|
4e0aab1bb2 | ||
|
|
87fc565b14 | ||
|
|
ed46a7a6f9 | ||
|
|
6749bf3486 | ||
|
|
b32c1bab39 | ||
|
|
bbebf6ade2 | ||
|
|
1bde3e6516 | ||
|
|
3674f11183 | ||
|
|
76b3cb4643 | ||
|
|
5a06ecbb10 | ||
|
|
83a00bbde1 | ||
|
|
556190dfc5 | ||
|
|
60dc34a24b | ||
|
|
8cc5c94da5 | ||
|
|
83952b44b5 | ||
|
|
e609cc3fcb | ||
|
|
4dc31bd156 | ||
|
|
d57c0d6261 | ||
|
|
59a34143df | ||
|
|
69f3a84bf5 | ||
|
|
214ae2fe19 | ||
|
|
bc86be70ef | ||
|
|
8555d30dab | ||
|
|
6f1ca70b7a | ||
|
|
16dcd314bb | ||
|
|
aae6524acb | ||
|
|
9b834d8893 | ||
|
|
bd8ddaacc3 | ||
|
|
1cf7116bd8 |
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"ignorePatterns": ["**/dist", "**/build", "**/.next"],
|
||||
"extends": ["react-app", "plugin:react/recommended", "plugin:react-hooks/recommended"],
|
||||
"parserOptions": {
|
||||
// "project": "./tsconfig.json"
|
||||
"project": ["packages/*/tsconfig.json", "examples/*/tsconfig.json"]
|
||||
},
|
||||
"plugins": ["react", "@typescript-eslint", "react-hooks", "simple-import-sort"],
|
||||
"rules": {
|
||||
"no-use-before-define": "off",
|
||||
"simple-import-sort/exports": "error",
|
||||
|
||||
"simple-import-sort/imports": [
|
||||
"error",
|
||||
{
|
||||
"groups": [
|
||||
// Node.js builtins. You could also generate this regex if you use a `.js` config.
|
||||
// For example: `^(${require("module").builtinModules.join("|")})(/|$)`
|
||||
[
|
||||
"^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)"
|
||||
],
|
||||
// Packages
|
||||
["^\\w"],
|
||||
// Internal packages.
|
||||
["^(@|config/)(/*|$)"],
|
||||
// Side effect imports.
|
||||
["^\\u0000"],
|
||||
// Parent imports. Put `..` last.
|
||||
["^\\.\\.(?!/?$)", "^\\.\\./?$"],
|
||||
// Other relative imports. Put same-folder imports and `.` last.
|
||||
["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
|
||||
// Style imports.
|
||||
["^.+\\.s?css$"]
|
||||
]
|
||||
}
|
||||
],
|
||||
"import/no-anonymous-default-export": [
|
||||
"error",
|
||||
{
|
||||
"allowArrowFunction": true,
|
||||
"allowAnonymousFunction": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
// Automatically exclude all node_modules from the bundled version
|
||||
const { nodeExternalsPlugin } = require('esbuild-node-externals')
|
||||
esbuild
|
||||
.build({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outfile: 'dist/index.cjs.js',
|
||||
bundle: true,
|
||||
minify: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
sourcemap: true,
|
||||
target: 'node14',
|
||||
plugins: [nodeExternalsPlugin()]
|
||||
})
|
||||
.catch(() => process.exit(1))
|
||||
|
||||
esbuild
|
||||
.build({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outfile: 'dist/index.es.js',
|
||||
bundle: true,
|
||||
minify: true,
|
||||
platform: 'browser',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
target: 'es2019'
|
||||
})
|
||||
.catch(() => process.exit(1))
|
||||
@@ -1,7 +0,0 @@
|
||||
const base = require('./jest.config.base.js')
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
projects: ['<rootDir>/packages/*/jest.config.js'],
|
||||
coverageDirectory: '<rootDir>/coverage/'
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import dts from 'vite-plugin-dts'
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
const PWD = process.env.PWD
|
||||
const pkg = require(path.join(PWD, 'package.json'))
|
||||
|
||||
const tsEntry = path.resolve(PWD, 'src/index.ts')
|
||||
const entry = fs.existsSync(tsEntry) ? tsEntry : tsEntry.replace('.ts', '.tsx')
|
||||
|
||||
/**
|
||||
* @type {import('vite').UserConfig}
|
||||
*/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
tsconfigPaths(),
|
||||
dts({
|
||||
exclude: ['**/*.spec.ts', '**/*.test.ts', '**/tests/**'],
|
||||
afterBuild: () => {
|
||||
const types = fs.readdirSync(path.join(PWD, 'dist/src'))
|
||||
types.forEach((file) => {
|
||||
fs.renameSync(path.join(PWD, 'dist/src', file), path.join(PWD, 'dist', file))
|
||||
})
|
||||
fs.rmdirSync(path.join(PWD, 'dist/src'))
|
||||
}
|
||||
})
|
||||
],
|
||||
build: {
|
||||
lib: {
|
||||
entry,
|
||||
name: pkg.name,
|
||||
fileName: 'index'
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['react', '@nhost/react'],
|
||||
output: {
|
||||
globals: {
|
||||
react: 'react',
|
||||
'@nhost/react': '@nhost/react'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
1
.github/workflows/changesets.yaml
vendored
1
.github/workflows/changesets.yaml
vendored
@@ -9,6 +9,7 @@ on:
|
||||
- 'examples/**'
|
||||
- 'assets/**'
|
||||
- '**.md'
|
||||
- '!.changeset/**'
|
||||
- 'LICENSE'
|
||||
|
||||
jobs:
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -19,6 +19,7 @@ logs/
|
||||
# Directories
|
||||
coverage/
|
||||
dist/
|
||||
umd/
|
||||
lib/
|
||||
node_modules/
|
||||
tmp/
|
||||
@@ -32,16 +33,15 @@ tmp/
|
||||
*.map
|
||||
todo.md
|
||||
|
||||
# Generated configs
|
||||
# Config files that are not part of the repository root anymore. Should be removed in the future.
|
||||
/.eslintignore
|
||||
/.eslintrc
|
||||
/.prettierignore
|
||||
/prettier.config.js
|
||||
/.eslintrc*
|
||||
/vite.*.js
|
||||
/jest.*.js
|
||||
/*tsconfig*.json
|
||||
/esbuild.*.js
|
||||
|
||||
!.config/**
|
||||
!config/**
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"packages/(docgen|hasura-auth-js|hasura-storage-js|nextjs|nhost-js|react|core)/src/**/*.{js,ts,jsx,tsx}": [
|
||||
"packages/(docgen|hasura-auth-js|hasura-storage-js|nextjs|nhost-js|react|core|vue)/src/**/*.{js,ts,jsx,tsx}": [
|
||||
"pnpm docgen",
|
||||
"git add docs"
|
||||
]
|
||||
|
||||
@@ -11,6 +11,11 @@ module.exports = {
|
||||
tabWidth: 2,
|
||||
trailingComma: 'none',
|
||||
useTabs: false,
|
||||
// TODO: add import sort configuration to match ESLint rules
|
||||
// plugins: ['./node_modules/@trivago/prettier-plugin-sort-imports'],
|
||||
// importOrderSeparation: true,
|
||||
// importOrderSortSpecifiers: true
|
||||
plugins: [],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.json', '*.yaml'],
|
||||
@@ -18,7 +23,5 @@ module.exports = {
|
||||
useTabs: false
|
||||
}
|
||||
}
|
||||
],
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true
|
||||
]
|
||||
}
|
||||
@@ -2,15 +2,17 @@
|
||||
|
||||
## Requirements
|
||||
|
||||
- We use [pnpm](https://pnpm.io/) as a package manager to fasten development and builds, and as a backbone to our monorepo. You have to make sure it is installed in your machine. There are [multiple ways to install it](https://pnpm.io/installation), but the easiest way is with `npm`:
|
||||
- We use [pnpm](https://pnpm.io/) as a package manager to speed up development and builds, and as a basis for our monorepo. You need to make sure it's installed on your machine. There are [several ways to install it](https://pnpm.io/installation), but the easiest way is with `npm`:
|
||||
|
||||
```sh
|
||||
npm install -g pnpm
|
||||
$ npm install -g pnpm
|
||||
```
|
||||
|
||||
- Our tests and examples are using the Nhost CLI to run the backend services locally. You can follow the instructions to install it in [our documentation](https://docs.nhost.io/get-started/cli-workflow/install-cli)
|
||||
- Our tests and examples use the Nhost CLI, to run the backend services locally. You can follow the installation instructions in [our documentation](https://docs.nhost.io/get-started/cli-workflow/install-cli).
|
||||
|
||||
## Getting things ready
|
||||
## Get started
|
||||
|
||||
### Installation
|
||||
|
||||
First, clone this repository:
|
||||
|
||||
@@ -21,57 +23,74 @@ git clone https://github.com/nhost/nhost
|
||||
Then, install the dependencies with `pnpm`:
|
||||
|
||||
```sh
|
||||
cd nhost
|
||||
pnpm install
|
||||
$ cd nhost
|
||||
$ pnpm install
|
||||
```
|
||||
|
||||
## Starting development from an example
|
||||
### Development
|
||||
|
||||
Let's follow the instructions to start [react-apollo example](https://github.com/nhost/nhost/blob/main/examples/react-apollo/README.md).
|
||||
Although package references are correctly updated on the fly for TypeScript, example projects won't
|
||||
see the changes because they are depending on the build output. To fix this, you can run packages
|
||||
in development mode.
|
||||
|
||||
## Running the documentation website locally
|
||||
|
||||
The easier way to contribute to our documentation is to move to the `docs` folder and follow the [instructions to start local development](https://github.com/nhost/nhost/blob/main/docs/README.md):
|
||||
Running packages in development mode is as simple as:
|
||||
|
||||
```sh
|
||||
cd docs
|
||||
# not necessary step if you've done this step already anywhere in the repository
|
||||
pnpm install
|
||||
pnpm start
|
||||
$ pnpm dev
|
||||
```
|
||||
|
||||
## Testing
|
||||
Our packages are linked together using [PNPM's workspace](https://pnpm.io/workspaces) feature. Vite automatically detects changes in the dependencies and rebuilds everything, so that the changes are immediately reflected in the other packages.
|
||||
|
||||
In order to run tests, the Nhost testing backend should run locally. You can start it in from a separate terminal:
|
||||
### Use examples
|
||||
|
||||
Examples are a great way to test your changes in practice. Make sure you've `pnpm dev` running in your terminal and then run an example.
|
||||
|
||||
Let's follow the instructions to run [react-apollo example](https://github.com/nhost/nhost/blob/main/examples/react-apollo/README.md).
|
||||
|
||||
## Run the documentation website locally
|
||||
|
||||
The easier way to contribute to our documentation is to go to the `docs` folder and follow the [instructions to start local development](https://github.com/nhost/nhost/blob/main/docs/README.md):
|
||||
|
||||
```sh
|
||||
cd examples/testing-project
|
||||
nhost -d
|
||||
$ cd docs
|
||||
# not necessary if you've already done this step somewhere in the repository
|
||||
$ pnpm install
|
||||
$ pnpm start
|
||||
```
|
||||
|
||||
Once Nhost started locally, you can run the tests from the root folder of the repository with the following command:
|
||||
## Run test suites
|
||||
|
||||
In order to run tests, the Nhost testing backend should be running locally. You can run it from a separate terminal:
|
||||
|
||||
```sh
|
||||
pnpm test
|
||||
$ cd examples/testing-project
|
||||
$ nhost -d
|
||||
```
|
||||
|
||||
Once Nhost is started locally, you can run the tests with the following command from the repository root:
|
||||
|
||||
```sh
|
||||
$ pnpm test
|
||||
```
|
||||
|
||||
## Changesets
|
||||
|
||||
If you made some changes in the packages, you will have to describe these changes so they will be taken into account in the next release.
|
||||
We are using [changesets](https://github.com/changesets/changesets) to support our version/publish workflows. When submitting a pull request, a bot will check if some changesets are present, and if not, will guide you to add them.
|
||||
If you've made changes to the packages, you must describe those changes so that they can be reflected in the next release.
|
||||
We use [changesets](https://github.com/changesets/changesets) to support our versioning and release workflows. When you submit a pull request, a bot checks if some changesets are present, and if not, it directs you to add them.
|
||||
|
||||
The most comprehensive way to add a changeset is to run the following command from the root directory of the repository:
|
||||
The most comprehensive way to add a changeset is to run the following command in the repository root:
|
||||
|
||||
```sh
|
||||
pnpm changeset
|
||||
$ pnpm changeset
|
||||
```
|
||||
|
||||
This will generate a file in the `.changeset` directory. You can edit it to give further details about the change you just made.
|
||||
You can have a look at the changesets documentation on [how to add a changeset](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md)
|
||||
This will create a file in the `.changeset` directory. You can edit it to give more details about the change you just made.
|
||||
|
||||
## Commiting changes
|
||||
You can take a look at the changeset documentation: [How to add a changeset](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).
|
||||
|
||||
You may notice `git commit` takes a few seconds to run. We set a commit hook that scan the changes in the code, automatically generates the documentation from the inline [TSDoc](https://tsdoc.org/) annotations, and add these generated documentation files to the commit. They will automatically update the [reference documentation](https://docs.nhost.io/reference).
|
||||
## Committing changes
|
||||
|
||||
You'll notice that `git commit` takes a few seconds to run. We set a commit hook that scans the changes in the code, automatically generates documentation from the inline [TSDoc](https://tsdoc.org/) annotations, and adds these generated documentation files to the commit. They automatically update the [reference documentation](https://docs.nhost.io/reference).
|
||||
|
||||
<!-- ## Good practices
|
||||
- lint
|
||||
|
||||
129
README.md
129
README.md
@@ -1,14 +1,14 @@
|
||||

|
||||

|
||||
|
||||
<div align="center">
|
||||
|
||||
# Nhost
|
||||
|
||||
<a href="https://docs.nhost.io/get-started">Quickstart</a>
|
||||
<a href="https://docs.nhost.io/#quickstart">Quickstart</a>
|
||||
<span> • </span>
|
||||
<a href="http://nhost.io/">Website</a>
|
||||
<span> • </span>
|
||||
<a href="https://docs.nhost.io/get-started">Docs</a>
|
||||
<a href="https://docs.nhost.io">Docs</a>
|
||||
<span> • </span>
|
||||
<a href="https://nhost.io/blog">Blog</a>
|
||||
<span> • </span>
|
||||
@@ -20,12 +20,12 @@
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
**Nhost is an open-source GraphQL backend,** built with the following things in mind:
|
||||
**Nhost is an open source Firebase alternative with GraphQL,** built with the following things in mind:
|
||||
|
||||
- Open-Source
|
||||
- Developer Productivity
|
||||
- SQL
|
||||
- Open Source
|
||||
- GraphQL
|
||||
- SQL
|
||||
- Great Developer Experience
|
||||
|
||||
Nhost consists of open source software:
|
||||
|
||||
@@ -47,19 +47,19 @@ Nhost consists of open source software:
|
||||
|
||||
Visit [https://docs.nhost.io](http://docs.nhost.io) for the complete documentation.
|
||||
|
||||
# How to get started
|
||||
# Get Started
|
||||
|
||||
### Option 1: One-click deployment with Nhost (recommended)
|
||||
## Option 1: Nhost Hosted Platform
|
||||
|
||||
1. Create [Nhost account](https://app.nhost.io) (you can use GitHub to sign up)
|
||||
2. Create Nhost app
|
||||
3. Done!
|
||||
1. Sign in to [Nhost](https://app.nhost.io).
|
||||
2. Create Nhost app.
|
||||
3. Done.
|
||||
|
||||
### Option 2: Self-hosting
|
||||
## Option 2: Self-hosting
|
||||
|
||||
Since Nhost is 100% open source, you can self-host the whole Nhost stack. Check out the example [docker-compose file](https://github.com/nhost/nhost/tree/main/examples/docker-compose) to self-host Nhost.
|
||||
|
||||
## Sign in a user and make your first GraphQL query
|
||||
## Sign In and Make a Graphql Request
|
||||
|
||||
Install the `@nhost/nhost-js` package and start build your app:
|
||||
|
||||
@@ -103,6 +103,7 @@ Nhost libraries and tools
|
||||
- [Nhost CLI](https://docs.nhost.io/reference/cli)
|
||||
- [Nhost React](https://docs.nhost.io/reference/react)
|
||||
- [Nhost Next.js](https://docs.nhost.io/reference/nextjs)
|
||||
- [Nhost Vue](https://docs.nhost.io/reference/vue)
|
||||
|
||||
## Community ❤️
|
||||
|
||||
@@ -148,6 +149,13 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Szilárd Dóró</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/gdangelo">
|
||||
<img src="https://avatars.githubusercontent.com/u/4352286?v=4" width="100;" alt="gdangelo"/>
|
||||
<br />
|
||||
<sub><b>Grégory D'Angelo</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/nunopato">
|
||||
<img src="https://avatars.githubusercontent.com/u/1523504?v=4" width="100;" alt="nunopato"/>
|
||||
@@ -161,15 +169,15 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Subha Das</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/guicurcio">
|
||||
<img src="https://avatars.githubusercontent.com/u/20285232?v=4" width="100;" alt="guicurcio"/>
|
||||
<br />
|
||||
<sub><b>Guido Curcio</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/sebagudelo">
|
||||
<img src="https://avatars.githubusercontent.com/u/43288271?v=4" width="100;" alt="sebagudelo"/>
|
||||
@@ -184,13 +192,6 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Mrinal Wahal</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/gdangelo">
|
||||
<img src="https://avatars.githubusercontent.com/u/4352286?v=4" width="100;" alt="gdangelo"/>
|
||||
<br />
|
||||
<sub><b>Grégory D'Angelo</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/FuzzyReason">
|
||||
<img src="https://avatars.githubusercontent.com/u/62517920?v=4" width="100;" alt="FuzzyReason"/>
|
||||
@@ -198,12 +199,27 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Vadim Smirnov</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/macmac49">
|
||||
<img src="https://avatars.githubusercontent.com/u/831190?v=4" width="100;" alt="macmac49"/>
|
||||
<br />
|
||||
<sub><b>Macmac49</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/subhendukundu">
|
||||
<img src="https://avatars.githubusercontent.com/u/20059141?v=4" width="100;" alt="subhendukundu"/>
|
||||
<br />
|
||||
<sub><b>Subhendu Kundu</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/heygambo">
|
||||
<img src="https://avatars.githubusercontent.com/u/449438?v=4" width="100;" alt="heygambo"/>
|
||||
<br />
|
||||
<sub><b>Christian Gambardella</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/chrtze">
|
||||
@@ -211,8 +227,7 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Christopher Möller</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/dbarrosop">
|
||||
<img src="https://avatars.githubusercontent.com/u/6246622?v=4" width="100;" alt="dbarrosop"/>
|
||||
@@ -240,7 +255,8 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Jerry Jäppinen</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/mustafa-hanif">
|
||||
<img src="https://avatars.githubusercontent.com/u/30019262?v=4" width="100;" alt="mustafa-hanif"/>
|
||||
@@ -254,8 +270,7 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Pratim</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Savinvadim1312">
|
||||
<img src="https://avatars.githubusercontent.com/u/16936043?v=4" width="100;" alt="Savinvadim1312"/>
|
||||
@@ -278,12 +293,13 @@ Here are some ways of contributing to making Nhost better:
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/heygambo">
|
||||
<img src="https://avatars.githubusercontent.com/u/449438?v=4" width="100;" alt="heygambo"/>
|
||||
<a href="https://github.com/Sonichigo">
|
||||
<img src="https://avatars.githubusercontent.com/u/53110238?v=4" width="100;" alt="Sonichigo"/>
|
||||
<br />
|
||||
<sub><b>Christian Gambardella</b></sub>
|
||||
<sub><b>Animesh Pathak</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rustyb">
|
||||
<img src="https://avatars.githubusercontent.com/u/53086?v=4" width="100;" alt="rustyb"/>
|
||||
@@ -297,8 +313,14 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Dominic Garms</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/gaurav1999">
|
||||
<img src="https://avatars.githubusercontent.com/u/20752142?v=4" width="100;" alt="gaurav1999"/>
|
||||
<br />
|
||||
<sub><b>Gaurav Agrawal</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/alveshelio">
|
||||
<img src="https://avatars.githubusercontent.com/u/8176422?v=4" width="100;" alt="alveshelio"/>
|
||||
@@ -313,6 +335,14 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Hoang Do</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/MelodicCrypter">
|
||||
<img src="https://avatars.githubusercontent.com/u/18341500?v=4" width="100;" alt="MelodicCrypter"/>
|
||||
<br />
|
||||
<sub><b>Hugh Caluscusin</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jladuval">
|
||||
<img src="https://avatars.githubusercontent.com/u/1935359?v=4" width="100;" alt="jladuval"/>
|
||||
@@ -320,6 +350,27 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Jacob Duval</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/leothorp">
|
||||
<img src="https://avatars.githubusercontent.com/u/12928449?v=4" width="100;" alt="leothorp"/>
|
||||
<br />
|
||||
<sub><b>Leo Thorp</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/MarcelloTheArcane">
|
||||
<img src="https://avatars.githubusercontent.com/u/21159570?v=4" width="100;" alt="MarcelloTheArcane"/>
|
||||
<br />
|
||||
<sub><b>Max Reynolds</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/nbourdin">
|
||||
<img src="https://avatars.githubusercontent.com/u/5602476?v=4" width="100;" alt="nbourdin"/>
|
||||
<br />
|
||||
<sub><b>Nicolas Bourdin</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ghoshnirmalya">
|
||||
<img src="https://avatars.githubusercontent.com/u/6391763?v=4" width="100;" alt="ghoshnirmalya"/>
|
||||
@@ -333,6 +384,14 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Quentin Decré</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/atapas">
|
||||
<img src="https://avatars.githubusercontent.com/u/3633137?v=4" width="100;" alt="atapas"/>
|
||||
<br />
|
||||
<sub><b>Tapas Adhikary</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/komninoschat">
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 61 KiB |
64
config/.eslint.base.js
Normal file
64
config/.eslint.base.js
Normal file
@@ -0,0 +1,64 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
node: true
|
||||
},
|
||||
ignorePatterns: [
|
||||
'dist',
|
||||
'umd',
|
||||
'build',
|
||||
'.next',
|
||||
'node_modules',
|
||||
'tsup.config.ts',
|
||||
'__tests__',
|
||||
'__mocks__',
|
||||
'*.test.ts',
|
||||
'*.test.tsx',
|
||||
'*.spec.ts',
|
||||
'*.spec.tsx',
|
||||
'tests/**/*.ts',
|
||||
'tests/**/*.d.ts'
|
||||
],
|
||||
plugins: ['@typescript-eslint', 'simple-import-sort'],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module'
|
||||
},
|
||||
rules: {
|
||||
'react/prop-types': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'simple-import-sort/exports': 'error',
|
||||
'simple-import-sort/imports': [
|
||||
'error',
|
||||
{
|
||||
groups: [
|
||||
// Node.js builtins. You could also generate this regex if you use a `.js` config.
|
||||
// For example: `^(${require("module").builtinModules.join("|")})(/|$)`
|
||||
[
|
||||
'^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)'
|
||||
],
|
||||
// Packages
|
||||
['^\\w'],
|
||||
// Internal packages.
|
||||
['^(@|config/)(/*|$)'],
|
||||
// Side effect imports.
|
||||
['^\\u0000'],
|
||||
// Parent imports. Put `..` last.
|
||||
['^\\.\\.(?!/?$)', '^\\.\\./?$'],
|
||||
// Other relative imports. Put same-folder imports and `.` last.
|
||||
['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
|
||||
// Style imports.
|
||||
['^.+\\.s?css$']
|
||||
]
|
||||
}
|
||||
],
|
||||
'import/no-anonymous-default-export': [
|
||||
'error',
|
||||
{
|
||||
allowArrowFunction: true,
|
||||
allowAnonymousFunction: true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
6
config/.eslintrc.js
Normal file
6
config/.eslintrc.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const base = require('./.eslint.base')
|
||||
module.exports = {
|
||||
...base,
|
||||
extends: ['react-app', 'plugin:react/recommended', 'plugin:react-hooks/recommended'],
|
||||
plugins: [...base.plugins, 'react', 'react-hooks']
|
||||
}
|
||||
10
config/.eslintrc.vue.js
Normal file
10
config/.eslintrc.vue.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const base = require('./.eslint.base')
|
||||
module.exports = {
|
||||
...base,
|
||||
extends: ['plugin:import/recommended', 'plugin:import/typescript'],
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
...base.parserOptions,
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ module.exports = {
|
||||
testRegex: '(/tests/.*.(test|spec)).(jsx?|tsx?)$',
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
||||
collectCoverage: true,
|
||||
// coveragePathIgnorePatterns: ['(tests/.*.mock).(jsx?|tsx?)$'],
|
||||
verbose: true,
|
||||
testTimeout: 30000,
|
||||
globals: {
|
||||
15
config/jest.config.js
Normal file
15
config/jest.config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
rootDir: process.cwd(),
|
||||
preset: 'ts-jest',
|
||||
collectCoverage: true,
|
||||
coverageProvider: 'v8',
|
||||
coverageDirectory: '<rootDir>/coverage',
|
||||
clearMocks: true,
|
||||
verbose: true,
|
||||
testTimeout: 30000,
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
isolatedModules: true
|
||||
}
|
||||
}
|
||||
}
|
||||
8
config/react-library.tsconfig.json
Normal file
8
config/react-library.tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "React Library",
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve"
|
||||
}
|
||||
}
|
||||
57
config/tsconfig.base.json
Normal file
57
config/tsconfig.base.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Default",
|
||||
"compilerOptions": {
|
||||
"composite": false,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"strict": true,
|
||||
"isolatedModules": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
"target": "ES6",
|
||||
"module": "CommonJS",
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.promise",
|
||||
"es2015.symbol",
|
||||
"es2015.iterable",
|
||||
"es2015.collection",
|
||||
"es2015.symbol.wellknown",
|
||||
"es2015.core",
|
||||
"es2017.object",
|
||||
"es2017.string"
|
||||
],
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": true,
|
||||
"types": ["node"],
|
||||
"typeRoots": [
|
||||
"./node_modules/@types", "**/*/dist", "**/*/build", "**/*/.next", "**/*/umd"
|
||||
],
|
||||
"paths": {
|
||||
"@nhost/apollo": ["../packages/apollo/src/index.ts"],
|
||||
"@nhost/core": ["../packages/core/src/index.ts"],
|
||||
"@nhost/docgen": ["../packages/docgen/src/index.ts"],
|
||||
"@nhost/hasura-auth-js": ["../packages/hasura-auth-js/src/index.ts"],
|
||||
"@nhost/hasura-storage-js": ["../packages/hasura-storage-js/src/index.ts"],
|
||||
"@nhost/nextjs": ["../packages/nextjs/src/index.ts"],
|
||||
"@nhost/nhost-js": ["../packages/nhost-js/src/index.ts"],
|
||||
"@nhost/react": ["../packages/react/src/index.ts"],
|
||||
"@nhost/react-apollo": ["../packages/react-apollo/src/index.ts"],
|
||||
"@nhost/react-auth": ["../packages/react-auth/src/index.ts"],
|
||||
"@nhost/vue": ["../packages/vue/src/index.ts"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*/dist",
|
||||
"**/*/build",
|
||||
"**/*/.next",
|
||||
"**/*/__tests__",
|
||||
"**/*/__mocks__"
|
||||
]
|
||||
}
|
||||
74
config/vite.lib.config.js
Normal file
74
config/vite.lib.config.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import dts from 'vite-plugin-dts'
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
const PWD = process.env.PWD
|
||||
const pkg = require(path.join(PWD, 'package.json'))
|
||||
|
||||
const tsEntry = path.resolve(PWD, 'src/index.ts')
|
||||
const entry = fs.existsSync(tsEntry) ? tsEntry : tsEntry.replace('.ts', '.tsx')
|
||||
|
||||
const deps = [...Object.keys(Object.assign({}, pkg.peerDependencies, pkg.dependencies))]
|
||||
|
||||
export default defineConfig({
|
||||
optimizeDeps: {
|
||||
include: ['react/jsx-runtime']
|
||||
},
|
||||
plugins: [
|
||||
tsconfigPaths(),
|
||||
dts({
|
||||
exclude: ['**/*.spec.ts', '**/*.test.ts', '**/tests/**'],
|
||||
afterBuild: () => {
|
||||
const types = fs.readdirSync(path.join(PWD, 'dist/src'))
|
||||
types.forEach((file) => {
|
||||
fs.renameSync(path.join(PWD, 'dist/src', file), path.join(PWD, 'dist', file))
|
||||
})
|
||||
fs.rmdirSync(path.join(PWD, 'dist/src'))
|
||||
}
|
||||
})
|
||||
],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
reporters: 'verbose',
|
||||
include: [`${PWD}/src/**/*.{spec,test}.{ts,tsx}`, `${PWD}/tests/**/*.{spec,test}.{ts,tsx}`],
|
||||
// Note: temporarily disabled threads, because of a bug in vitest
|
||||
// https://github.com/vitest-dev/vitest/issues/1171
|
||||
threads: false
|
||||
},
|
||||
build: {
|
||||
sourcemap: true,
|
||||
lib: {
|
||||
entry,
|
||||
name: pkg.name,
|
||||
fileName: (format) => (format === 'cjs' ? `index.cjs.js` : `index.esm.mjs`),
|
||||
formats: ['cjs', 'es']
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (id) => deps.some((dep) => id.startsWith(dep)),
|
||||
output: {
|
||||
globals: {
|
||||
graphql: 'graphql',
|
||||
'@apollo/client': '@apollo/client',
|
||||
'@apollo/client/core': '@apollo/client/core',
|
||||
'@apollo/client/link/context': '@apollo/client/link/context',
|
||||
'@apollo/client/link/subscriptions': '@apollo/client/link/subscriptions',
|
||||
'@apollo/client/utilities': '@apollo/client/utilities',
|
||||
'graphql-ws': 'graphql-ws',
|
||||
xstate: 'xstate',
|
||||
axios: 'axios',
|
||||
'js-cookie': 'Cookies',
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'react/jsx-runtime': '_jsx',
|
||||
'@nhost/react': '@nhost/react',
|
||||
vue: 'Vue',
|
||||
'vue-demi': 'vue-demi'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
13
config/vite.lib.dev.config.js
Normal file
13
config/vite.lib.dev.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
import viteLibConfig from './vite.lib.config'
|
||||
|
||||
export default defineConfig({
|
||||
...viteLibConfig,
|
||||
build: {
|
||||
...viteLibConfig.build,
|
||||
watch: {
|
||||
buildDelay: 500
|
||||
}
|
||||
}
|
||||
})
|
||||
28
config/vite.lib.umd.config.js
Normal file
28
config/vite.lib.umd.config.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import path from 'path'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
import baseLibConfig from './vite.lib.config'
|
||||
|
||||
const PWD = process.env.PWD
|
||||
const pkg = require(path.join(PWD, 'package.json'))
|
||||
|
||||
const deps = [...Object.keys(Object.assign({}, pkg.peerDependencies))]
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tsconfigPaths()],
|
||||
build: {
|
||||
...(baseLibConfig.build || {}),
|
||||
outDir: 'umd',
|
||||
lib: {
|
||||
...(baseLibConfig.build?.lib || {}),
|
||||
fileName: pkg.name.replace(/@nhost\//g, ''),
|
||||
formats: ['umd']
|
||||
},
|
||||
rollupOptions: {
|
||||
...(baseLibConfig.build?.rollupOptions || {}),
|
||||
external: (id) => deps.some((dep) => id.startsWith(dep))
|
||||
}
|
||||
}
|
||||
})
|
||||
10
config/vite.react.config.js
Normal file
10
config/vite.react.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
import baseLibConfig from './vite.lib.config'
|
||||
|
||||
export default defineConfig({
|
||||
...baseLibConfig,
|
||||
plugins: [react(), ...baseLibConfig.plugins]
|
||||
})
|
||||
13
config/vite.react.dev.config.js
Normal file
13
config/vite.react.dev.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
import viteReactConfig from './vite.react.config'
|
||||
|
||||
export default defineConfig({
|
||||
...viteReactConfig,
|
||||
build: {
|
||||
...viteReactConfig.build,
|
||||
watch: {
|
||||
buildDelay: 500
|
||||
}
|
||||
}
|
||||
})
|
||||
30
config/vite.react.umd.config.js
Normal file
30
config/vite.react.umd.config.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import path from 'path'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
import baseLibConfig from './vite.lib.config'
|
||||
|
||||
const PWD = process.env.PWD
|
||||
const pkg = require(path.join(PWD, 'package.json'))
|
||||
|
||||
const deps = [...Object.keys(Object.assign({}, pkg.peerDependencies))]
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), tsconfigPaths()],
|
||||
build: {
|
||||
...(baseLibConfig.build || {}),
|
||||
outDir: 'umd',
|
||||
lib: {
|
||||
...(baseLibConfig.build?.lib || {}),
|
||||
fileName: pkg.name.replace(/@nhost\//g, ''),
|
||||
formats: ['umd']
|
||||
},
|
||||
rollupOptions: {
|
||||
...(baseLibConfig.build?.rollupOptions || {}),
|
||||
external: (id) => deps.some((dep) => id.startsWith(dep))
|
||||
}
|
||||
}
|
||||
})
|
||||
10
config/vite.vue.config.js
Normal file
10
config/vite.vue.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
import baseLibConfig from './vite.lib.config'
|
||||
|
||||
export default defineConfig({
|
||||
...baseLibConfig,
|
||||
plugins: [vue(), ...baseLibConfig.plugins]
|
||||
})
|
||||
13
config/vite.vue.dev.config.js
Normal file
13
config/vite.vue.dev.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
import viteVueConfig from './vite.vue.config'
|
||||
|
||||
export default defineConfig({
|
||||
...viteVueConfig,
|
||||
build: {
|
||||
...viteVueConfig.build,
|
||||
watch: {
|
||||
buildDelay: 500
|
||||
}
|
||||
}
|
||||
})
|
||||
30
config/vite.vue.umd.config.js
Normal file
30
config/vite.vue.umd.config.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import path from 'path'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
import baseLibConfig from './vite.lib.config'
|
||||
|
||||
const PWD = process.env.PWD
|
||||
const pkg = require(path.join(PWD, 'package.json'))
|
||||
|
||||
const deps = [...Object.keys(Object.assign({}, pkg.peerDependencies))]
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue(), tsconfigPaths()],
|
||||
build: {
|
||||
...(baseLibConfig.build || {}),
|
||||
outDir: 'umd',
|
||||
lib: {
|
||||
...(baseLibConfig.build?.lib || {}),
|
||||
fileName: pkg.name.replace(/@nhost\//g, ''),
|
||||
formats: ['umd']
|
||||
},
|
||||
rollupOptions: {
|
||||
...(baseLibConfig.build?.rollupOptions || {}),
|
||||
external: (id) => deps.some((dep) => id.startsWith(dep))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"endOfLine": "auto",
|
||||
"semi": true
|
||||
}
|
||||
9
docs/CHANGELOG.md
Normal file
9
docs/CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# @nhost/docs
|
||||
|
||||
## 0.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 584976d: - publishable directory structure changes (ESM, CJS and UMD included in the output)
|
||||
- build system improvements
|
||||
- fixed some bundling concerns (https://github.com/nhost/nhost/issues/428)
|
||||
@@ -1,125 +0,0 @@
|
||||
---
|
||||
title: 'Authenticate users'
|
||||
slug: /get-started/authentication
|
||||
---
|
||||
|
||||
In the previous section, you defined `select` permissions for the `public` role. You will now add `insert` and `select` permissions for authenticated users to secure your app's GraphQL API with authentication.
|
||||
|
||||
> Nhost's authentication service lets you deliver frictionless registration and login experiences to your users. We support most social providers and different methods such as email & password and passwordless (magic link).
|
||||
|
||||
---
|
||||
|
||||
## Insert a test user
|
||||
|
||||
Manually create a user by going to your app's **Users** tab (top menu) and clicking on **Add User**.
|
||||
|
||||
<video width="99%" loop="" muted="" playsInline="" controls="true">
|
||||
<source src="/videos/add-user.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
You will now use that newly created user. We'll use this newly created user to make authenticated requests to the GraphQL API.
|
||||
|
||||
---
|
||||
|
||||
## Sign in and query data
|
||||
|
||||
Add the following code to sign in the new user and request the list of todos again:
|
||||
|
||||
```js
|
||||
import { NhostClient } from '@nhost/nhost-js';
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: 'https://[app-subdomain].nhost.run',
|
||||
})(async () => {
|
||||
// Sign in user
|
||||
const signInResponse = await nhost.auth.signIn({
|
||||
email: 'joe@example.com',
|
||||
password: 'securepassword',
|
||||
});
|
||||
|
||||
// Handle sign-in error
|
||||
if (signInResponse.error) {
|
||||
throw signInResponse.error;
|
||||
}
|
||||
|
||||
// Get todos
|
||||
const todos = await nhost.graphql.request(`
|
||||
query {
|
||||
todos {
|
||||
id
|
||||
created_at
|
||||
name
|
||||
is_completed
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
console.log(JSON.stringify(todos.data, null, 2));
|
||||
})();
|
||||
```
|
||||
|
||||
Why is the return value `null`? Because when making GraphQL requests as an authenticated user, the `user` role is assumed.
|
||||
|
||||
> For authenticated requests, there is always the option to override the default `user` role with any other valid role.
|
||||
|
||||
To prepare our database and GraphQL API to work for signed-in users we need to do two things:
|
||||
|
||||
1. Add a `user_id` column to the `todos` table, so we know what todo belongs to which user.
|
||||
2. Use the `user` role instead of the `public` role for permissions.
|
||||
|
||||
## Add `user_id` column
|
||||
|
||||
Before adding the `user_id` column, let's delete all existing todos.
|
||||
|
||||
Then add the `user_id` column as a `UUID` type. Make sure that `nullable` is **not** checked. This will ensure that all todos must have a `user_id` value.
|
||||
|
||||
At last, we'll create a connection between the `todos` table and the `users` table. For that, we need to do yet another two things:
|
||||
|
||||
1. Create a Foreign Key (FK) between `todos` and `auth.users.id`.
|
||||
2. Let Hasura track the relationship between the two tables.
|
||||
|
||||
<video width="99%" loop="" muted="" playsInline="" controls="true">
|
||||
<source src="/videos/user-id-column.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
### Create FK
|
||||
|
||||
Create a FK between the `auth.users.id` column and the `public.todos.user_id` column. See video above.
|
||||
|
||||
### Track relationship
|
||||
|
||||
Click on the `public` schema and track the untracked foreign key relationship. Then click on the `auth` schema and track the relationship again. See video above.
|
||||
|
||||
We track these relationships to create the GrpahQL relationships between the `todos` table to the `users` table and the `users` table to the `todos` table.
|
||||
|
||||
Ok, our `user_id` column is added and connected correctly. Let's continue with setting permissions for signed-in users.
|
||||
|
||||
## Permissions for signed-in users
|
||||
|
||||
Let us organize the permissions so it works for signed in users too.
|
||||
|
||||
### Remove permissions for the public role
|
||||
|
||||
We won't use the `public` role anymore, so let's remove all permission for that role.
|
||||
|
||||

|
||||
|
||||
Now we'll add permissions for the `user` role.
|
||||
|
||||
> Signed-in users use the `user` role by default
|
||||
|
||||
### Insert permission
|
||||
|
||||
First, we'll set the **Insert permission**.
|
||||
|
||||
A user can only insert `name` because all other columns will be set automatically. More specifically, `user_id` is set to the user's id making the request (`x-hasura-user-id`) and is configured in the `Column presets` section. See the image below.
|
||||
|
||||

|
||||
|
||||
### Select permission
|
||||
|
||||
For **Select permission**, set a **custom check** so users can only select todos where `user_id` is the same as their user id. In other words: users are only allowed to select their own todos. See the image below.
|
||||
|
||||

|
||||
|
||||
Now rerun the app. New todos are inserted, and only todos for the user are fetched and displayed. Your backend is successfully secured!
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"label": "CLI",
|
||||
"position": 8
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
title: 'CLI from Zero to Production'
|
||||
---
|
||||
|
||||
In the previous tutorials, we tested various parts of Nhost, such as:
|
||||
|
||||
- Database
|
||||
- GraphQL API
|
||||
- Permission
|
||||
- JavaScript SDK
|
||||
- Authentication
|
||||
|
||||
All changes we did to our database and API happened directly in production of our Nhost app.
|
||||
|
||||
It's not ideal for making changes in production because you might break things, which will affect all users of your app.
|
||||
|
||||
Instead, it's recommended to make changes and test your app locally before deploying those changes to production.
|
||||
|
||||
To do changes locally, we need to have a complete Nhost app running locally, which the Nhost CLI does.
|
||||
|
||||
The Nhost CLI matches your production application in a local environment, this way you can make changes and test your code before deploying your changes to production.
|
||||
|
||||
## Recommended workflow with Nhost
|
||||
|
||||
1. Develop locally using the Nhost CLI.
|
||||
2. Push changes to GitHub.
|
||||
3. Nhost automatically applies changes to production.
|
||||
|
||||
## What you'll learn in this guide:
|
||||
|
||||
- Use the Nhost CLI to create a local environment
|
||||
- Connect a GitHub repository with a Nhost app
|
||||
- Deploy local changes to production
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
title: 'Install the CLI'
|
||||
---
|
||||
|
||||
Install the Nhost CLI using the following command:
|
||||
|
||||
```bash
|
||||
sudo curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | bash
|
||||
```
|
||||
|
||||
Initialize a new Nhost App locally:
|
||||
|
||||
```bash
|
||||
nhost init -n "nhost-example-app" && cd nhost-example-app
|
||||
```
|
||||
|
||||
And initialize the GitHub repository in the same folder:
|
||||
|
||||
```bash
|
||||
echo "# nhost-example-app" >> README.md
|
||||
git init
|
||||
git add README.md
|
||||
git commit -m "first commit"
|
||||
git branch -M main
|
||||
git remote add origin https://github.com/[github-username]/nhost-example-app.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
Now go back to the **Nhost Console** and click **Deployments**. You just made a new deployment to your Nhost app!
|
||||
|
||||

|
||||
|
||||
If you click on the deployment you can see that nothing was really deployed. That’s because we just made a change to the README file.
|
||||
|
||||

|
||||
|
||||
Let's do some local backend changes!
|
||||
@@ -1,81 +0,0 @@
|
||||
---
|
||||
title: 'Local changes'
|
||||
---
|
||||
|
||||
Start Nhost locally:
|
||||
|
||||
```bash
|
||||
nhost dev
|
||||
```
|
||||
|
||||
:::tip
|
||||
Make sure you have [Docker](https://www.docker.com/get-started) installed on your computer. It’s required for Nhost to work.
|
||||
:::
|
||||
|
||||
The `nhost dev` command will automatically start a complete Nhost environment locally on your computer using:
|
||||
|
||||
- Postgres
|
||||
- Hasura
|
||||
- Authentication
|
||||
- Storage
|
||||
- Serverless Functions
|
||||
- Mailhog
|
||||
|
||||
You use this local environment to do changes and testing before you deploy your changes to production.
|
||||
|
||||
Running `nhost dev` also starts the Hasura Console.
|
||||
|
||||
:::tip
|
||||
It's important that you use the Hasura Console that is started automatically when you do changes. This way, changes are automatically tracked for you.
|
||||
:::
|
||||
|
||||

|
||||
|
||||
In the Hasura Console, create a new table `customers` with two columns:
|
||||
|
||||
- id
|
||||
- name
|
||||
|
||||
<video
|
||||
src="/videos/cli-workflow/hasura-create-customers-table.mp4"
|
||||
width="100%"
|
||||
controls
|
||||
/>
|
||||
|
||||
When we created the `customers` table there was also a migration created automatically. The migration was created at under `nhost/migrations/default`.
|
||||
|
||||
```bash
|
||||
$ ls -la nhost/migrations/default
|
||||
total 0
|
||||
drwxr-xr-x 3 eli staff 96 Feb 7 16:19 .
|
||||
drwxr-xr-x 3 eli staff 96 Feb 7 16:19 ..
|
||||
drwxr-xr-x 4 eli staff 128 Feb 7 16:19 1644247179684_create_table_public_customers
|
||||
```
|
||||
|
||||
This database migration has only been applied locally, meaning, you created the `customers` table locally but it does not (yet) exists in production.
|
||||
|
||||
To apply the local change to production we need to commit the changes and push it to GitHub. Nhost will then automatically pick up the change in the repository and apply the changes.
|
||||
|
||||
:::tip
|
||||
You can commit and push files in another terminal while still having `nhost dev` running.
|
||||
:::
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "Initialized Nhost and added a customers table"
|
||||
git push
|
||||
```
|
||||
|
||||
Head over to the **Deployments** tab in the **Nhost console** to see the deployment.
|
||||
|
||||

|
||||
|
||||
Once the deployment finishes the `customers` table is created in production.
|
||||
|
||||

|
||||
|
||||
We've now completed the recommended workflow with Nhost:
|
||||
|
||||
1. Develop locally using the Nhost CLI.
|
||||
2. Push changes to GitHub.
|
||||
3. Nhost deploys changes to production.
|
||||
@@ -1,170 +0,0 @@
|
||||
---
|
||||
title: 'Metadata and Serverless Functions'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
In the previous section, we only created a new table; `customers`. Using the CLI you can also do changes to other parts of your backend.
|
||||
|
||||
There are three things the CLI and the GitHub integration track and applies to production:
|
||||
|
||||
1. Database migrations
|
||||
2. Hasura Metadata
|
||||
3. Serverless Functions
|
||||
|
||||
For this section, let's do one change to the Hasura metadata and create one serverless function
|
||||
|
||||
### Hasura Metadata
|
||||
|
||||
We'll add permissions to the `users` table, making sure users can only see their own data. For this, go to the `auth` schema and click on the `users` table. then click on **Permissions** and enter a new role **user** and create a new **select** permission for that role**.**
|
||||
|
||||
Create the permission **with custom check**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": {
|
||||
"_eq": "X-Hasura-User-Id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Select the following columns:
|
||||
|
||||
- id
|
||||
- created_at
|
||||
- display_name
|
||||
- avatar_url
|
||||
- email
|
||||
|
||||
Then click **Save permissions**.
|
||||
|
||||
<video
|
||||
src="/videos/cli-workflow/hasura-user-permissions.mp4"
|
||||
width="100%"
|
||||
controls
|
||||
/>
|
||||
|
||||
Now, let's do a `git status` again to confirm the permission changes we did was tracked locally in your git repository.
|
||||
|
||||

|
||||
|
||||
We can now commit this change:
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "added permission for uses"
|
||||
```
|
||||
|
||||
Now let's create a serverless function before we push all changes to GitHub so Nhost can deploy our changes.
|
||||
|
||||
### Serverless Function
|
||||
|
||||
A serverless function is a pieces of code written in JavaScript or TypeScript that take an HTTP request and returns a response.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```bash
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
export default (req: Request, res: Response) => {
|
||||
res.status(200).send(`Hello ${req.query.name}!`)
|
||||
}
|
||||
```
|
||||
|
||||
Serverless functions are placed in the `functions/` folder of your repository. Every file will become its own endpoint.
|
||||
|
||||
Before we create our serverless function we'll install `express`, which is a requirement for serverless functions to work.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm install express
|
||||
npm install -d @types/node @types/express
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn add express
|
||||
yarn add -D @types/node @types/express
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Then we'll create a file `functions/time.ts`
|
||||
|
||||
In the file `time.ts` we'll add the following code to create our serverless function:
|
||||
|
||||
```bash
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
export default (req: Request, res: Response) => {
|
||||
return res
|
||||
.status(200)
|
||||
.send(`Hello ${req.query.name}! It's now: ${new Date().toUTCString()}`);
|
||||
};
|
||||
```
|
||||
|
||||
We can now test the function locally. Locally, the backend URL is `http://localhost:1337`. Functions are under `/v1/functions`. And every function's path and filename becomes an API endpoint.
|
||||
|
||||
This means our function `functions/time.ts` is at `http://localhost:1337/v1/functions/time`.
|
||||
|
||||
Let's use curl to test our new function:
|
||||
|
||||
```bash
|
||||
curl http://localhost:1337/v1/functions/time
|
||||
Hello undefined! It's now: Sun, 06 Feb 2022 17:44:45 GMT
|
||||
```
|
||||
|
||||
And with a query parameter with our name:
|
||||
|
||||
```bash
|
||||
curl http://localhost:1337/v1/functions/time\?name\=Johan
|
||||
Hello Johan! It's now: Sun, 06 Feb 2022 17:44:48 GMT
|
||||
```
|
||||
|
||||
Again, let's use `git status` to see the changes we did to create our serverless function.
|
||||
|
||||
Now let's commit the changes and push them to GitHub.
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "added serverless function"
|
||||
git push
|
||||
```
|
||||
|
||||
In the Nhost Console, click on the new deployment to see details.
|
||||
|
||||

|
||||
|
||||
After Nhost has finished deploying your changes we can test them in production. First let's confirm that the user permissions are applied.
|
||||
|
||||

|
||||
|
||||
Then, let's confirm that the serverless function was deployed. Again, we'll use curl:
|
||||
|
||||
```bash
|
||||
curl https://your-backend-url.nhost.run/v1/functions/time\?name\=Johan
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Conclusion
|
||||
|
||||
In this tutorial we have installed the Nhost CLI and created a local Nhost environment to do local development and testing.
|
||||
|
||||
In the local environment we've made changes to our database, to Hasura's metadata and created a serverless function.
|
||||
|
||||
We've connected a GitHub repository and pushed our changes to GitHub.
|
||||
|
||||
We've seen Nhost automatically deploying our changes and we've verified that the changes were applied.
|
||||
|
||||
In summary, we've set up a productive environment using the recommended Nhost workflow:
|
||||
|
||||
1. Develop locally using the Nhost CLI.
|
||||
2. Push changes to GitHub.
|
||||
3. Nhost deploys changes to production.
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
title: 'Workflow setup'
|
||||
---
|
||||
|
||||
What follows is a detailed tutorial on how you setup Nhost for this workflow
|
||||
|
||||
### Create Nhost App
|
||||
|
||||
Create a **new Nhost app** for this tutorial.
|
||||
|
||||
:::tip
|
||||
It's important that you create a **new** Nhost app for this guide instead of reusing an old Nhost app because we want to start with a clean Nhost app.
|
||||
:::
|
||||
|
||||

|
||||
|
||||
### Create new GitHub Repository
|
||||
|
||||
Create a new GitHub repository for your new Nhost app. The repo can be either private or public.
|
||||
|
||||

|
||||
|
||||
## Connect GitHub Repository to Nhost App
|
||||
|
||||
In the Nhost Console, go to the dashboard of your Nhost app and click **Connect to GitHub**.
|
||||
|
||||
<video
|
||||
src="/videos/cli-workflow/connect-github-repo.mp4"
|
||||
width="100%"
|
||||
controls
|
||||
/>
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
title: 'Welcome to Nhost'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Nhost is an open-source, real-time, server-less backend platform for building reliable apps that scale with your business.
|
||||
|
||||
---
|
||||
|
||||
## Components
|
||||
|
||||
Nhost uses an opinionated set of open-source components.
|
||||
|
||||
#### Database
|
||||
|
||||
Your application gets its own PostgreSQL database, the world's most advanced relational database.
|
||||
|
||||
#### GraphQL API
|
||||
|
||||
Highly performant and real-time GraphQL API with Hasura.
|
||||
|
||||
#### Authentication and storage
|
||||
|
||||
User management & file storage seamlessly integrated with Hasura permissions.
|
||||
|
||||
#### Serverless functions
|
||||
|
||||
JavaScript and TypeScript functions run your custom code in the backend.
|
||||
|
||||
---
|
||||
|
||||
## Get started
|
||||
|
||||
Follow our [Quick start](/get-started/quick-start) guide to build your first app.
|
||||
|
||||
Check out [Nhost on GitHub](https://github.com/nhost/nhost). Give us a star, and feel free to open a discussion for any feature requests as well.
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
title: 'Create your app'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Let's create a simple todo-app using Nhost. In a todo-app, a user should be able to create list items for their account (CRUD) and not have anyone else see them (permissions).
|
||||
|
||||
To implement this todo-app with Nhost, we'll briefly cover these topics:
|
||||
|
||||
- Creating a new app on Nhost
|
||||
- Defining a database schema
|
||||
- Inserting data
|
||||
- Setting permissions
|
||||
- Querying data via the GraphQL API
|
||||
|
||||
By the end of this quick-start, you will better understand what Nhost is and what it does for you.
|
||||
|
||||
---
|
||||
|
||||
## Log in to Nhost
|
||||
|
||||
Go to [app.nhost.io](https://app.nhost.io) and sign up for a new account if you don't have one already.
|
||||
|
||||
---
|
||||
|
||||
## Create app
|
||||
|
||||
Press the **"New App"** button on the console's home page. Choose a name and pick the region closest to your users.
|
||||
|
||||
You'll be all set with the Default Workspace and the Free plan for now.
|
||||
|
||||

|
||||
|
||||
Creating a new app takes around 20 seconds or so. During this time, Nhost sets up your app's entire backend and infrastructure.
|
||||
|
||||
Once the setup completes, you'll automatically see the app dashboard, and you're ready to define your app's database schema.
|
||||
@@ -1,110 +0,0 @@
|
||||
---
|
||||
title: 'JavaScript client'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
In the previous section, you used the Hasura Console to fetch a list of todos. Now, you will write a small JavaScript client to interact and retrieve todos from your Nhost app.
|
||||
|
||||
### Frontend frameworks
|
||||
|
||||
Nhost is framework-agnostic and works with any frontend you might build. You can also connect to Nhost from your server-side if you wish.
|
||||
|
||||
In this guide, we'll keep the example simple. We're not using a frontend framework. In a real-life scenario, you'd probably build a frontend client with a framework such as React, Vue, Svelte or React Native.
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
:::info
|
||||
|
||||
Make sure you have [Node.js](https://nodejs.org) and [npm](https://docs.npmjs.com/getting-started) or [Yarn](https://classic.yarnpkg.com/lang/en/docs/install) installed.
|
||||
|
||||
:::
|
||||
|
||||
Create a new folder called `nhost-todos`, initialize a new JavaScript app there, and install the Nhost JavaScript SDK:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm init -y
|
||||
npm install @nhost/nhost-js graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn init -y
|
||||
yarn add @nhost/nhost-js graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::caution attention
|
||||
You might have to edit the `package.json` file and add/change the `type` object to `module` (`"type": "module"`).
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Initialize Nhost
|
||||
|
||||
In the new directory, create a file called `index.js`.
|
||||
|
||||
Enter the following code into this file. It will initialize a new `NhostClient` that will interact with your backend:
|
||||
|
||||
```js
|
||||
import { NhostClient } from '@nhost/nhost-js';
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: 'https://[app-subdomain].nhost.run', // replace this with the backend URL of your app
|
||||
});
|
||||
|
||||
console.log(nhost.graphql.getUrl());
|
||||
```
|
||||
|
||||
Run the code in your terminal. You should see your app's GraphQL endpoint URL:
|
||||
|
||||
```bash
|
||||
➜ node index.js
|
||||
|
||||
https://[app-subdomain].nhost.run/v1/graphql
|
||||
```
|
||||
|
||||
### Query todos
|
||||
|
||||
If you now add the following GraphQL query to the client, let's see what happens when you run the updated version:
|
||||
|
||||
```js
|
||||
import { NhostClient } from '@nhost/nhost-js';
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: 'https://[app-subdomain].nhost.run',
|
||||
})(async () => {
|
||||
// nhost.graphql.request returns a promise, so we use await here
|
||||
const todos = await nhost.graphql.request(`
|
||||
query {
|
||||
todos {
|
||||
id
|
||||
created_at
|
||||
name
|
||||
is_completed
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
// Print todos to console
|
||||
console.log(JSON.stringify(todos.data, null, 2));
|
||||
})();
|
||||
```
|
||||
|
||||
```bash
|
||||
➜ node index.js
|
||||
|
||||
null
|
||||
```
|
||||
|
||||
`null` is printed. Why is that? Let's find out.
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
title: 'Set permissions'
|
||||
---
|
||||
|
||||
While using the Hasura Console, you could fetch the todos because the **admin** role is enabled by default but when building your applications with a client, you want to define permissions using **roles** that your users can assume when making requests.
|
||||
|
||||
Hasura supports role-based access control. You create rules for each role, table, and operation (select, insert, update and delete) that can check dynamic session variables, like user ID.
|
||||
|
||||
## Unauthenticated users
|
||||
|
||||
Use the `public` role in permissions when you want some data to be accessed by anyone without being signed in. The `public` role is the default role in all unauthenticated requests.
|
||||
|
||||
Generally speaking, the `public` role should not have insert, update or delete permissions defined.
|
||||
|
||||
### Setting `public` permissions
|
||||
|
||||
In Hasura Console, go to the **Data** tab, click on the **todos** table, then click **Permissions**. Add a new role called `public` and click on **select**. The permission options for the select operation show up below.
|
||||
|
||||
Add the following permissions:
|
||||
|
||||

|
||||
|
||||
Rerun the program. Now you see all todos.
|
||||
|
||||
```bash
|
||||
➜ node index.js
|
||||
|
||||
{
|
||||
"todos": [
|
||||
{
|
||||
"id": "558b9754-bb18-4abd-83d9-e9056934e812",
|
||||
"created_at": "2021-12-01T17:05:09.311362+00:00",
|
||||
"name": "write docs",
|
||||
"is_completed": false
|
||||
},
|
||||
{
|
||||
"id": "480369c8-6f57-4061-bfdf-9ead647e10d3",
|
||||
"created_at": "2021-12-01T17:05:20.5693+00:00",
|
||||
"name": "cook dinner",
|
||||
"is_completed": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
There are two reasons why the request succeeded:
|
||||
|
||||
1. Nhost sets the `public` role for every unauthenticated GraphQL request.
|
||||
2. You explicitly defined permissions for the `public` role.
|
||||
|
||||
It is essential to understand that Hasura has an **allow nothing by default** policy to ensure that only roles and permissions you define explicitly have access to the GraphQL API.
|
||||
@@ -1,95 +0,0 @@
|
||||
---
|
||||
title: 'Define schema'
|
||||
---
|
||||
|
||||
To implement an app for managing a todo list, let's ensure we have database tables for storing todos and users.
|
||||
|
||||
---
|
||||
|
||||
## Open Hasura Console
|
||||
|
||||
Hasura generates real-time GraphQL APIs, but it also provides a web console for manipulating the schema and data of your database.
|
||||
|
||||
Go to the **Data** tab on your app's dashboard and select **Open Hasura**. Remember to copy the admin secret.
|
||||
|
||||
The Hasura Console of your app's dedicated Hasura instance will open in a new tab. You can use Hasura Console to manage your app's schema, data, permissions, and event triggers.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Users table
|
||||
|
||||
You should see all your database tables on the left-hand side of the screen. You should see multiple different **schemas** displayed as folders:
|
||||
|
||||
- `public` schema for your app's custom tables
|
||||
- `auth` and `storage` schemas for Nhost's user management and file storage
|
||||
|
||||
If you open the `auth` schema, you'll see that your app already has a `users` table, so you don't have to create one.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Create todos table
|
||||
|
||||
In Hasura Console, go to the **data** tab, then click **Create Table**. Name this table `todos`.
|
||||
|
||||
### Add frequently used columns
|
||||
|
||||
`id` and `created_at` columns are standard and can be added with two clicks. Click **Frequently used columns** and create them:
|
||||
|
||||
- `id` (UUID)
|
||||
- `created_at` (timestamp)
|
||||
|
||||
Using frequently used columns ensures the columns get the right name, type, and default value.
|
||||
|
||||

|
||||
|
||||
### Add custom columns
|
||||
|
||||
Add two more columns manually:
|
||||
|
||||
- `name` (text)
|
||||
- `is_completed` (boolean)
|
||||
|
||||
Make sure to set the default value of `is_completed` to `false`.
|
||||
|
||||

|
||||
|
||||
This is all we need! A new table will be created when you click **Add Table**.
|
||||
|
||||
---
|
||||
|
||||
## Insert data
|
||||
|
||||
Go to the **Insert Row** tab to add some data to your database.
|
||||
|
||||
<video width="99%" loop="" muted="" playsInline="" controls="true">
|
||||
<source src="/videos/insert-todos.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
---
|
||||
|
||||
## Query data
|
||||
|
||||
Now that we have data in our database, we can retrieve it via a GraphQL API. Go to the **API** tab in the main menu. You can use this view to make GraphQL requests that query or mutate data in your database.
|
||||
|
||||
Paste the following GraphQL query into the form and press the "play" button:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
todos {
|
||||
id
|
||||
created_at
|
||||
name
|
||||
is_completed
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You should see the todos you just inserted show up as output on the right-hand side.
|
||||
|
||||
### Admin role
|
||||
|
||||
All requests in the Hasura Console use the `admin` role by default. This role has access to all tables and permissions.
|
||||
@@ -1,104 +0,0 @@
|
||||
---
|
||||
title: 'Upgrade from v1 to v2'
|
||||
---
|
||||
|
||||
Upgrading from Nhost v1 to v2 requires database schema and Hasura metadata changes.
|
||||
|
||||
---
|
||||
|
||||
## Upgrade Steps
|
||||
|
||||
### Create a new Nhost v2 app locally
|
||||
|
||||
:::tip
|
||||
Make sure you have the [Nhost CLI](/reference/cli) installed
|
||||
:::
|
||||
|
||||
```bash
|
||||
nhost init my-nhost-v2-app
|
||||
cd my-nhost-v2-app
|
||||
```
|
||||
|
||||
### Update config
|
||||
|
||||
Update `version: 3` to `version: 2` in `nhost/config.yaml`. This will update Hasura's configuration version, and we need to downgrade the version when we export migrations and metadata.
|
||||
|
||||
### Export current migrations and metadata from Nhost v1
|
||||
|
||||
Inside the `nhost/` folder of your app, run:
|
||||
|
||||
```bash
|
||||
hasura migrate create init --from-server --endpoint=[v1-endpoint] --admin-secret=[v1-admin-secret]
|
||||
|
||||
hasura metadata export --endpoint=[v1-endpoint] --admin-secret=[v1-admin-secret]
|
||||
```
|
||||
|
||||
### Update Migrations
|
||||
|
||||
Make the following changes manually to your migrations.
|
||||
|
||||
:::tip
|
||||
The migration file is located at `nhost/migrations/[timestamp]/up.sql`.
|
||||
:::
|
||||
|
||||
- Add `OR REPLACE` after `CREATE` for the `public.set_current_timestamp_updated_at` function
|
||||
- Delete all `auth.*` tables and functions (if any).
|
||||
- Delete `public.users` table and everything related to the table such as constraints, triggers, etc.
|
||||
- Update FK references from `public.users` to `auth.users` (if any).
|
||||
|
||||
### Update Metadata
|
||||
|
||||
Make the following changes manually to your metadata.
|
||||
|
||||
:::tip
|
||||
The metadata is located at `nhost/metadata/tables.yaml`.
|
||||
:::
|
||||
|
||||
- Delete tracking all tables in the `auth` schema.
|
||||
- Delete tracking the `public.users` table.
|
||||
- Update all references to `users` from the `public` to `auth` schema.
|
||||
|
||||
### Start nhost
|
||||
|
||||
Start Nhost locally using the [CLI](/reference/cli). From the root of your app, run:
|
||||
|
||||
```bash
|
||||
nhost -d
|
||||
```
|
||||
|
||||
:::tip
|
||||
Running Nhost applies your local database migrations and Hasura metadata.
|
||||
:::
|
||||
|
||||
### Restart Auth and Storage containers
|
||||
|
||||
Open Docker UI and restart Hasura Auth and Hasura Storage. Restarting those containers applies new metadata, effectively tracking everything in the `auth` and the `storage` schema.
|
||||
|
||||
### Delete migrations and metadata
|
||||
|
||||
Delete the local migrations and metadata.
|
||||
|
||||
```bash
|
||||
rm -rf nhost/migrations nhost/metadata
|
||||
```
|
||||
|
||||
### Update config (again)
|
||||
|
||||
Update `config: 2` to `config: 3` in `nhost/config.yaml`.
|
||||
|
||||
### Pull migrations and metadata from our local instance
|
||||
|
||||
In the `nhost/` folder, run the following command:
|
||||
|
||||
```bash
|
||||
hasura migrate create init --from-server --endpoint=http://localhost:[hasura-port] --admin-secret=nhost-admin-secret
|
||||
hasura metadata export --endpoint=http://localhost:[hasura-port] --admin-secret=nhost-admin-secret
|
||||
```
|
||||
|
||||
:::warning
|
||||
You cannot use port `1337` in the commands above. You have to use the specific port Hasura uses. Go to the Hasura Console under API and look for the port Hasura is using under GraphQL Endpoint.
|
||||
:::
|
||||
|
||||
### Done
|
||||
|
||||
You now have a Nhost v2 project locally with correct migrations and metadata. Happy hacking!
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
title: 'Welcome to Nhost'
|
||||
---
|
||||
|
||||
Nhost is an open-source, real-time, server-less backend platform for building reliable apps that scale with your business.
|
||||
|
||||
---
|
||||
|
||||
## Components
|
||||
|
||||
Nhost uses an opinionated set of open-source components.
|
||||
|
||||
#### Database
|
||||
|
||||
Your application gets its own PostgreSQL database, the world's most advanced relational database.
|
||||
|
||||
#### GraphQL API
|
||||
|
||||
Highly performant and real-time GraphQL API with Hasura.
|
||||
|
||||
#### Authentication and storage
|
||||
|
||||
User management & file storage seamlessly integrated with Hasura permissions.
|
||||
|
||||
#### Serverless functions
|
||||
|
||||
JavaScript and TypeScript functions run your custom code in the backend.
|
||||
|
||||
---
|
||||
|
||||
## Get started
|
||||
|
||||
Follow our [Quick start](/get-started/quick-start) guide to build your first app.
|
||||
|
||||
Check out [Nhost on GitHub](https://github.com/nhost/nhost). Give us a star, and feel free to open a discussion for any feature requests as well.
|
||||
@@ -2,6 +2,7 @@
|
||||
title: 'Introduction to Nhost'
|
||||
sidebar_label: Introduction
|
||||
sidebar_position: 1
|
||||
image: /img/og/platform/introduction-to-nhost.png
|
||||
---
|
||||
|
||||
Nhost is the open source GraphQL backend (Firebase Alternative) and a development platform. Nhost is doing for the backend, what [Netlify](https://netlify.com/) and [Vercel](https://vercel.com/) are doing for the frontend.
|
||||
@@ -16,6 +17,8 @@ Get started quickly by following one of our quickstart guides:
|
||||
|
||||
- [Next.js](/platform/quickstarts/nextjs)
|
||||
- [React](/platform/quickstarts/react)
|
||||
- [RedwoodJS](/platform/quickstarts/redwoodjs)
|
||||
- [Vue](/platform/quickstarts/vue)
|
||||
|
||||
## Products and features
|
||||
|
||||
@@ -1,93 +1,108 @@
|
||||
---
|
||||
title: 'Email templates'
|
||||
title: 'Email Templates'
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
The following emails can be sent as part of the authentication flow:
|
||||
Nhost Auth sends out transactional emails as part of the authentication service. These emails can be modified using email templates.
|
||||
|
||||
- Sign up confirmation email (when using email + password)
|
||||
- Reset password email (when using email + password)
|
||||
- Passwordless login email (when using Magic Link)
|
||||
- Confirm email change (any sign-up method)
|
||||
The following email templates are available:
|
||||
|
||||
---
|
||||
- **email-verify** - Verify email address
|
||||
- **email-confirm-change** - Confirm email change to a new email address.
|
||||
- **signin-passwordless** - Magic Link
|
||||
- **password-reset** - Reset password
|
||||
|
||||
## Enabling custom email templates
|
||||
Changing email templates is only available for apps on the [Pro and Enterprise plan](https://nhost.io/pricing).
|
||||
|
||||
If you have developed custom email templates, you must make them available over HTTP and then point Nhost to them. You can host the templates on your server, or use a public repository on GitHub.
|
||||
## Update Email Templates
|
||||
|
||||
Go to **Users -> Login settings** and scroll down to **Custom email templates**, and set the URL to where your templates are located:
|
||||
Your app must be connected to a GitHub repository using the [GitHub Integration](/platform/github-integration) to be able to change the email templates.
|
||||
|
||||

|
||||
Email templates are automatically deployed during a deployment, just like database migrations, Hasura metadata, and Serverless Functions.
|
||||
|
||||
You only need to define the base URL to point to your hosted templates. The UI will give you a hint about where Nhost will look for your actual template files.
|
||||
## File Structure
|
||||
|
||||
---
|
||||
Emails are located in the `nhost/` folder like this:
|
||||
|
||||
## File structure
|
||||
The email templates should be provided as body.html and subject.txt files in this predefined folder structure.
|
||||
|
||||
The email templates should be provided as body.html and subject.txt files in this predefined folder structure:
|
||||
**Example:** Email templates for `en` (English) and `es` (Spanish):
|
||||
|
||||
```txt
|
||||
// At base URL (e.g. https://yourapp.com/email-templates/)
|
||||
en/
|
||||
email-confirm-change/
|
||||
body.html
|
||||
subject.txt
|
||||
email-verify/
|
||||
body.html
|
||||
subject.txt
|
||||
password-reset/
|
||||
body.html
|
||||
subject.txt
|
||||
signin-passwordless/
|
||||
body.html
|
||||
subject.txt
|
||||
|
||||
// Other language versions
|
||||
fr/
|
||||
/* ... */
|
||||
se/
|
||||
/* ... */
|
||||
my-nhost-app/
|
||||
└── nhost/
|
||||
├── config.yaml
|
||||
├── emails/
|
||||
│ ├── en/
|
||||
│ │ ├── email-verify/
|
||||
│ │ │ ├── subject.txt
|
||||
│ │ │ └── body.html
|
||||
│ │ ├── email-confirm-change/
|
||||
│ │ │ ├── subject.txt
|
||||
│ │ │ └── body.html
|
||||
│ │ ├── signin-passwordless/
|
||||
│ │ │ ├── subject.txt
|
||||
│ │ │ └── body.html
|
||||
│ │ └── password-reset/
|
||||
│ │ ├── subject.txt
|
||||
│ │ └── body.html
|
||||
│ └── es/
|
||||
│ ├── email-verify/
|
||||
│ │ ├── subject.txt
|
||||
│ │ └── body.html
|
||||
│ ├── email-confirm-change/
|
||||
│ │ ├── subject.txt
|
||||
│ │ └── body.html
|
||||
│ ├── signin-passwordless/
|
||||
│ │ ├── subject.txt
|
||||
│ │ └── body.html
|
||||
│ └── password-reset/
|
||||
│ ├── subject.txt
|
||||
│ └── body.html
|
||||
├── migrations/
|
||||
├── metadata/
|
||||
└── seeds
|
||||
```
|
||||
|
||||
You don’t have to provide all templates - only the one you wish to use and customize. For the templates you do provide, you must provide both body.html and subject.txt.
|
||||
As you see, the format is:
|
||||
|
||||
[View example on GitHub](https://github.com/nhost/nhost/tree/main/examples/custom-email-templates)
|
||||
|
||||
---
|
||||
|
||||
## Localisation
|
||||
|
||||
If Nhost finds a template that matches the recipent user’s locale, the email will be sent in that language. Use two-letter language codes to set the locale.
|
||||
English will always be used as the default if another language version is not found.
|
||||
|
||||
---
|
||||
|
||||
## Template variables
|
||||
|
||||
Use variables like `${displayName}` to make your templates more dynamic:
|
||||
|
||||
```html
|
||||
<!-- https://yourapp.com/email-templates/en/email-verify/body.html -->
|
||||
<h2>Confirm Email Change</h2>
|
||||
|
||||
<p>Hi, ${displayName}! Please click this link to verify your email:</p>
|
||||
|
||||
<p>
|
||||
<a href="${displayName}">Verify new email</a>
|
||||
</p>
|
||||
```
|
||||
nhost/emails/{two-letter-language-code}/{email-template}/[subject.txt, body.html]
|
||||
```
|
||||
|
||||
These variables can be used either in the template subject or body. The following variables are supported:
|
||||
Default templates for English (`en`) and French (`fr`) are automatically generated when the app is initialized with the [CLI](/platform/cli).
|
||||
|
||||
## Languages
|
||||
|
||||
The user's language is what decides what template to send. The user's language is stored in the `auth.users` table in the `locale` column. This `locale` column contains a two-letter language code in [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format.
|
||||
|
||||
This value is `en` by default for new users.
|
||||
|
||||
## Variables
|
||||
|
||||
The following variables are available to use in the email templates:
|
||||
|
||||
| Variable | Description |
|
||||
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| link | The full URL to the target of the transaction. This should be used in the main call to action. This is available in all templates. |
|
||||
| serverUrl | URL of the authentication server |
|
||||
| clientUrl | URL to your frontend app |
|
||||
| redirectTo | URL where the user will be redirected to after clicking the link and finishing the action of the email |
|
||||
| ticket | Ticket that is used to authorize the link request |
|
||||
| displayName | The display name of the user. |
|
||||
| email | The email of the user. |
|
||||
| locale | Locale of the user as a two-letter language code. E.g. "en". |
|
||||
|
||||
---
|
||||
Use variables like this: `${displayName}` in the email templates.
|
||||
|
||||
<!-- ## Developing emails templates locally -->
|
||||
**Example:** A email template to verify users' emails:
|
||||
|
||||
```html title="nhost/emails/en/email-verify/body.html"
|
||||
<h2>Verify You Email</h2>
|
||||
|
||||
<p>Hi, ${displayName}! Please click the link to verify your email:</p>
|
||||
|
||||
<p>
|
||||
<a href="${link}">Verify Email</a>
|
||||
</p>
|
||||
```
|
||||
|
||||
@@ -1,75 +1,28 @@
|
||||
---
|
||||
title: Authentication
|
||||
title: Nhost Authentication
|
||||
sidebar_label: Authentication
|
||||
sidebar_position: 1
|
||||
image: /img/og/platform/authentication.png
|
||||
---
|
||||
|
||||
# Authentication
|
||||
Nhost Authentication is a ready-to-use authentication service that is integrated with the [GraphQL API](/platform/graphql) and its permission system from Hasura.
|
||||
|
||||
Nhost provides a ready-to-use authentication service, integrated with Nhost JavaScript SDK. This makes it easy to build login flows with multiple sign-in methods.
|
||||
Nhost Authentication lets you authenticate users to your app using different sign-in methods:
|
||||
|
||||
## Getting Started
|
||||
|
||||
Sign up a user with the [Nhost JavaScript SDK](/reference/javascript):
|
||||
|
||||
```js
|
||||
import { NhostClient } from '@nhost/nhost-js';
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: 'https://[app-subdomain].nhost.run',
|
||||
});
|
||||
|
||||
await nhost.auth.signUp({
|
||||
email: 'joe@nhost.io',
|
||||
password: 'secret-password',
|
||||
});
|
||||
```
|
||||
- [Email and Password](/platform/authentication/sign-in-with-email-and-password)
|
||||
- [Magic Link](/platform/authentication/sign-in-with-magic-link)
|
||||
- [Phone Number (SMS)](/platform/authentication/sign-in-with-phone-number-sms)
|
||||
- [Google](/platform/authentication/sign-in-with-google)
|
||||
- [Facebook](/platform/authentication/sign-in-with-facebook)
|
||||
- [GitHub](/platform/authentication/sign-in-with-github)
|
||||
- [LinkedIn](/platform/authentication/sign-in-with-linkedin)
|
||||
- [Spotify](/platform/authentication/sign-in-with-spotify)
|
||||
|
||||
## How it works
|
||||
|
||||
1. A user signs up and the user information is added to the `auth.users` table.
|
||||
2. Nhost returns an [access token](#access-tokens) (JWT token) and the user's information.
|
||||
3. The user sends a request to the GraphQL API together with the access token.
|
||||
4. The GraphQL API reviews the access token to ensure the user is authorized to send the request.
|
||||
1. When a user signs up or is created, the user's information is inserted into the `auth.users` table in your database.
|
||||
2. Nhost returns an access token and a refresh token, together with the user's information.
|
||||
3. The user sends requests to Nhost services (GraphQL API, Authentication, Storage, Functions) with the access token as a header.
|
||||
4. The Nhost services use the user's access token to authorize the requests.
|
||||
|
||||
Nhost's authentication service is integrated with your database. All users are stored in the app's database under the `auth` schema and can be accessed using GraphQL:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
users {
|
||||
id
|
||||
displayName
|
||||
avatarUrl
|
||||
email
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tokens
|
||||
|
||||
Nhost authentication uses two tokens: Access tokens and refresh tokens.
|
||||
|
||||
[Nhost JavaScript SDK](/reference/javascript) automatically handles access and refresh tokens.
|
||||
|
||||
### Access tokens
|
||||
|
||||
An access token is used to authenticate and authorize a user when doing a GraphQL request.
|
||||
|
||||
Access tokens are cryptographically signed and cannot be revoked. They are only valid for 15 minutes. Users can request a new valid access token with a refresh token.
|
||||
|
||||
An access token includes a user's ID and roles. Here's an example:
|
||||
|
||||
```json
|
||||
{
|
||||
"https://hasura.io/jwt/claims": {
|
||||
"x-hasura-user-id": "c8ee8353-b886-4530-9089-631ea7fd4c8a",
|
||||
"x-hasura-default-role": "user",
|
||||
"x-hasura-allowed-roles": ["user", "me"]
|
||||
},
|
||||
"iat": 1595146465,
|
||||
"exp": 1595147365
|
||||
}
|
||||
```
|
||||
|
||||
### Refresh tokens
|
||||
|
||||
A refresh token is used to request a new access token. Refresh tokens are long-lived tokens stored in the database.
|
||||
Nhost's authentication service is integrated with your database. All users are stored in the `users` table under the `auth` schema.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"label": "OAuth providers",
|
||||
"position": 3
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
title: 'Sign-in methods'
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
Nhost supports a variety of sign-in methods:
|
||||
|
||||
---
|
||||
|
||||
## Email + password
|
||||
|
||||
To sign in a user with email and password, the user must first sign up:
|
||||
|
||||
```js
|
||||
await nhost.auth.signUp({
|
||||
email: 'joe@nhost.io',
|
||||
password: 'secret-password',
|
||||
});
|
||||
```
|
||||
|
||||
If you've turned on email verification in your app's **login settings**, a user will be sent a verification email upon signup. The user must click the verification link in the email before they can log in.
|
||||
|
||||
Once a user has been signed up (and optionally verified), you can sign them in:
|
||||
|
||||
```js
|
||||
await nhost.auth.signIn({
|
||||
email: 'joe@nhost.io',
|
||||
password: 'secret-password',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Passwordless email (magic link)
|
||||
|
||||
Users can sign in with passwordless email, also called magic link.
|
||||
|
||||
When a user signs in with passwordless email, Nhost will create the user if they don't already exist and send an email to the user.
|
||||
|
||||
When a user clicks the link in the email, they will be redirected to your app and automatically signed in.
|
||||
|
||||
Example in JavaScript:
|
||||
|
||||
```js
|
||||
await nhost.auth.signIn({
|
||||
email: 'joe@nhost.io',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Passwordless SMS
|
||||
|
||||
Users can sign in with passwordless SMS. The passwordless SMS sign in method has a flow:
|
||||
|
||||
First, "sign in" the user with a phone number.
|
||||
|
||||
```js
|
||||
await nnhost.auth.signIn({
|
||||
phoneNumber: '+467610337135',
|
||||
});
|
||||
```
|
||||
|
||||
This will create the user if the user does not already exist, and send a One Time Password (OTP) to the user's
|
||||
phone number.
|
||||
|
||||
Use the OTP to finalize the sign-in:
|
||||
|
||||
```js
|
||||
await nhost.auth.signIn({
|
||||
phoneNumber: '+467610337135',
|
||||
otp: 'otp-from-sms',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anonymous
|
||||
|
||||
A user can be created anonymously. This is useful for offering a limited version of your application to your users without having them sign in first.
|
||||
|
||||
An anonymous user gets a user ID with the `anonymous` role. This role can be used to [set permissions in Hasura](/platform/graphql/permissions).
|
||||
|
||||
### Deanonymize users
|
||||
|
||||
Anonymous users can be converted to "normal" users by deanonymize the user.
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: Sign In with Email and Password
|
||||
sidebar_label: Email and Password
|
||||
slug: /platform/authentication/sign-in-with-email-and-password
|
||||
image: /img/og/platform/sign-in-with-email-and-password.png
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with email and password.
|
||||
|
||||
The email and password sign-in method is enabled by default for all Nhost apps.
|
||||
|
||||
## Sign Up
|
||||
|
||||
Users must first sign up to be able to sign in with Email and Password.
|
||||
|
||||
**Example:** Sign up users using the [Nhost JavaScript client](/reference/javascript).
|
||||
|
||||
```js
|
||||
await nhost.auth.signUp({
|
||||
email: 'joe@example.com',
|
||||
password: 'secret-password'
|
||||
})
|
||||
```
|
||||
|
||||
If you've turned on email verification in your app's **login settings**, a user will be sent a verification email upon signup. The user must click the verification link in the email before they can sign in.
|
||||
|
||||
## Sign In
|
||||
|
||||
Once a user has been signed up (and optionally verified), you can sign them in.
|
||||
|
||||
**Example:** Sign in users using the [Nhost JavaScript client](/reference/javascript).
|
||||
|
||||
```js
|
||||
await nhost.auth.signIn({
|
||||
email: 'joe@example.com',
|
||||
password: 'secret-password'
|
||||
})
|
||||
```
|
||||
|
||||
## Verified Emails
|
||||
|
||||
You can decide if only verified emails should be able to sign in or not. Modify the **Only allow login for verified emails.** setting in the **Login Settings** section under **Users** in your Nhost app.
|
||||
|
||||
An email-verification email is automatically sent to the user during sign-up if your app only allows to sign in users with verified emails. You can also manually send the verification email to the user using [`nhost.auth.sendVerificationEmail()`](/reference/javascript/auth/send-verification-email).
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title: Sign In with Magic Link
|
||||
sidebar_label: Magic Link
|
||||
slug: /platform/authentication/sign-in-with-magic-link
|
||||
image: /img/og/platform/sign-in-with-magic-link.png
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with Magic Link, also called passwordless email.
|
||||
|
||||
The Magic Link sign-in method enables you to sign in users to your app using an email address, without requiring a password.
|
||||
|
||||
To sign in users with Magic Link is a two-step process:
|
||||
|
||||
1. Send a Magic Link to the user's email address.
|
||||
2. The user clicks the Magic Link in their email to sign in to your app.
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign up users with Magic Link in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
email: 'joe@example.com'
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Sign In with Phone Number (SMS)
|
||||
sidebar_label: Phone Number (SMS)
|
||||
slug: /platform/authentication/sign-in-with-phone-number-sms
|
||||
image: /img/og/platform/sign-in-with-phone-number-sms.png
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with a phone number (SMS).
|
||||
|
||||
## Sign In
|
||||
|
||||
To sign in users with a phone number is a two-step process:
|
||||
|
||||
1. Send a one-time password (OTP) to the user's phone number.
|
||||
2. The user uses the OTP to sign in
|
||||
|
||||
```js
|
||||
// Step 1: Send OTP to the user's phone number
|
||||
await nhost.auth.signIn({
|
||||
phoneNumber: '0011233213123'
|
||||
})
|
||||
|
||||
// Step 2: Sign in user using their phone number and OTP
|
||||
await nhost.auth.signIn({
|
||||
phoneNumber: '0011233213123'
|
||||
otp: '123456',
|
||||
})
|
||||
```
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
title: Sign in with Google
|
||||
sidebar_position: 1
|
||||
title: Sign In with Google
|
||||
sidebar_label: Google
|
||||
slug: /platform/authentication/sign-in-with-google
|
||||
image: /img/og/platform/sign-in-with-google.png
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with Google with your Nhost App.
|
||||
@@ -15,17 +16,17 @@ Follow this guide to sign in users with Google with your Nhost App.
|
||||
/>
|
||||
</p>
|
||||
|
||||
## Sign up for Google
|
||||
## Sign Up for Google
|
||||
|
||||
- Sign up for [Google Cloud](https://cloud.google.com/free) if you don’t have one already.
|
||||
- Sign up for [Google Cloud](https://cloud.google.com/free) if you don't have one already.
|
||||
|
||||
## Create a Google Cloud Project
|
||||
|
||||
> 💡 You can skip this step if you already have a Google Cloud project you want to use.
|
||||
|
||||
- Create a new Google Cloud project if you don’t already have a project you want to use.
|
||||
- Create a new Google Cloud project if you don't already have a project you want to use.
|
||||
|
||||
## Configure OAuth consent screen
|
||||
## Configure OAuth Consent Screen
|
||||
|
||||
- Search for **OAuth consent screen** in the top search bar in the Google Cloud Console.
|
||||
- Click on **OAuth consent screen** in the search results.
|
||||
@@ -33,16 +34,16 @@ Follow this guide to sign in users with Google with your Nhost App.
|
||||
|
||||
## **Edit app registration**
|
||||
|
||||
### OAuth consent screen
|
||||
### OAuth Consent Screen
|
||||
|
||||
- Fill in your App information.
|
||||
- Click **SAVE AND CONTINUE.**
|
||||
- Click **SAVE AND CONTINUE**.
|
||||
|
||||
### Scopes
|
||||
|
||||
- Click **SAVE AND CONTINUE**.
|
||||
|
||||
### Test user
|
||||
### Test User
|
||||
|
||||
- Click **SAVE AND CONTINUE**.
|
||||
|
||||
@@ -50,7 +51,7 @@ Follow this guide to sign in users with Google with your Nhost App.
|
||||
|
||||
- Click **BACK TO DASHBOARD**.
|
||||
|
||||
## Create credentials
|
||||
## Create Credentials
|
||||
|
||||
- Click on **Credentials** under **APIs & Services** in the left menu.
|
||||
- Click **+ CREATE CREDENTIALS** and then **OAuth client ID** in the top menu.
|
||||
@@ -60,17 +61,17 @@ Follow this guide to sign in users with Google with your Nhost App.
|
||||
|
||||
## Configure Nhost
|
||||
|
||||
- A modal appears with your Google Client ID and Client secret.
|
||||
- A modal appears with your Google Client ID and Client Secret.
|
||||
- Copy and paste the **Client ID** and **Client Secret** from Google to your Nhost OAuth settings for Google. Make sure the OAuth provider is enabled in Nhost.
|
||||
- Click the checkbox “**I have pasted the redirect URI into Google”**.
|
||||
- Click **Confirm settings**.
|
||||
|
||||
## Sign In users in your app
|
||||
## Sign In Users
|
||||
|
||||
Use the Nhost JavaScript client to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'google',
|
||||
});
|
||||
provider: 'google'
|
||||
})
|
||||
```
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
title: Sign in with Facebook
|
||||
sidebar_position: 2
|
||||
title: Sign In with Facebook
|
||||
sidebar_label: Facebook
|
||||
slug: /platform/authentication/sign-in-with-facebook
|
||||
image: /img/og/platform/sign-in-with-facebook.png
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with Facebook with your Nhost App.
|
||||
@@ -15,53 +16,53 @@ Follow this guide to sign in users with Facebook with your Nhost App.
|
||||
/>
|
||||
</p>
|
||||
|
||||
## Create Facebook account
|
||||
## Create Facebook Account
|
||||
|
||||
- Create a new [Facebook account](https://www.facebook.com/) if you don't have one already.
|
||||
|
||||
## Create Facebook App
|
||||
|
||||
- Go to [Meta for Developers](https://developers.facebook.com/).
|
||||
- Click **My Apps** in the top right
|
||||
- Click **My Apps** in the top right.
|
||||
- Click **Create App** in the top right.
|
||||
- Select your **app type** (e.g. Consumer).
|
||||
- Click **Next**.
|
||||
- Fill in the **Display name.**
|
||||
- Fill in the **Display name**.
|
||||
- Click **Create app**.
|
||||
|
||||
## Set up Facebook Login
|
||||
|
||||
- Click on Add Product in the left menu.
|
||||
- Click on Setup in the Facebook login card.
|
||||
- Click on **Add Product** in the left menu.
|
||||
- Click on **Setup** on the Facebook login card.
|
||||
- **Don't** complete the quickstart. Instead, follow the next step.
|
||||
- Click on **Settings** under **Facebook Login** in the left menu.
|
||||
- Make sure **Embedded Browser OAuth Login** is set to **Yes**.
|
||||
- Fill in **Valid OAuth Redirect URIs** with your **OAuth Callback URL** from Nhost.
|
||||
- Click **Save changes**.
|
||||
|
||||
## Activate Facebook permissions and features
|
||||
## Activate Facebook Permissions and Features
|
||||
|
||||
To make sure we can fetch all user data (email, profile picture and name). For that we need to enable **email** and **public_profile** permissions.
|
||||
|
||||
- Click on App Review and Permission and Features in the left menu
|
||||
- Search and for **email** in the **Search Permissions and Features** search box**.**
|
||||
- Click on App Review and Permission and Features in the left menu.
|
||||
- Search for **email** in the **Search Permissions and Features** search box.
|
||||
- Click on Request advanced access and complete the steps.
|
||||
- Search and for **public_profile** in the **Search Permissions and Features** search box**.**
|
||||
- Search for **public_profile** in the **Search Permissions and Features** search box.
|
||||
- Click on **Request advanced access** and complete the steps.
|
||||
|
||||
## Configure Nhost
|
||||
|
||||
- Click **Settings** and then **Basic** in the left menu.
|
||||
- Copy and paste the **App ID (Client ID)** and **App secret (Client Secret)** from Facebook to your Nhost OAuth settings for Facebook. Make sure the [OAuth provider is enabled in Nhost](/platform/authentication/social-sign-in#enabling-social-sign-in-provider).
|
||||
- Copy and paste the **App ID (Client ID)** and **App secret (Client Secret)** from Facebook to your Nhost OAuth settings for Facebook. Make sure the OAuth provider is enabled in Nhost.
|
||||
- Click the checkbox “**I have pasted the redirect URI into Facebook”**.
|
||||
- Click **Confirm settings**.
|
||||
|
||||
## Sign In users in your app
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'facebook',
|
||||
});
|
||||
provider: 'facebook'
|
||||
})
|
||||
```
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
title: Sign in with GitHub
|
||||
sidebar_position: 3
|
||||
title: Sign In with GitHub
|
||||
sidebar_label: GitHub
|
||||
slug: /platform/authentication/sign-in-with-github
|
||||
image: /img/og/platform/sign-in-with-github.png
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with GitHub with your Nhost App.
|
||||
@@ -15,7 +16,7 @@ Follow this guide to sign in users with GitHub with your Nhost App.
|
||||
/>
|
||||
</p>
|
||||
|
||||
## Create GitHub account
|
||||
## Create GitHub Account
|
||||
|
||||
- Create a new [GitHub account](https://github.com/signup) if you don't have one already.
|
||||
|
||||
@@ -23,30 +24,30 @@ Follow this guide to sign in users with GitHub with your Nhost App.
|
||||
|
||||
- Create a new OAuth application [(direct link)](https://github.com/settings/applications/new) by:
|
||||
- Click on your profile photo in the top right.
|
||||
- Click on Settings
|
||||
- Click on Settings.
|
||||
- In the left menu, click Developer settings at the bottom.
|
||||
- Click on Oauth Apps in the left menu
|
||||
- Click on New OAuth App button in the top right
|
||||
- Click on Oauth Apps in the left menu.
|
||||
- Click on New OAuth App button in the top right.
|
||||
|
||||
## GitHub OAuth App information
|
||||
## GitHub OAuth App Information
|
||||
|
||||
- Fill in Application Name
|
||||
- Fill in Homepage URL
|
||||
- Fill in **Authorization callback URL** with your OAuth Callbacke URL from Nhost
|
||||
- Fill in Application Name.
|
||||
- Fill in Homepage URL.
|
||||
- Fill in **Authorization callback URL** with your OAuth Callbacke URL from Nhost.
|
||||
|
||||
## Configure Nhost
|
||||
|
||||
- Click Generate a new client secret to generate a OAuth client secret.
|
||||
- Copy and paste the **Client ID** and **Client Secret** from GitHub to your Nhost OAuth settings for GitHub. Make sure the [OAuth provider is enabled in Nhost](/platform/authentication/social-sign-in#enabling-social-sign-in-provider).
|
||||
- Click Generate a new client secret to generate an OAuth client secret.
|
||||
- Copy and paste the **Client ID** and **Client Secret** from GitHub to your Nhost OAuth settings for GitHub. Make sure the OAuth provider is enabled in Nhost.
|
||||
- Click the checkbox “**I have pasted the redirect URI into GitHub”**.
|
||||
- Click **Confirm settings**.
|
||||
|
||||
## Sign In users in your app
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'github',
|
||||
});
|
||||
provider: 'github'
|
||||
})
|
||||
```
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
title: Sign in with LinkedIn
|
||||
sidebar_position: 4
|
||||
title: Sign In with LinkedIn
|
||||
sidebar_label: LinkedIn
|
||||
slug: /platform/authentication/sign-in-with-linkedin
|
||||
image: /img/og/platform/sign-in-with-linkedin.png
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with LinkedIn with your Nhost App.
|
||||
@@ -15,11 +16,11 @@ Follow this guide to sign in users with LinkedIn with your Nhost App.
|
||||
/>
|
||||
</p>
|
||||
|
||||
## Create LinkedIn account
|
||||
## Create LinkedIn Account
|
||||
|
||||
- Create a [LinkedIn account](https://linkedin.com/) if you don't have one already.
|
||||
|
||||
## Create LinkedIn OAuth App
|
||||
## Create a LinkedIn OAuth App
|
||||
|
||||
- Go to the [LinkedIn Developer Dashboard](https://www.linkedin.com/developers/apps).
|
||||
- Click on Create App in the top right.
|
||||
@@ -27,10 +28,10 @@ Follow this guide to sign in users with LinkedIn with your Nhost App.
|
||||
- Click **“I have read and agree to these terms”**.
|
||||
- Click **Create app** in the bottom right.
|
||||
|
||||
## LinkedIn OAuth App information
|
||||
## LinkedIn OAuth App Information
|
||||
|
||||
- Click on **Auth** in the top menu.
|
||||
- Click on the **pen icon** under **OAuth 2.0 settings** and right next to **Authorized redirect URLs for your app.**
|
||||
- Click on the **pen icon** under **OAuth 2.0 settings** and right next to **Authorized redirect URLs for your app**.
|
||||
- Click **Add redirect URL**.
|
||||
- Copy and past the **OAuth Callback URL** from Nhost.
|
||||
- Click **Update**.
|
||||
@@ -43,17 +44,17 @@ Follow this guide to sign in users with LinkedIn with your Nhost App.
|
||||
|
||||
## Enable Auth for your LinkedIn OAuth App
|
||||
|
||||
- Click on **Products** in the top menu
|
||||
- Click on **Products** in the top menu.
|
||||
- Click Select on the **Sign In with LinkedIn**.
|
||||
- Check the checkbox **I have read and agree to these terms.**
|
||||
- Check the checkbox **I have read and agree to these terms**.
|
||||
- Click **Add product**.
|
||||
|
||||
## Sign In users in your app
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'linkedin',
|
||||
});
|
||||
provider: 'linkedin'
|
||||
})
|
||||
```
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
title: Sign in with Spotify
|
||||
sidebar_position: 5
|
||||
title: Sign In with Spotify
|
||||
sidebar_label: Spotify
|
||||
slug: /platform/authentication/sign-in-with-spotify
|
||||
image: /img/og/platform/sign-in-with-spotify.png
|
||||
---
|
||||
|
||||
Follow this guide to sign in users with Spotify with your Nhost App.
|
||||
@@ -15,7 +16,7 @@ Follow this guide to sign in users with Spotify with your Nhost App.
|
||||
/>
|
||||
</p>
|
||||
|
||||
## Create Spotify account
|
||||
## Create Spotify Account
|
||||
|
||||
- Create a new [Spotify account](https://www.spotify.com/) if you don't have one already.
|
||||
|
||||
@@ -23,13 +24,13 @@ Follow this guide to sign in users with Spotify with your Nhost App.
|
||||
|
||||
- Go to the [Spotify Developer Dashboard](https://developer.spotify.com/dashboard/).
|
||||
- Click on CREATE AN APP.
|
||||
- Fill in a App name and App description
|
||||
- Check the box to aggre Spotify's [Developer Terms of Service](https://developer.spotify.com/terms) and [Branding Guidelines](https://developer.spotify.com/branding-guidelines).
|
||||
- Fill in an App name and App description.
|
||||
- Check the box to agree Spotify's [Developer Terms of Service](https://developer.spotify.com/terms) and [Branding Guidelines](https://developer.spotify.com/branding-guidelines).
|
||||
|
||||
## Configure OAuth Callback URL
|
||||
|
||||
- Click EDIT SETTINGS
|
||||
- A modal appears
|
||||
- Click EDIT SETTINGS.
|
||||
- A modal appears.
|
||||
- Fill in **Redirect URIs** with your **OAuth Callback URL** from Nhost.
|
||||
- Click ADD to add the OAuth callback URL.
|
||||
- Click SAVE.
|
||||
@@ -41,12 +42,12 @@ Follow this guide to sign in users with Spotify with your Nhost App.
|
||||
- Click the checkbox “**I have pasted the redirect URI into Spotify”**.
|
||||
- Click **Confirm settings**.
|
||||
|
||||
## Sign In users in your app
|
||||
## Sign In Users
|
||||
|
||||
Use the [Nhost JavaScript client](/reference/javascript) to sign in users in your app:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'spotify',
|
||||
});
|
||||
provider: 'spotify'
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Sign-In Methods",
|
||||
"position": 2
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
---
|
||||
title: 'OAuth Providers'
|
||||
slug: /platform/authentication/social-sign-in
|
||||
title: 'Sign-In Methods'
|
||||
slug: /platform/authentication/sign-in-methods
|
||||
image: /img/og/platform/sign-in-methods.png
|
||||
---
|
||||
|
||||
Nhost Auth supports the following social sign-in providers:
|
||||
Nhost Authentication support the following sign-in methods:
|
||||
|
||||
- [Email and Password](/platform/authentication/sign-in-with-email-and-password)
|
||||
- [Magic Link](/platform/authentication/sign-in-with-magic-link)
|
||||
- [Phone Number (SMS)](/platform/authentication/sign-in-with-phone-number-sms)
|
||||
- [Google](/platform/authentication/sign-in-with-google)
|
||||
- [Facebook](/platform/authentication/sign-in-with-facebook)
|
||||
- [GitHub](/platform/authentication/sign-in-with-github)
|
||||
- [LinkedIn](/platform/authentication/sign-in-with-linkedin)
|
||||
- [Spotify](/platform/authentication/sign-in-with-spotify)
|
||||
|
||||
---
|
||||
|
||||
## Enabling Social Sign-In Provider
|
||||
|
||||
To start with social sign-in, select your app in Nhost Console and go to **Users** → **Login settings**.
|
||||
|
||||
You need to set client ID and client secret for each provider that you want to enable.
|
||||
|
||||
---
|
||||
You need to set the Client ID and Client Secret for each provider that you want to enable.
|
||||
|
||||
## Implementing sign-in experience
|
||||
|
||||
@@ -29,19 +29,17 @@ Here's an example of how to implement sign-in with GitHub:
|
||||
|
||||
```js
|
||||
nhost.auth.signIn({
|
||||
provider: 'github',
|
||||
});
|
||||
provider: 'github'
|
||||
})
|
||||
```
|
||||
|
||||
Users are redirected to your Nhost app's **client URL** by default. By default your Nhost app's client URL is set to `http://localhost:3000`. You can change the value of your client URL in the Nhost console by going to **Users** → **Login settings** → **Client URL**.
|
||||
|
||||
---
|
||||
Users are redirected to your Nhost app's **client URL** by default. By default, your Nhost app's client URL is set to `http://localhost:3000`. You can change the value of your client URL in the Nhost console by going to **Users** → **Login settings** → **Client URL**.
|
||||
|
||||
## Provider OAuth scopes
|
||||
|
||||
Scopes are a mechanism in OAuth to allow or limit an application's access to a user's account.
|
||||
|
||||
By default, Nhost sets the scope to get the name, email and avatar for each user. Editing scope is not currently supported.
|
||||
By default, Nhost sets the scope to get the name, email, and avatar url for each user. Editing scope is not currently supported.
|
||||
|
||||
## Provider OAuth Tokens
|
||||
|
||||
74
docs/docs/platform/authentication/tokens.mdx
Normal file
74
docs/docs/platform/authentication/tokens.mdx
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: Tokens
|
||||
sidebar_label: Tokens
|
||||
sidebar_position: 10
|
||||
image: /img/og/platform/tokens.png
|
||||
---
|
||||
|
||||
Nhost Authentication makes use of two types of tokens:
|
||||
|
||||
- **Access token** - used to authenticate a user and access APIs.
|
||||
- **Refresh token** - used to get a new access token.
|
||||
|
||||
Users get both an access token and a refresh token when they sign in.
|
||||
|
||||
:::info
|
||||
If you're using the [Nhost JavaScript client](/reference/javascript), all tokens are automatically set and updated for you. But it can still be good to understand how they work.
|
||||
:::
|
||||
|
||||
## Access Token
|
||||
|
||||
An access token (also called [JSON Web Token or JWT](https://en.wikipedia.org/wiki/JSON_Web_Token)) contains information about the user such as the user id. Users send this token to the Nhost services (GraphQL, Auth, Storage, Serverless Functions) to let the services know who's making the request so the services can verify the user's identity and resolve the correct permissions.
|
||||
|
||||
The access token is added as an `Authorization` header when making a request, like this:
|
||||
|
||||
```http title="Header"
|
||||
Authorization: Bearer <access_token>
|
||||
```
|
||||
|
||||
Here's an example of an encoded access token:
|
||||
|
||||
```
|
||||
eyJhbGciOiJIUzI1NiJ9.eyJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsibWUiLCJ1c2VyIl0sIngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6InVzZXIiLCJ4LWhhc3VyYS11c2VyLWlkIjoiMTUzODYzZjktZTQwMC00Njg2LTgyMTEtMzI0OGNjYWY2MGJhIiwieC1oYXN1cmEtdXNlci1pcy1hbm9ueW1vdXMiOiJmYWxzZSJ9LCJzdWIiOiIxNTM4NjNmOS1lNDAwLTQ2ODYtODIxMS0zMjQ4Y2NhZjYwYmEiLCJpc3MiOiJoYXN1cmEtYXV0aCIsImlhdCI6MTY1Mzg5MjA5NCwiZXhwIjoxNjUzODkyOTk0fQ.9nVL2Lj8KWBW3WrjJr4tPNH3_29qJKKKSDRNYebhiHI
|
||||
```
|
||||
|
||||
The decoded payload of this access token is a JSON object that looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"https://hasura.io/jwt/claims": {
|
||||
"x-hasura-allowed-roles": ["me", "user"],
|
||||
"x-hasura-default-role": "user",
|
||||
"x-hasura-user-id": "153863f9-e400-4686-8211-3248ccaf60ba",
|
||||
"x-hasura-user-is-anonymous": "false"
|
||||
},
|
||||
"sub": "153863f9-e400-4686-8211-3248ccaf60ba",
|
||||
"iss": "hasura-auth",
|
||||
"iat": 1653892094,
|
||||
"exp": 1653892994
|
||||
}
|
||||
```
|
||||
|
||||
The token contains information about the user id, default role, allowed roles, if the user is anonymous or not, and other metadata.
|
||||
|
||||
The claims under `https://hasura.io/jwt/claims` are the same claims that are used by the GraphQL API to create [permissions](/platform/graphql/permissions). The claims (`x-hasura-*`) are also called permission variables. It's possible to add custom [permission variables](/platform/graphql/permissions#custom-permission-variables).
|
||||
|
||||
:::info
|
||||
You can manually decode an access token using [JWT.io](https://jwt.io/).
|
||||
:::
|
||||
|
||||
The token is cryptographically signed by Nhost Authentication, which means that all other Nhost services can trust the information in the token.
|
||||
|
||||
:::info
|
||||
Use the `NHOST_JWT_SECRET` [system environment variable](/platform/environment-variables#system-environment-variables) to verify access tokens in [Serverless Functions](/platform/serverless-functions). Here's a guide on how to [Get the authenticated user in a Serverless Function](https://github.com/nhost/nhost/discussions/278).
|
||||
:::
|
||||
|
||||
The access token can not be revoked. Instead, the token is only valid for 15 minutes. The user can get a new access token by using the refresh token.
|
||||
|
||||
## Refresh Token
|
||||
|
||||
A refresh token is used to request a new access token. Refresh tokens are long-lived tokens stored in the database in the `auth.refresh_tokens` table.
|
||||
|
||||
Refresh tokens are valid for 30 days.
|
||||
|
||||
To revoke a refresh token, simply delete it from the database.
|
||||
@@ -1,87 +0,0 @@
|
||||
---
|
||||
title: 'User management'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Users are saved in the database in the `auth.users` table and are accessible via the GraphQL API.
|
||||
|
||||
---
|
||||
|
||||
## Querying users
|
||||
|
||||
Example of getting all users in GraphQL:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
users {
|
||||
id
|
||||
displayName
|
||||
email
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example of getting one user in GraphQL:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
user(id: "<user-id>") {
|
||||
id
|
||||
displayName
|
||||
email
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Creating users
|
||||
|
||||
Users should be created using the sign-up or sign-in flows as described under [sign-in methods](/platform/authentication/sign-in-methods).
|
||||
|
||||
**Never** create users directly via GraphQL or database. **Never** modify the `auth.users` table. **Never** modify the GraphQL root queries.
|
||||
|
||||
You can update the permissions of the `auth.users` table.
|
||||
|
||||
---
|
||||
|
||||
## Roles
|
||||
|
||||
Each user can have one or multiple roles for API requests. You can see the roles of a user and set a default role in Nhost Console under **Users**.
|
||||
|
||||
Every GraphQL request is made with a specific role. This role will be used to resolve permissions when querying the database. In other words, every user can have multiple roles, but only one role will be applied for any given GraphQL request.
|
||||
|
||||
### Defaults
|
||||
|
||||
For new apps, the following roles are available:
|
||||
|
||||
- Available roles: `user` and `me`
|
||||
- Default role: `user`
|
||||
|
||||
### Public role
|
||||
|
||||
If the user is not signed in, the `public` role will be used.
|
||||
|
||||
### Set request role in GraphQL
|
||||
|
||||
When no request role is specified, the user's default role will be used:
|
||||
|
||||
```js
|
||||
await nhost.graphql.request(QUERY, {});
|
||||
```
|
||||
|
||||
Make a GraphQL request with the `me` role:
|
||||
|
||||
```js
|
||||
await nhost.graphql.request(
|
||||
QUERY,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-role': 'me',
|
||||
},
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
If the request is not part of the user's roles, the request will fail.
|
||||
114
docs/docs/platform/authentication/users.mdx
Normal file
114
docs/docs/platform/authentication/users.mdx
Normal file
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: Users
|
||||
sidebar_label: Users
|
||||
sidebar_position: 1
|
||||
image: /img/og/platform/users.png
|
||||
---
|
||||
|
||||
Users are stored in the database in the `users` table in the `auth` schema.
|
||||
|
||||
## Get User Information using GraphQL
|
||||
|
||||
**Example:** Get all users.
|
||||
|
||||
```graphql
|
||||
query {
|
||||
users {
|
||||
id
|
||||
displayName
|
||||
email
|
||||
metadata
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example:** Get a single user.
|
||||
|
||||
```graphql
|
||||
query {
|
||||
user(id: "<user-id>") {
|
||||
id
|
||||
displayName
|
||||
email
|
||||
metadata
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Users
|
||||
|
||||
Users should be created using the sign-up or sign-in flows as described under [sign-in methods](/platform/authentication/sign-in-methods).
|
||||
|
||||
- **Never** create users directly via GraphQL or database.
|
||||
- **Never** modify the `auth.users` table.
|
||||
- **Never** modify the GraphQL root queries or fields for any of the tables in the `auth` schema.
|
||||
|
||||
You're allowed to:
|
||||
|
||||
- Add and remove your GraphQL relationships for the `users` table and other tables in the `auth` schema.
|
||||
- Create, edit and delete permissions for the `users` table and other tables in the `auth` schema.
|
||||
|
||||
## Roles
|
||||
|
||||
Each user can have one or multiple roles for API requests. You can see the roles of a user and set a default role in Nhost Console under **Users**.
|
||||
|
||||
Every GraphQL request is made with a specific role. This role will be used to resolve permissions when querying the database. In other words, every user can have multiple roles, but only one role will be applied for any given GraphQL request.
|
||||
|
||||
### Default Role
|
||||
|
||||
The default role is used when no role is specified in the GraphQL request. By default, users' default role is `user`.
|
||||
|
||||
### Allowed Roles
|
||||
|
||||
By default, users have two allowed roles:
|
||||
|
||||
- `user`
|
||||
- `me`
|
||||
|
||||
### Public Role
|
||||
|
||||
The `public` role is used to resolve GraphQL permissions for unauthenticated users.
|
||||
|
||||
### Set Role for GraphQL Requests
|
||||
|
||||
When no request role is specified, the user's default role will be used:
|
||||
|
||||
```js
|
||||
await nhost.graphql.request(QUERY, {})
|
||||
```
|
||||
|
||||
Make a GraphQL request with the `me` role:
|
||||
|
||||
```js
|
||||
await nhost.graphql.request(
|
||||
QUERY,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
'x-hasura-role': 'me'
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
If the request is not part of the user's allowed roles, the request will fail.
|
||||
|
||||
## Metadata
|
||||
|
||||
You can store custom information about the user in the `metadata` column of the `users` table. The `metadata` column is of type JSONB so any JSON data can be stored.
|
||||
|
||||
This is how you attach custom metadata to a user during sign-up:
|
||||
|
||||
```js
|
||||
await nhost.auth.signUp({
|
||||
email: 'joe@example.com',
|
||||
password: 'secret-password',
|
||||
options: {
|
||||
metadata: {
|
||||
birthYear: 1989,
|
||||
town: 'Stockholm',
|
||||
likes: ['Postgres', 'GraphQL', 'Hasura', 'Authentication', 'Storage', 'Serverless Functions']
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
title: 'CLI'
|
||||
sidebar_position: 11
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
Nhost CLI lets you run Nhost's development environment locally on macOS, Linux and Windows.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Download and install Nhost CLI for your platform by running this command in your terminal:
|
||||
|
||||
```bash
|
||||
sudo curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | bash
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [Git](https://git-scm.com/downloads) must be installed on your system
|
||||
- [Docker](https://www.docker.com/get-started) must be installed and running when using Nhost CLI
|
||||
|
||||
### Function runtimes
|
||||
|
||||
To run serverless functions locally, you must have the appropriate runtimes installed on your machine:
|
||||
|
||||
- JavaScript and TypeScript functions: `Node.js 14.*`
|
||||
|
||||
For Node.js, you will also need to have [express](https://www.npmjs.com/package/express) installed in your repository:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm install --save-dev express @types/express
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn add -D express @types/express
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
[Read more about runtimes](/platform/serverless-functions)
|
||||
|
||||
---
|
||||
|
||||
## Windows support
|
||||
|
||||
If you have Windows Subsystem for Linux and `curl` in your Windows environment, you run the command following the instructions above. It will download the `.exe` binary to your current working directory.
|
||||
|
||||
If you do not have the above dependencies, download and install the latest release manually from [GitHub releases](https://github.com/nhost/cli/releases).
|
||||
|
||||
---
|
||||
|
||||
## Apple silicon (M1)
|
||||
|
||||
As of late 2021, Hasura does not yet have an M1 optimized version for their GraphQL engine, which Nhost depends on.
|
||||
|
||||
If you have a MacBook with an M1 chip, the CLI will automatically change the image used in `nhost/config.yaml` of your app:
|
||||
|
||||
```yml
|
||||
services:
|
||||
hasura:
|
||||
image: fedormelexin/graphql-engine-arm64
|
||||
```
|
||||
|
||||
This will run the Hasura GraphQL engine using Rosetta on your machine until an M1 optimized image is launched.
|
||||
|
||||
---
|
||||
|
||||
## Upgrading
|
||||
|
||||
If you already Nhost CLI installed, you can upgrade your installation:
|
||||
|
||||
```bash
|
||||
# sudo permissions needed
|
||||
sudo nhost upgrade
|
||||
```
|
||||
|
||||
The `upgrade` command was added in `0.5.0`.
|
||||
81
docs/docs/platform/cli.mdx
Normal file
81
docs/docs/platform/cli.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: 'CLI'
|
||||
sidebar_position: 11
|
||||
image: /img/og/platform/cli.png
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
Nhost CLI, or `nhost`, is a command-line interface that lets you run and manage Nhost apps locally on Mac, Linux and Windows (WSL2).
|
||||
|
||||
This means you get a full-featured Nhost app running locally on your machine:
|
||||
|
||||
- Postgres Database
|
||||
- Hasura GraphQL API
|
||||
- Hasura Console
|
||||
- Auth
|
||||
- Storage
|
||||
- Serverless Functions
|
||||
|
||||
This way, you can develop and test local changes before you deploy them live. The CLI automatically tracks:
|
||||
|
||||
- Postgres database migrations
|
||||
- Hasura metadata
|
||||
- Serverless functions
|
||||
|
||||
It's recommended to commit and push changes to GitHub and use the [GitHub integration](/platform/github-integration) for Nhost to automatically deploy those changes live.
|
||||
|
||||
## Installation
|
||||
|
||||
To download and install Nhost CLI, run the following command:
|
||||
|
||||
```bash
|
||||
sudo curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | bash
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
The following dependencies are required:
|
||||
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
- [Docker](https://www.docker.com/get-started) (must be running while using the CLI)
|
||||
|
||||
## Get started
|
||||
|
||||
Start by authenticating yourself to Nhost Cloud:
|
||||
|
||||
```
|
||||
nhost login
|
||||
```
|
||||
|
||||
Once signed in, you can list all your Nhost apps using:
|
||||
|
||||
```
|
||||
nhost list
|
||||
```
|
||||
|
||||
Let's start by initializing a remote Nhost app locally with the following command:
|
||||
|
||||
```
|
||||
nhost init --remote
|
||||
```
|
||||
|
||||
Pick a Nhost app to use then change the directory once the app initialization is completed:
|
||||
|
||||
```
|
||||
cd my_test_app
|
||||
```
|
||||
|
||||
Then start the Nhost app locally:
|
||||
|
||||
```
|
||||
nhost up
|
||||
```
|
||||
|
||||
Hasura Console starts automatically and your Nhost app is running locally with the backend URL: `http://localhost:1337`.
|
||||
|
||||
## What's next?
|
||||
|
||||
- Read our in-depth guide on [Get started with Nhost CLI](/platform/overview/get-started-with-nhost-cli)
|
||||
- [CLI commands reference](/reference/cli)
|
||||
89
docs/docs/platform/database/event-triggers.mdx
Normal file
89
docs/docs/platform/database/event-triggers.mdx
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: 'Event triggers'
|
||||
sidebar_position: 2
|
||||
image: /img/og/platform/event-triggers.png
|
||||
---
|
||||
|
||||
Event Triggers enable you to invoke webhooks when a database event happens. Event Triggers are typically used to do post-processing tasks, using custom backend code, based on database events.
|
||||
|
||||
Event Triggers are associated with a specific table in the database the following event types are available:
|
||||
|
||||
- **INSERT** - A row is inserted into a table
|
||||
- **UPDATE** - A row is updated in a table
|
||||
- **DELETE** - A row is deleted from a table
|
||||
|
||||
Event Triggers can also be triggered manually in the Hasura Console.
|
||||
|
||||
### Example Use Case
|
||||
|
||||
Let's say you're building an e-commerce application and you want to send an email to the customer when a new order is placed. Orders are stored in the `orders` table in your database.
|
||||
|
||||
To send out an email every time a new order is placed, you create an event trigger that listens for the `INSERT` event on the `orders` table. Now every time an order is placed, the even trigger invokes a webhook with the order information, and the webhook sends out the email.
|
||||
|
||||
## Create Event Trigger
|
||||
|
||||
Event Triggers are managed in the Hasura Console. Select **Events** in the main menu and click **Create** to add an Event Trigger.
|
||||
|
||||
<video width="99%" autoPlay muted loop controls="true">
|
||||
<source src="/videos/hasura-create-event-trigger.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
## Event Triggers and Serverless Functions
|
||||
|
||||
Event Triggers and [Serverless Functions](/platform/serverless-functions) is a perfect combination to build powerful database-backend logic. Every Serverless Function is exposed as an HTTP endpoint that can be used as a webhook for your Event Triggers.
|
||||
|
||||
### Format
|
||||
|
||||
When using Serverless Functions as webhooks you should configure the webhook using a combination of environment variables and endpoints like this:
|
||||
|
||||
```
|
||||
{{NHOST_BACKEND_URL}}/v1/functions/orders-insert-send-email
|
||||
```
|
||||
|
||||

|
||||
|
||||
The `NHOST_BACKEND_URL` is a [system environment variable](/platform/environment-variables#system-environment-variables) and available in production and in development environments using the [CLI](/platform/cli).
|
||||
|
||||
### Security
|
||||
|
||||
To make sure incoming requests to your webhook comes from Hasura, and not some malicious third party, you can use a shared webhook secret between Hasura and your webhook handler (e.g. your Serverless Function).
|
||||
|
||||
It is recommended to use the `NHOST_WEBHOOK_SECRET`, which is a [system environment variable](/platform/environment-variables#system-environment-variables) and available in production and in development environments using the [CLI](/platform/cli). The `NHOST_WEBHOOK_SECRET` is available both in Hasura and in every Serverless Function.
|
||||
|
||||
To set this up is a two-step process:
|
||||
|
||||
- Step 1: Add the header `nhost-webhook-secret` with the value `NHOST_WEBHOOK_SECRET` (From env var) when creating the Event Trigger in the Hasura Console.
|
||||
|
||||

|
||||
|
||||
- Step 2: Check the header `nhost-webhook-secret` for incoming requests and make sure the header is the same as the environment variable `NHOST_WEBHOOK_SECRET`.
|
||||
|
||||
Here is an example of how to check the header in a Serverless Function:
|
||||
|
||||
```js
|
||||
export default async function handler(req, res) {
|
||||
// Check header to make sure the request comes from Hasura
|
||||
if (req.headers['nhost-webhook-secret'] !== process.env.NHOST_WEBHOOK_SECRET) {
|
||||
return res.status(400).send('Incorrect webhook secret')
|
||||
}
|
||||
|
||||
// Do something
|
||||
// Example:
|
||||
// - Send an email
|
||||
// - Create a subscription in Stripe
|
||||
// - Generate a PDF
|
||||
// - Send a message to Slack or Discord
|
||||
// - Update some data in the database
|
||||
|
||||
console.log(JSON.stringify(req.body, null, 2))
|
||||
|
||||
return res.send('OK')
|
||||
}
|
||||
```
|
||||
|
||||
The `NHOST_WEBHOOK_SECRET` is a [system environment variable](/platform/environment-variables#system-environment-variables) and available in production and in development environments using the [CLI](/platform/cli).
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the full [Event Triggers documentation from Hasura](https://hasura.io/docs/latest/graphql/core/event-triggers/index/).
|
||||
- Learn about the [GraphQL API](/platform/graphql).
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
title: 'Database'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Every Nhost app comes with a Postgres database. Postgres is the world's most advanced open-source relational database and the most popular SQL database among developers. The database is hosted with Amazon RDS.
|
||||
|
||||
Tables are managed in the Hasura Console.
|
||||
|
||||
---
|
||||
|
||||
## Creating tables
|
||||
|
||||
1. In Hasura Console, go to the **Data** tab, select the **public** schema in the left menu and click **Create Table**
|
||||
2. Enter a table name
|
||||
3. Add table columns
|
||||
4. Add a primary key (usually the ID column)
|
||||
5. (Optional) Add foreign keys
|
||||
6. (Optional) Add unique keys
|
||||
7. Click **Add Table**
|
||||
|
||||
When a table is created, the table is created in Postgres and added to your GraphQL API.
|
||||
|
||||
#### Schemas
|
||||
|
||||
You should use the `public` schema when developing your app. `auth` and `storage` are reserved for system functionality like user and file management. You are allowed to modify permissions for tables in the `auth` and `storage` schemas, however.
|
||||
|
||||
---
|
||||
|
||||
## Modifying table schema
|
||||
|
||||
1. In Hasura Console, go to the **Data** tab and click on the table you want to edit in the left menu
|
||||
2. Click **Modify**
|
||||
3. Modify or add table columns
|
||||
|
||||
#### Track foreign-key relations
|
||||
|
||||
1. Click on Data in the top menu.
|
||||
2. A list of untracked foreign-key relations is presented.
|
||||
3. Click Track All (recommended) or click Track for each relationship you want to track.
|
||||
|
||||
---
|
||||
|
||||
## Deleting tables
|
||||
|
||||
1. In Hasura Console, go to the **Data** tab and select the table you want to delete in the left menu
|
||||
2. Click **Modify**
|
||||
3. Scroll to the bottom of the page and click **Delete table** to open the confirmation dialog
|
||||
144
docs/docs/platform/database/index.mdx
Normal file
144
docs/docs/platform/database/index.mdx
Normal file
@@ -0,0 +1,144 @@
|
||||
---
|
||||
title: 'Database'
|
||||
sidebar_position: 1
|
||||
image: /img/og/platform/database.png
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
Every Nhost app comes with its own [Postgres database](https://postgres.org/). Postgres is the world's most advanced open-source relational database and it's the most [popular SQL database for developers](https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-databases).
|
||||
|
||||
:::info
|
||||
|
||||
It's currently not possible to connect directly to the Postgres database via a connection string. We're working on making the database available soon. You can follow [this issue on GitHub](https://github.com/nhost/nhost/issues/113).
|
||||
|
||||
:::
|
||||
|
||||
The database is managed via the Hasura Console where you can manage the database via an intuative UI. You can also use SQL to directly interact with the database via the Hasura Console.
|
||||
|
||||
## Hasura Console
|
||||
|
||||
Hasura Console is where you manage your database. This is where you create and manage tables, schemas, and data.
|
||||
|
||||
Open the Hasura Console by clicking on **Data** in the top menu in the Nhost Dashboard, copy the **admin secret**, and click **Open Hasura**. Use the **admin secret** to sign in.
|
||||
|
||||
<video width="99%" autoPlay muted loop controls="true">
|
||||
<source src="/videos/open-hasura-console.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
## Schemas
|
||||
|
||||
The two schemas `auth` and `storage` are reserved for Nhost Auth and Nhost Storage to work. You're allowed to modify **permissions** and **add relationships**. However, never modify any tables or remove relationships that were added by Nhost inside the `auth` and `storage` schemas.
|
||||
|
||||
Generally, you should use the `public` schema when creating and managing your tables for your app. It's also ok to add custom schemas for more advanced usage.
|
||||
|
||||
## Create Table
|
||||
|
||||
1. In Hasura Console, go to the **Data** tab, select the **public** schema in the left menu and click **Create Table**.
|
||||
2. Enter a table name.
|
||||
3. Add table columns.
|
||||
4. Add a primary key (usually the ID column).
|
||||
5. (Optional) Add foreign keys.
|
||||
6. (Optional) Add unique keys.
|
||||
7. Click **Add Table**.
|
||||
|
||||
When a table is created the table is instantly available through the [GraphQL API](/platform/graphql).
|
||||
|
||||
Here's an example of how to create a `customers` table:
|
||||
|
||||
<Tabs groupId="hasura-console-vs-sql">
|
||||
<TabItem value="hasura-cosnole" label="Hasura Console" default>
|
||||
|
||||
<video width="99%" autoPlay muted loop controls="true">
|
||||
<source src="/videos/hasura-create-table.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sql" label="SQL">
|
||||
|
||||
```sql
|
||||
CREATE TABLE "public"."customers" (
|
||||
"id" serial NOT NULL PRIMARY KEY,
|
||||
"name" text NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Modify Table
|
||||
|
||||
1. In Hasura Console, go to the **Data** tab and click on the table you want to edit in the left menu.
|
||||
2. Click **Modify**.
|
||||
3. Modify or add table columns.
|
||||
|
||||
Here's an example of how to modify a `customers` table by adding an `address` column:
|
||||
|
||||
<Tabs groupId="hasura-console-vs-sql">
|
||||
<TabItem value="hasura-cosnole" label="Hasura Console" default>
|
||||
|
||||
<video width="99%" autoPlay muted loop controls="true">
|
||||
<source src="/videos/hasura-modify-table.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sql" label="SQL">
|
||||
|
||||
```sql
|
||||
ALTER TABLE "public"."customers" ADD COLUMN "address" text;
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Delete Table
|
||||
|
||||
1. In Hasura Console, go to the **Data** tab and select the table you want to delete in the left menu.
|
||||
2. Click **Modify**.
|
||||
3. Scroll to the bottom of the page and click **Delete table** to open the confirmation dialog.
|
||||
4. Type the **name of the table** and click OK.
|
||||
|
||||
Here's an example of how to delete a `customers` table:
|
||||
|
||||
<Tabs groupId="hasura-console-vs-sql">
|
||||
<TabItem value="hasura-cosnole" label="Hasura Console" default>
|
||||
|
||||
<video width="99%" autoPlay muted loop controls="true">
|
||||
<source src="/videos/hasura-delete-table.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sql" label="SQL">
|
||||
|
||||
```sql
|
||||
DROP TABLE "public"."customers";
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Migrations
|
||||
|
||||
To track database changes, use the [Nhost CLI](/platform/cli) to develop locally and use our [GitHub integration](/platform/github-integration) to automatically deploy database migrations live.
|
||||
|
||||
1. Develop locally using the Nhost CLI.
|
||||
2. Push changes to GitHub.
|
||||
3. Nhost automatically deploys changes.
|
||||
|
||||
Learn how to [get started with Nhost CLI](/platform/overview/get-started-with-nhost-cli).
|
||||
|
||||
## Backups
|
||||
|
||||
Databases on [Pro and Enterprise plans](https://nhost.io/pricing) are backed up automatically.
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Use lower-case names for tables. E.g. `customers` instead of `Customers`.
|
||||
- Use plural names for tables. E.g. `customers` instead of `customer`.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Learn PostgreSQL Tutorial - Full Course for Beginners (YouTube)](https://www.youtube.com/watch?v=qw--VYLpxG4).
|
||||
- Learn more about how to manage your [Postgres database in Hasura](https://hasura.io/docs/latest/graphql/core/databases/postgres/schema/index/).
|
||||
- Learn about the [GraphQL API](/platform/graphql).
|
||||
@@ -1,15 +1,25 @@
|
||||
---
|
||||
title: 'Environment variables'
|
||||
title: 'Environment Variables'
|
||||
sidebar_position: 9
|
||||
image: /img/og/platform/environment-variables.png
|
||||
---
|
||||
|
||||
Environment variables are key-value pairs configured outside your source code. They are used to store environment-specific values such as API keys.
|
||||
Environment Variables are key-value pairs configured outside your source code. They are used to store environment-specific values such as API keys.
|
||||
|
||||
---
|
||||
You can manage your app's environment variables in Nhost Console under **Variables**. When you define a new variable, you can set one value for **production** and one for **development**.
|
||||
|
||||
## System environment variables
|
||||

|
||||
|
||||
System environment variables are automatically available in production and local development. The following system environment variables are available:
|
||||
When an environment variable is changed, you must deploy your app again using the [GitHub integration](/platform/github-integration) for the changes to take effect.
|
||||
|
||||
Environment Variables are available in:
|
||||
|
||||
- Hasura
|
||||
- Serverless Functions
|
||||
|
||||
## System Environment Variables
|
||||
|
||||
System environment variables are automatically available in production and during development. The following system environment variables are available:
|
||||
|
||||
- `NHOST_ADMIN_SECRET`
|
||||
- `NHOST_WEBHOOK_SECRET`
|
||||
@@ -26,19 +36,9 @@ jwrsszdp2dhkjxsh4df69pzm3ja6ukedx8ja43zdt6q9kgbgg2w9vh2sedeppukud9a2qzy29v3afdn7
|
||||
NHOST_BACKEND_URL=https://xxxxxxx.nhost.run
|
||||
```
|
||||
|
||||
---
|
||||
## Development Environment Variables
|
||||
|
||||
## Custom environment variables
|
||||
|
||||
You can manage your app's environment variables in Nhost Console under **Variables**. When you define a new variable, you can set a different value for production and local development.
|
||||
|
||||
When an environment variable is changed, you must deploy your app again for the changes to take effect.
|
||||
|
||||
---
|
||||
|
||||
## Local environment variables
|
||||
|
||||
When developing locally, environment variables set in `.env.development` are available in your local environment. There are two ways to manage them:
|
||||
When developing locally using the [CLI](/platform/cli), environment variables set in `.env.development` are available in your local environment. There are two ways to manage them:
|
||||
|
||||
1. Edit the `.env.development` file manually.
|
||||
2. Add development environment variables in the Nhost Console and use `nhost env pull` to sync them. This way, your team members will also have access to the same variables.
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
title: 'GitHub integration'
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
You can connect your Nhost app to a GitHub repository. When you do this, any updates you push to your code will automatically be deployed.
|
||||
|
||||
---
|
||||
|
||||
## Production branch
|
||||
|
||||
Nhost will only deploy your production branch. By default this will match the default branch set on GitHub (usually `main`). You can change this option on Nhost Console.
|
||||
|
||||
Specifically, the following will be deployed:
|
||||
|
||||
- Database migrations
|
||||
- Hasura metadata
|
||||
- Serverless functions
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
Create a new Nhost app. Then use [Nhost CLI](/platform/cli) to initialize your Nhost app locally.
|
||||
|
||||
The workflow is as follows:
|
||||
|
||||
1. Make local changes (migrations, metadata, functions)
|
||||
2. Push changes to GitHub
|
||||
3. Nhost automatically applies changes to production
|
||||
|
||||
**You should always follow this workflow.** Never change things in production directly because that will make the local and production state to be out of sync.
|
||||
|
||||
### Local and production branches out of sync
|
||||
|
||||
If you do changes directly in your production backend, say you add a new table in production, your migrations in your repository will be out of sync. In such a case, we recommend to start over with `nhost init --remote` to get into a consistent state.
|
||||
57
docs/docs/platform/github-integration.mdx
Normal file
57
docs/docs/platform/github-integration.mdx
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: 'GitHub Integration'
|
||||
sidebar_position: 10
|
||||
image: /img/og/platform/github-integration.png
|
||||
---
|
||||
|
||||
The GitHub integration allows you to automatically deploy your Nhost app when on push and merge to a GitHub repository that is connected to your Nhost app.
|
||||
|
||||
When a GitHub repository is connected to a Nhost app, Nhost automatically deploys changes when you push code to the repo.
|
||||
|
||||
The following things are deployed:
|
||||
|
||||
- Database migrations
|
||||
- Hasura metadata
|
||||
- Serverless Functions
|
||||
|
||||
:::info
|
||||
Settings in `nhost/config.yaml` are **not** deployed. That menas you need to manually sync settings between local and remote environments between the CLI and Nhost Cloud.
|
||||
:::
|
||||
|
||||
## Connecting a GitHub repository
|
||||
|
||||
1. From your Nhost app, click **Connect to Github**.
|
||||
|
||||

|
||||
|
||||
2. **Install the Nhost app** on your Github account.
|
||||
|
||||

|
||||
|
||||
3. **Connect** your Github repository.
|
||||
|
||||

|
||||
|
||||
## Deployment Branch
|
||||
|
||||
Nhost only deploys your **deployment branch**. By default, your deployment branch matches the default branch set on GitHub (usually `main`).
|
||||
|
||||
You can change the deployment branch by clicking **Edit** next to the repository in your Nhost app's dashboard.
|
||||
|
||||
You can have multiple Nhost apps connected to the same GitHub repository and use different deployment branches (e.g., `main` and `staging`).
|
||||
|
||||
<center>
|
||||
<img src="/img/platform/github-integration/deployment-branch.png" alt="drawing" width="50%" />
|
||||
</center>
|
||||
|
||||
## Base Directory
|
||||
|
||||
If your Nhost app is not at the root of your git repository, you can set a custom base directory. The base directory is where the `nhost` and `functions` directories are located. In other words, the base directory is the **parent directory** of the `nhost` and `functions` directories.
|
||||
|
||||
<center>
|
||||
<img src="/img/platform/github-integration/base-directory.png" alt="drawing" width="50%" />
|
||||
</center>
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn how to [use the CLI to deploy your Nhost app](/platform/overview/get-started-with-nhost-cli).
|
||||
@@ -1,43 +1,55 @@
|
||||
---
|
||||
title: 'GraphQL'
|
||||
sidebar_position: 1
|
||||
image: /img/og/platform/graphql.png
|
||||
---
|
||||
|
||||
Every Nhost app has its own autogenerated GraphQL API. The GraphQL API is based on the tables and columns in the [Postgres database](/platform/database) and is instantly available. It's [Hasura GraphQL engine](https://github.com/hasura/graphql-engine) that powers the GraphQL API.
|
||||
A GraphQL API is automatically and instantly available based on the tables and columns in your [database](/platform/database).
|
||||
|
||||
The GraphQL API is available at: `https://[subdomain].nhost.run/v1/graphql`.
|
||||
The GraphQL API has instant support for inserting, selecting, updating, and deleting data, which usually account for 80% of all operations you need in your app.
|
||||
|
||||
It's the [Hasura GraphQL engine](https://github.com/hasura/graphql-engine) that powers the GraphQL API which means that all documentation about [queries](https://hasura.io/docs/latest/graphql/core/databases/postgres/queries/index/), [mutations](https://hasura.io/docs/latest/graphql/core/databases/postgres/mutations/index/), and [subscriptions](https://hasura.io/docs/latest/graphql/core/databases/postgres/subscriptions/index/) from Hasura's documentation is applicable.
|
||||
|
||||
## What is GraphQL
|
||||
|
||||
GraphQL is a query language for APIs that prioritize developer experience. The GraphQL API can be used to both fetch (query) and modify (mutation) data. GraphQL is especially powerful for frontend developers who wants to build products fast.
|
||||
GraphQL is a query language for APIs that prioritize developer experience. The GraphQL API can be used to both fetch (query) and modify (mutation) data. GraphQL is especially powerful for frontend developers who want to build products fast.
|
||||
|
||||
### GraphQL clients for JavaScript
|
||||
GraphQL has grown rapidly in popularity in the last years and has been adopted by almost all major tech companies such as Facebook, GitHub, and Stripe.
|
||||
|
||||
To interact with the GraphQL API it's recommended to use a GraphQL client:
|
||||
Building your GraphQL API is a lot of work, but with Nhost it's easy because every table and column is instantly available in your GraphQL API.
|
||||
|
||||
- [Apollo Client](https://www.apollographql.com/docs/react/)
|
||||
## Endpoint
|
||||
|
||||
The GraphQL API is available at `https://[subdomain].nhost.run/v1/graphql` When using the [CLI](/platform/cli) the GraphQL API is available at `http://localhost:1337/v1/graphql`.
|
||||
|
||||
## GraphQL Clients for JavaScript
|
||||
|
||||
The Nhost JavaScript client comes with a simple [GraphQL client](/reference/javascript/nhost-js/graphql) that works well for the backend or simple applications.
|
||||
|
||||
When building more complex frontend applications, we recommend using a more advanced GraphQL client such as:
|
||||
|
||||
- [Apollo Client](https://www.apollographql.com/docs/react/):
|
||||
- [Nhost Apollo Client for React](/reference/react/apollo)
|
||||
- [Nhost Apollo Client for Vue](/reference/vue/apollo)
|
||||
- [URQL](https://formidable.com/open-source/urql/)
|
||||
- [React Query](https://react-query.tanstack.com/graphql)
|
||||
- [SWR](https://swr.vercel.app/docs/data-fetching#graphql)
|
||||
|
||||
It's also possible to use the built-in [GraphQL client](/reference/javascript/nhost-js/graphql) in the Nhost JavaScript client.
|
||||
|
||||
---
|
||||
|
||||
## GraphQL Query
|
||||
## Queries
|
||||
|
||||
A GraphQL query is used to fetch data from the database.
|
||||
|
||||
Here is a GraphQL query that selects `title`, `body`, and `isCompleted` for every row in a `todos` table.
|
||||
:::tip
|
||||
The [Queries documentation from Hasura](https://hasura.io/docs/latest/graphql/core/databases/postgres/queries/index/) is applicable since Nhost uses Hasura's GraphQL Engine for your app.
|
||||
:::
|
||||
|
||||
**Example:**
|
||||
**Example:** A GraphQL query to select `title`, `body`, and `isCompleted` for every row in the `todos` table.
|
||||
|
||||
```graphql
|
||||
```graphql title=GraphQL
|
||||
query GetTodos {
|
||||
todos {
|
||||
title
|
||||
body
|
||||
done
|
||||
isCompleted
|
||||
}
|
||||
}
|
||||
@@ -45,7 +57,7 @@ query GetTodos {
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
```json title=Response
|
||||
{
|
||||
"data": {
|
||||
"todos": [
|
||||
@@ -61,11 +73,11 @@ query GetTodos {
|
||||
|
||||
#### Filtering and sorting
|
||||
|
||||
GraphQL queries More complex queries utilize filters, limits, sorting and aggregation.
|
||||
More complex queries utilize filters, limits, sorting, and aggregation.
|
||||
|
||||
This GraphQL query selects all items in the todo table that aren't done, with the total number of comments and the last five comments:
|
||||
Here's an example of a more complex GraphQL query that selects all items in the `todos` table that aren not completed, with the total number of comments and the last five comments:
|
||||
|
||||
```graphql
|
||||
```graphql title=GraphQL
|
||||
query GetTodosWithLatestComments {
|
||||
todos(where: { isCompleted: { _eq: false } }) {
|
||||
title
|
||||
@@ -84,15 +96,13 @@ query GetTodosWithLatestComments {
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
```json title=Response
|
||||
{
|
||||
"data": {
|
||||
"todos": [
|
||||
{
|
||||
"title": "Delete Firebase account",
|
||||
"body": "Migrate to nhost.io",
|
||||
"body": "Migrate to Nhost",
|
||||
"comments": [
|
||||
{
|
||||
"comment": "Let's do this",
|
||||
@@ -116,137 +126,121 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
Check out Hasura's documentation for full documentation for GraphQL queries.
|
||||
## Mutations
|
||||
|
||||
[Hasura GraphQL queries](https://hasura.io/docs/latest/graphql/core/databases/postgres/queries/index/)
|
||||
A GraphQL mutation is used to insert, upsert, update, or delete data.
|
||||
|
||||
:::tip
|
||||
The [Mutations documentation from Hasura](https://hasura.io/docs/latest/graphql/core/databases/postgres/mutations/index/) is applicable since Nhost uses Hasura's GraphQL Engine for your app.
|
||||
:::
|
||||
|
||||
---
|
||||
### Insert Data
|
||||
|
||||
## GraphQL mutations
|
||||
**Example:** A GraphQL mutation to insert data:
|
||||
|
||||
Mutations are used to insert, update or delete data.
|
||||
|
||||
### Inserting data
|
||||
|
||||
GraphQL mutation to insert data looks like this:
|
||||
|
||||
```graphql
|
||||
```graphql title=GraphQL
|
||||
mutation InsertTodo {
|
||||
insert_todos(
|
||||
objects: [
|
||||
{
|
||||
title: "Delete Firebase account"
|
||||
body: "Migrate to nhost.io"
|
||||
done: false
|
||||
}
|
||||
]
|
||||
objects: [{ title: "Delete Firebase account", body: "Migrate to Nhost", isCompleted: false }]
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
title
|
||||
body
|
||||
done
|
||||
isCompleted
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
```json title=Reponse
|
||||
{
|
||||
"data": {
|
||||
"insert_todos": [
|
||||
{
|
||||
"id": "bf4b01ec-8eb6-451b-afac-81f5058ce852",
|
||||
"title": "Delete Firebase account",
|
||||
"body": "Migrate to nhost.io",
|
||||
"done": true
|
||||
"body": "Migrate to Nhost",
|
||||
"isCompleted": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Inserting multiple rows
|
||||
#### Insert Multiple Rows
|
||||
|
||||
Multiple rows can be inserted with an array as the objects property. This can be useful for migrating data.
|
||||
Use an array of objects to insert multiple rows at the same time.
|
||||
|
||||
```graphql
|
||||
**Example:** Insert multiple Todos at the same time:
|
||||
|
||||
```graphql title=GraphQL
|
||||
mutation InsertMultipleTodos {
|
||||
insert_todos(
|
||||
objects: [
|
||||
{
|
||||
title: "Build the front end"
|
||||
body: "Mobile app or website first?"
|
||||
done: false
|
||||
}
|
||||
{ title: "Launch 🚀", body: "That was easy", done: false }
|
||||
{ title: "Build the front end", body: "Mobile app or website first?", isCompleted: false }
|
||||
{ title: "Launch 🚀", body: "That was easy", isCompleted: false }
|
||||
]
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
title
|
||||
body
|
||||
done
|
||||
isCompleted
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Updating data
|
||||
### Update Data
|
||||
|
||||
You can update existing data with an update mutation. You can update multiple rows at once.
|
||||
|
||||
To mark a todo as done, you would use a mutation like this:
|
||||
**Example:** A GraphQL mutation to mark a atodo item as completed:
|
||||
|
||||
```graphql
|
||||
mutation UpdateTodoStatus($id: uuid, $done: Boolean) {
|
||||
update_todos(_set: { done: $done }, where: { id: { _eq: $id } }) {
|
||||
```graphql title=GraphQL
|
||||
mutation UpdateTodoStatus($id: uuid, $isCompleted: Boolean) {
|
||||
update_todos(_set: { isCompleted: $isCompleted }, where: { id: { _eq: $id } }) {
|
||||
returning {
|
||||
body
|
||||
done
|
||||
isCompleted
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notice how we are using variables as the `id` and `done` variables, which lets us mark any todo as done or not done with the same mutation.
|
||||
Notice how we are using variables as the `id` and `isDone` variables, which lets us mark any todo as completed or not completed with the same mutation.
|
||||
|
||||
### Upsert
|
||||
### Upsert Data
|
||||
|
||||
When you're not sure if a piece of data already exists, use an upsert mutation. It will either insert an object into the database if it doesn't exist, or update the fields of an existing object.
|
||||
When you're not sure if a piece of data already exists, use an upsert mutation. It will either insert an object into the database if it doesn't exist or update the fields of an existing object.
|
||||
|
||||
Unlike for update mutations, you must pass all columns to an upsert mutation.
|
||||
|
||||
In order to convert your insert mutation to an upsert, you need to add an `on_conflict` property. This tells Hasura which fields it should use to find duplicates.
|
||||
To convert your insert mutation to an upsert, you need to add an `on_conflict` property for the GraphQL API to know which fields it should use to find duplicates.
|
||||
|
||||
The `on_conflict` key must be a unique key in your database:
|
||||
|
||||
```graphql
|
||||
```graphql title=GraphQL
|
||||
mutation UpsertTodo {
|
||||
insert_todos(
|
||||
objects: { title: "Delete Firebase account", body: "...", done: false }
|
||||
on_conflict: { constraint: todos_title_key, update_columns: [title, done] }
|
||||
objects: { title: "Delete Firebase account", body: "...", isCompleted: false }
|
||||
on_conflict: { constraint: todos_title_key, update_columns: [title, isCompleted] }
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
title
|
||||
body
|
||||
done
|
||||
isCompleted
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will update the body and done properties of the todo titled `"Delete Firebase account"`.
|
||||
This will update `body` and `done` of the todos with the title "Delete Firebase account".
|
||||
|
||||
### Conditional upsert
|
||||
#### Conditional upsert
|
||||
|
||||
Inserts a new object into a table, or if the primary key already exists, updates columns if the `where` condition is met.
|
||||
|
||||
@@ -272,7 +266,7 @@ mutation UpsertTodo {
|
||||
}
|
||||
```
|
||||
|
||||
### Ignore mutation on conflict
|
||||
#### Ignore mutation on conflict
|
||||
|
||||
If `update_columns` is empty, the mutation will be ignored if the object already exists.
|
||||
|
||||
@@ -296,13 +290,11 @@ mutation InsertTodo {
|
||||
|
||||
In this case, the insert mutation is ignored because a todo with the `title` `"Delete Firebase account"` already exists, and `update_columns` is empty.
|
||||
|
||||
---
|
||||
|
||||
## Deleting data
|
||||
### Delete Data
|
||||
|
||||
To delete your data, use a delete mutation. This mutation will delete all `todos` where `done` is `true`:
|
||||
|
||||
```graphql
|
||||
```graphql title="GraphQL mutation"
|
||||
mutation DeleteDoneTodos {
|
||||
delete_todos(where: { done: { _eq: true } }) {
|
||||
affected_rows
|
||||
@@ -316,9 +308,9 @@ If you have set up foreign keys which will restrict a delete violation, you will
|
||||
|
||||
## Subscriptions
|
||||
|
||||
GraphQL subscriptions are queries that use WebSockets to keep the data up to date in your app in real time:
|
||||
GraphQL subscriptions are queries that use WebSockets to keep the data up to date in your app in real-time. You only have to change `query` to `subscription` when constructing the GraphQL document:
|
||||
|
||||
```graphql
|
||||
```graphql title="GraphQL subscription"
|
||||
subscription GetTodos {
|
||||
todos {
|
||||
title
|
||||
|
||||
@@ -1,35 +1,102 @@
|
||||
---
|
||||
title: 'Permissions'
|
||||
sidebar_position: 1
|
||||
image: /img/og/platform/permissions.png
|
||||
---
|
||||
|
||||
The GraphQL API is protected by a role-based permission system based on access tokens. Permissions are handled on a per-table basis in Hasura Console.
|
||||
The GraphQL API is protected by a role-based permission system.
|
||||
|
||||
---
|
||||
For each **role**, you create **rules** for the **select**, **insert**, **update**, and **delete** operations.
|
||||
|
||||
## How it works
|
||||
**Example:** Let's say you have a `posts` table, and you want users to only access their own posts. This is how you would do it:
|
||||
|
||||
Upon login a user gets an access token which is then being sent with every GraphQL API request to authenticate the user and apply the correct permissions when reading and writing data.
|
||||
```sql title="Posts Table"
|
||||
CREATE TABLE "public"."posts" (
|
||||
"id" serial NOT NULL PRIMARY KEY,
|
||||
"title" text NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
);
|
||||
```
|
||||
|
||||
In Hasura you can specify what a user is allowed to do. A common use case is to allow a user to read something if `user_id` in the database is equal to the user ID in the access token.
|
||||
```json title="Hasura Permission Rule"
|
||||
{
|
||||
"user_id": {
|
||||
"_eq": "X-Hasura-User-Id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Access token data is included as headers with every API request. By default, every user has the following session variables that can be used when creating permission rules:
|
||||
The rule above make it so users can only select posts where the value of `user_id` is equal (`_eq`) to their user ID (`x-hasura-user-id`).
|
||||
|
||||
- `x-hasura-user-id`
|
||||
- `x-hasura-allowed-roles`
|
||||
- `x-hasura-default-role`
|
||||
## What is `x-hasura-user-id`?
|
||||
|
||||
The default role for users is `user`.
|
||||
`x-hasura-user-id` is a permission variable that is used to create permission rules in Hasura. The permission variable comes from the [access token](platform/authentication#access-tokens) that signed-in users have.
|
||||
|
||||
> You can also [add custom permission](#add-permission-variables) varaibles if you need to.
|
||||
The `x-hasura-user-id` permission variable is always available for all signed-in users. You can add [custom permission variables](#custom-permission-variables) to create more complex permission rules unique to your app.
|
||||
|
||||
---
|
||||
## Custom Permission Variables
|
||||
|
||||
## Select permissions
|
||||
You can add custom permission variables in the Nhost console under **Users** and then **Roles and permissions**. These permission variables are then available when creating permissions for your GraphQL API in the Hasura console.
|
||||
|
||||

|
||||
|
||||
**Example:**: Let's say you add a new permission variable `x-hasura-organisation-id` with path `user.profile.organisation.id`. This means that Nhost Auth will get the value for `x-hasura-organisation-id` by internally generating the following GraphQL query:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
user(id: "<User's ID>") {
|
||||
profile {
|
||||
organisation {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Roles
|
||||
|
||||
Every GraphQL request is resolved based on a **single role**. Roles are added in the Hasura Console when selecting a table and clicking **Permisisons**.
|
||||
|
||||
If the user is not signed in, the GraphQL API resolves permissions using the `public` role.
|
||||
|
||||
### Default Role
|
||||
|
||||
Every user have one **default role**. The default role is used to resolve permissions if no other role is specified using the `x-hasura-role` header in the GraphQL request. By default, the default role is `user` for signed-in users.
|
||||
|
||||
### Allowed Roles
|
||||
|
||||
Every user also has a one or more **allowed roles**. Allowed roles are roles that the user is allowed to use to override the default role when making a GraphQL request. To override the default role, add a header `x-hasura-role = <role>` to the GraphQL request.
|
||||
|
||||
## Public Access
|
||||
|
||||
GraphQL requests from unauthenticated users are resolved using the `public` role.
|
||||
|
||||
## Insert permissions
|
||||
|
||||

|
||||
|
||||
Here is a popular approach for insert permission for signed-in users.
|
||||
|
||||
1. At the top of the page, click **"insert"** on the **"user"** role.
|
||||
2. Select **"Without any checks"**.
|
||||
3. Select the columns you want to allow users to insert.
|
||||
|
||||
In our example, we only mark `title`, because the other columns should not be inserted by the user.
|
||||
|
||||
We also want every new record's `user_id` value to be set to the ID of the user making the request. We can tell Hasura to do this using **Column presets**.
|
||||
|
||||
4. Under **Column presets**, set `user_id` to `x-hasura-user-id`.
|
||||
|
||||
Now, users who are signed-in can insert posts. Users can add a title when inserting a post. The post's `id` is automatically generated by the database and the `user_id` is automatically set to the user's id using the `user_id = x-hasura-user-id` column preset.
|
||||
|
||||
## Select, Update and Delete Permissions
|
||||
|
||||
Select, update, and delete permissions usually follows the same pattern. Here's an example of how to add select permissions:
|
||||
|
||||

|
||||
|
||||
One of the most common permission requirements is that logged-in users should only be able to read their own data. This is how it can be achieved with Hasura.
|
||||
One of the most common permission requirements is that signed-in users should only be able to read their own data. This is how to do that:
|
||||
|
||||
1. Go to **Hasura Console**
|
||||
1. Select your table and open the **Permissions** tab
|
||||
@@ -46,40 +113,14 @@ To further refine this rule, do the following:
|
||||
|
||||
Note that if you add columns to your table table later, you must check new columns here to let users read them.
|
||||
|
||||
---
|
||||
## Next Steps
|
||||
|
||||
## Insert permissions
|
||||
Hasura has more in-depth documentation related to permissions that you can learn from:
|
||||
|
||||

|
||||
|
||||
Here is a popular approach for insert permission for logged in users.
|
||||
|
||||
1. At the top of the page, click **"insert"** on the **"user"** role.
|
||||
1. Select **"Without any checks"**.
|
||||
1. Select the columns you want to allow users to insert.
|
||||
|
||||
In our example, we only select `name`, because we want all other other columns to be filled with default values.
|
||||
|
||||
We also want every new record's `user_id` value to be set to the ID of the user making the request. We can tell Hasura to do this with **column presets**.
|
||||
|
||||
1. Under column presets, set `user_id` to `x-hasura-user-id`.
|
||||
|
||||
## Add Permission Variables
|
||||
|
||||
You can add extra permission variables in the Nhost console under **Users** and then **Roles and permissions**. These permission variables are then available when creating permissions for your GraphQL API in the Hasura console.
|
||||
|
||||

|
||||
|
||||
As an example, let's say you add a new permission variable `x-hasura-organisation-id` with path `user.profile.organisation.id`. This means that Nhost Auth will get the value for `x-hasura-organisation-id` by internally generating the following GraphQL query:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
user(id: "<user-id>") {
|
||||
profile {
|
||||
organisation {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- [Authorization / Access control](https://hasura.io/docs/latest/graphql/core/auth/authorization/index/)
|
||||
- [Access control basics](https://hasura.io/docs/latest/graphql/core/auth/authorization/basics/)
|
||||
- [Roles & Session variables](https://hasura.io/docs/latest/graphql/core/auth/authorization/roles-variables/)
|
||||
- [Inherited roles](https://hasura.io/docs/latest/graphql/core/auth/authorization/inherited-roles/)
|
||||
- [Configuring permission rules](https://hasura.io/docs/latest/graphql/core/auth/authorization/permission-rules/)
|
||||
- [Access control examples](https://hasura.io/docs/latest/graphql/core/auth/authorization/common-roles-auth-examples/)
|
||||
- [Multiple column + row permissions for the same role](https://hasura.io/docs/latest/graphql/core/auth/authorization/role-multiple-rules/)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"label": "Overview",
|
||||
"position": 2,
|
||||
"collapsed": false
|
||||
"collapsed": true
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: 'Architecture'
|
||||
sidebar_position: 2
|
||||
image: /img/og/platform/architecture.png
|
||||
---
|
||||
|
||||
Nhost is a backend as a service built with open source tools to provide developers the general building blocks required to build fantastic digital apps and products.
|
||||
@@ -23,7 +24,6 @@ Data is stored in Postgres and files are stored in S3.
|
||||
The open source tools used for the full Nhost stack are:
|
||||
|
||||
- Database: [Postgres](https://www.postgresql.org/)
|
||||
- S3: [Minio](https://github.com/minio/minio)
|
||||
- GraphQL: [Hasura](https://github.com/hasura/graphql-engine)
|
||||
- Authentication: [Hasura Auth](https://github.com/nhost/hasura-auth)
|
||||
- Storage: [Hasura Storage](https://github.com/nhost/hasura-storage)
|
||||
|
||||
426
docs/docs/platform/overview/get-started-with-nhost-cli.md
Normal file
426
docs/docs/platform/overview/get-started-with-nhost-cli.md
Normal file
@@ -0,0 +1,426 @@
|
||||
---
|
||||
title: 'Get Started with Nhost CLI'
|
||||
sidebar_position: 2
|
||||
image: /img/og/platform/get-started-with-nhost-cli.png
|
||||
---
|
||||
|
||||
# Get started with Nhost CLI
|
||||
|
||||
Nhost's command-line interface (CLI) lets you run a complete Nhost development
|
||||
environment locally with the following services: PostgreSQL database, Hasura,
|
||||
Authentication, Storage (MinIO), Serverless Functions, and Emails (Mailhog).
|
||||
|
||||
## Installation
|
||||
|
||||
### Install the binary globally
|
||||
|
||||
To install **Nhost CLI**, run this command from any directory in your terminal:
|
||||
|
||||
```bash
|
||||
sudo curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | bash
|
||||
```
|
||||
|
||||
On **MacOS and Linux**, this will install the **Nhost CLI** in `/usr/local/bin`.
|
||||
|
||||
If you'd prefer to install to a different location other than `/usr/local/bin`,
|
||||
set the `INSTALL_PATH` variable accordingly:
|
||||
|
||||
```bash
|
||||
sudo curl -L https://raw.githubusercontent.com/nhost/cli/main/get.sh | INSTALL_PATH=$HOME/bin bash
|
||||
```
|
||||
|
||||
On **Windows**, this will download and extract the binary `nhost.exe` available
|
||||
under `Assets` of the latest release from the GitHub release page:
|
||||
https://github.com/nhost/cli/releases.
|
||||
|
||||
You can move the executable to a different location and add the path to the
|
||||
environment variable `PATH` to make `nhost` accessible globally.
|
||||
|
||||
Finally, you can check that everything has been successfully installed by
|
||||
typing:
|
||||
|
||||
```bash
|
||||
nhost version
|
||||
```
|
||||
|
||||

|
||||
|
||||
### (Optional) Add shell completion
|
||||
|
||||
To add command auto-completion in the shell, you can run the following command:
|
||||
|
||||
```bash
|
||||
nhost completion [shell]
|
||||
```
|
||||
|
||||
This will generate the autocompletion script for `nhost` for the specified shell
|
||||
(bash, fish, PowerShell, or zsh).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Dependencies
|
||||
|
||||
Before using the **Nhost CLI**, make sure you have the following dependencies
|
||||
installed on your local machine:
|
||||
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
- [Docker](https://www.docker.com/get-started)
|
||||
|
||||
:::caution
|
||||
Docker must be running while using Nhost CLI.
|
||||
:::
|
||||
|
||||
### Nhost CLI login
|
||||
|
||||
After installing **Nhost CLI**, you can log in to your Nhost account by running
|
||||
the following command:
|
||||
|
||||
```bash
|
||||
nhost login
|
||||
```
|
||||
|
||||
This will display a prompt for you to enter your Nhost account credentials
|
||||
(email/password).
|
||||
|
||||
:::info
|
||||
You can create a Nhost account here: [https://app.nhost.io](https://app.nhost.io/).
|
||||
:::
|
||||
|
||||

|
||||
|
||||
After successfully logging in, you are authorized to manage your Nhost projects
|
||||
using the Nhost CLI.
|
||||
|
||||
You can also log out at any time by running:
|
||||
|
||||
```bash
|
||||
nhost logout
|
||||
```
|
||||
|
||||
## Set up your project
|
||||
|
||||
### 1. Create a new Nhost app
|
||||
|
||||
import CreateApp from '@site/src/components/create-nhost-app.mdx';
|
||||
|
||||
<CreateApp />
|
||||
|
||||
### 2. Create a new GitHub Repository
|
||||
|
||||
A typical workflow would also include creating a Github repository for your
|
||||
Nhost project. It will facilitate your development workflow since Nhost can
|
||||
integrate with Github to enable continuous deployment.
|
||||
|
||||
So, go to your Github account and
|
||||
[create a new repository](https://github.com/new). You can make your repository
|
||||
either public or private.
|
||||
|
||||

|
||||
|
||||
### 3. Connect Nhost project to Github
|
||||
|
||||
Finally, connect your Github repository to your Nhost project. Doing so will
|
||||
enable Nhost to deploy new versions of your project when you push automatically
|
||||
commits to your connected Git repository.
|
||||
|
||||
1. From your project workspace, click **Connect to Github**.
|
||||
|
||||

|
||||
|
||||
2. **Install the Nhost app** on your Github account.
|
||||
|
||||

|
||||
|
||||
3. **Connect** your Github repository.
|
||||
|
||||

|
||||
|
||||
## Develop locally
|
||||
|
||||
## 1. Initialize your Nhost app
|
||||
|
||||
**Nhost CLI** brings the functionality of your Nhost production environment
|
||||
directly to your local machine.
|
||||
|
||||
It provides Docker containers to run the backend services that match your
|
||||
production application in a local environment. That way, you can make changes
|
||||
and test your code locally before deploying those changes to production.
|
||||
|
||||
You can either initialize a blank Nhost app locally to start from scratch by
|
||||
running the following command:
|
||||
|
||||
```bash
|
||||
nhost init -n my-nhost-app
|
||||
```
|
||||
|
||||
And then link it to a remote app from your Nhost workspace in `app.nhost.io` by
|
||||
running the `link` command and selecting the corresponding app from the prompt:
|
||||
|
||||
```bash
|
||||
nhost link
|
||||
```
|
||||
|
||||
Or you can directly initialize a local Nhost app from one of your existing
|
||||
production apps by specifying the `--remote` flag:
|
||||
|
||||
```bash
|
||||
nhost init --remote -n my-nhost-app
|
||||
```
|
||||
|
||||
It will also prompt you to choose the remote app you'd like to use to initialize
|
||||
your local Nhost development environment.
|
||||
|
||||

|
||||
|
||||
The `init` command creates the Nhost app inside your current working directory
|
||||
within a `nhost/` folder.
|
||||
|
||||
```
|
||||
my-nhost-app/
|
||||
└─ nhost/
|
||||
├─ config.yaml
|
||||
├─ emails/
|
||||
├─ metadata/
|
||||
├─ migrations/
|
||||
└─ seeds/
|
||||
```
|
||||
|
||||
Finally, make sure to link your current working directory to your GitHub
|
||||
repository:
|
||||
|
||||
```bash
|
||||
echo "# my-nhost-app" >> README.md
|
||||
git init
|
||||
git add README.md
|
||||
git commit -m "first commit"
|
||||
git branch -M main
|
||||
git remote add origin https://github.com/[github-username]/my-nhost-app.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
## 2. Start a local development environment
|
||||
|
||||
To start a local development environment for your Nhost app, run the following
|
||||
command:
|
||||
|
||||
```bash
|
||||
nhost dev
|
||||
```
|
||||
|
||||
:::caution
|
||||
Make sure [Docker](https://www.docker.com/get-started) is up and running. It’s required for Nhost to work.
|
||||
:::
|
||||
|
||||
Running this command will start up all the backend services provided by Nhost.
|
||||
|
||||
It also runs a webserver to serve the Hasura console for the GraphQL engine so
|
||||
you can manage the database and try out the API.
|
||||
|
||||
The Hasura console should open automatically at
|
||||
[http://localhost:1337](http://localhost:1337/).
|
||||
|
||||

|
||||
|
||||
## 3. Make changes
|
||||
|
||||
There are three things the Nhost CLI and the GitHub integration track and apply
|
||||
to production:
|
||||
|
||||
- Database migrations
|
||||
- Hasura Metadata
|
||||
- Serverless Functions
|
||||
|
||||
:::caution
|
||||
Settings in `nhost/config.yaml` are not being applied to production. They only work locally for now.
|
||||
:::
|
||||
|
||||
### Database migrations
|
||||
|
||||
Database changes are tracked and managed through migrations.
|
||||
|
||||
:::tip
|
||||
It's important that you use the Hasura console to make database changes. Indeed, with the Hasura console, DB migration files are generated incrementally to track changes automatically for you.
|
||||
:::
|
||||
|
||||
To demonstrate how to make database changes, let's create a new table called
|
||||
`messages`, with the following columns:
|
||||
|
||||
- `id` (type UUID and default `gen_random_uuid()`),
|
||||
- `text` (type Text),
|
||||
- `authorId` (type UUID),
|
||||
- `createdAt` (type Timestamp and default `now()`)
|
||||
|
||||
In the Hasura console, head over to the **data** tab section and click on the
|
||||
PostgreSQL database (from the left side navigation) that Nhost provides us.
|
||||
|
||||
Click on the **public** schema and the **Create Table** button.
|
||||
|
||||

|
||||
|
||||
Then, enter the values for creating the `messages` table as mentioned above.
|
||||
Also, specify the `id` column as the primary key of the table, and link the
|
||||
`authorId` column to the `users.id` column using a foreign key to link the
|
||||
`users` and `messages` tables together.
|
||||
|
||||

|
||||
|
||||
Next, click on the **Add Table** button to create the table.
|
||||
|
||||
Finally, check out the `migrations/` folder in your project directory. A
|
||||
migration file has been created automatically to reflect our database changes
|
||||
and track the new table creation.
|
||||
|
||||
The migration was created under `nhost/migrations/default`:
|
||||
|
||||
```bash
|
||||
$ ls -la nhost/migrations/default
|
||||
total 0
|
||||
drwxr-xr-x 3 user staff 96 Apr 27 17:06 .
|
||||
drwxr-xr-x 3 user staff 96 Apr 27 17:06 ..
|
||||
drwxr-xr-x 4 user staff 128 Apr 27 17:06 1651071963431_create_table_public_messages
|
||||
```
|
||||
|
||||
However, note that this database migration has only been applied locally. In
|
||||
other words, the `messages` table does not (yet) exists in production.
|
||||
|
||||
To apply the local changes to production, check out the
|
||||
[Deploy your project](#deploy-your-project) section below.
|
||||
|
||||
### Hasura metadata
|
||||
|
||||
In addition to database schema changes, Nhost also tracks Hasura metadata.
|
||||
|
||||
The Hasura metadata track all the actions performed on the console, like
|
||||
tracking tables/views/functions, creating relationships, configuring
|
||||
permissions, creating event triggers, and remote schemas.
|
||||
|
||||
To demonstrate it, let's add a new permission to our `messages` table for the
|
||||
`user` role on the `insert` operation. That permission will allow users to
|
||||
create new messages.
|
||||
|
||||
So, open the permissions tab for the `messages` table, type in `user` in the
|
||||
role cell, and click the edit icon on the `insert` operation:
|
||||
|
||||

|
||||
|
||||
To restrict the users to create new messages only for themselves, specify an
|
||||
`_eq` condition between the `authorId` and the `X-Hasura-User-ID` session
|
||||
variable, which is passed with each request.
|
||||
|
||||

|
||||
|
||||
Then, select the columns the users can define through the GraphQL API, set the
|
||||
value for the `authorId` column to be equal to the `X-Hasura-User-ID` session
|
||||
variable, and click **Save Permissions**.
|
||||
|
||||

|
||||
|
||||
Finally, check out the `metadata/` folder in your project directory to confirm
|
||||
that the permission changes we did were tracked locally in your git repository.
|
||||
|
||||
In our case, those changes should be tracked in
|
||||
`nhost/metadata/databases/default/tables/public_messages.yaml`:
|
||||
|
||||
```yaml title="nhost/metadata/databases/default/tables/public_messages.yaml"
|
||||
table:
|
||||
name: messages
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- permission:
|
||||
backend_only: false
|
||||
check:
|
||||
authorId:
|
||||
_eq: X-Hasura-User-Id
|
||||
columns:
|
||||
- text
|
||||
set:
|
||||
authorId: x-hasura-User-Id
|
||||
role: user
|
||||
```
|
||||
|
||||
### Serverless functions
|
||||
|
||||
Now let's create a serverless function before we push all changes to GitHub so
|
||||
Nhost can deploy them to production.
|
||||
|
||||
For this guide, let's create a simple serverless function that will return the
|
||||
current date-time when called.
|
||||
|
||||
First, make sure to install `express`, which is required for serverless
|
||||
functions to work.
|
||||
|
||||
```bash
|
||||
npm install express
|
||||
npm install -d @types/node @types/express
|
||||
```
|
||||
|
||||
Then, create a new file named `time.ts` inside the `functions/` folder of your
|
||||
working directory, and paste the following code:
|
||||
|
||||
```ts title="functions/time.ts"
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
export default (req: Request, res: Response) => {
|
||||
return res.status(200).send(`Hello ${req.query.name}! It's now: ${new Date().toUTCString()}`)
|
||||
}
|
||||
```
|
||||
|
||||
Every JavaScript and TypeScript file inside the `functions/` folder becomes an
|
||||
API endpoint.
|
||||
|
||||
Locally, the base URL for the serverless functions is
|
||||
`http://localhost:1337/v1/functions`. Then, the endpoint for each function is
|
||||
determined by its filename or the name of its dedicated parent directory.
|
||||
|
||||
For example, the endpoint for our function is
|
||||
`http://localhost:1337/v1/functions/time`.
|
||||
|
||||
```bash
|
||||
curl http://localhost:1337/v1/functions/time\?name\=Greg
|
||||
Hello Greg! It's now: Wed, 27 Apr 2022 18:52:12 GMT
|
||||
```
|
||||
|
||||
## Deploy your project
|
||||
|
||||
To deploy your local changes to production, you can commit and push them to
|
||||
GitHub. As a result, Nhost will automatically pick up the changes in your
|
||||
repository and apply them to your associated remote Nhost project.
|
||||
|
||||
:::caution
|
||||
Make sure to [connect your Github repository](#3-connect-nhost-project-to-Github) to your Nhost project first.
|
||||
:::
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "commit message"
|
||||
git push
|
||||
```
|
||||
|
||||
To check out your deployment, head over to the **Deployments** tab in your
|
||||
[Nhost dashboard](https://app.nhost.io).
|
||||
|
||||

|
||||
|
||||
## Get help
|
||||
|
||||
To get usage tips and learn more about available commands from within Nhost CLI,
|
||||
run the following:
|
||||
|
||||
```shell
|
||||
nhost help
|
||||
```
|
||||
|
||||
For more information about a specific command, run the command with the `--help`
|
||||
flag:
|
||||
|
||||
```
|
||||
nhost init --help
|
||||
```
|
||||
|
||||
If you have additional questions or ideas for new features, you can
|
||||
[start an issue](https://github.com/nhost/cli/issues) or
|
||||
[a new discussion](https://github.com/nhost/cli/discussions/new) on Nhost CLI’s
|
||||
open-source repository. You can also
|
||||
[chat with our team](https://discord.com/invite/9V7Qb2U) on Discord.
|
||||
|
||||
We’d love to hear from you!
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
title: 'Quickstart: Next.js'
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
This is a quickstart guide for React with Nhost.
|
||||
700
docs/docs/platform/quickstarts/nextjs.mdx
Normal file
700
docs/docs/platform/quickstarts/nextjs.mdx
Normal file
@@ -0,0 +1,700 @@
|
||||
---
|
||||
title: 'Quickstart: Next.js'
|
||||
sidebar_position: 2
|
||||
image: /img/og/platform/quickstart-nextjs.png
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
# Quickstart: Next.js
|
||||
|
||||
## Introduction
|
||||
|
||||
This quickstart guide provides the steps you need to build a simple Next.js app
|
||||
powered by Nhost for the backend. It includes:
|
||||
|
||||
- Database: [PostgreSQL](https://www.postgresql.org/)
|
||||
- Instant GraphQL API: [Hasura](https://hasura.io/)
|
||||
- Authentication: [Hasura Auth](https://github.com/nhost/hasura-auth/)
|
||||
- Storage: [Hasura Storage](https://hub.docker.com/r/nhost/hasura-storage)
|
||||
|
||||
By the end of this guide, you'll have a full-stack app that allows users to log
|
||||
in to access a protected dashboard and update their profile information.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before getting started, let's make sure that your development environment is
|
||||
ready.
|
||||
|
||||
You'll need **Node.js** version 12 or later: [install it from here](https://nodejs.org/en/).
|
||||
|
||||
## Project setup
|
||||
|
||||
### Create a new Nhost app
|
||||
|
||||
import CreateApp from '@site/src/components/create-nhost-app.mdx'
|
||||
|
||||
<CreateApp />
|
||||
|
||||
:::info
|
||||
You can also connect your Nhost app to a GitHub repository. When you do this, any updates you push to your code will automatically be deployed. [Learn more](https://docs.nhost.io/platform/github-integration).
|
||||
:::
|
||||
|
||||
## Initialize the app
|
||||
|
||||
### Create a Next.js app
|
||||
|
||||
The simplest way to create a new Next.js application is by using the tool called
|
||||
`create-next-app`, which bootstraps a Next.js app for you without the hassle of
|
||||
configuring everything yourself.
|
||||
|
||||
So, open your terminal, and run the following command:
|
||||
|
||||
```bash
|
||||
npx create-next-app my-nhost-app --example "https://github.com/nhost/quickstart-nextjs"
|
||||
```
|
||||
|
||||
:::info
|
||||
This command uses an [existing template](https://github.com/nhost/quickstart-nextjs), through the `--example` flag, which already contains the React components and pages we'll use for this guide.
|
||||
:::
|
||||
|
||||
You can now `cd` into your project directory:
|
||||
|
||||
```bash
|
||||
cd my-nhost-app
|
||||
```
|
||||
|
||||
And run the development server with the following command:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
If everything is working fine, your Next.js development server should be running
|
||||
on port 3000. Open [http://localhost:3000](http://localhost:3000) from your
|
||||
browser to check this out.
|
||||
|
||||
### Configure Nhost with Next.js
|
||||
|
||||
To work with Nhost from within our Next.js app, we'll use the
|
||||
[Next.js SDK](https://github.com/nhost/nhost/tree/main/packages/nextjs) provided
|
||||
by Nhost. It's a wrapper around the
|
||||
[Nhost React SDK](https://github.com/nhost/nhost/tree/main/packages/react) which
|
||||
gives us a way to interact with our Nhost backend using React hooks.
|
||||
|
||||
You can install the Nhost Next.js SDK with:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm install @nhost/react @nhost/nextjs graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn add @nhost/react @nhost/nextjs graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Next, open your `_app.js` file as we'll now configure Nhost inside our app.
|
||||
|
||||
The Nhost Next.js SDK comes with a React provider named `NhostNextProvider` that
|
||||
makes the authentication state and all the provided React hooks available in our
|
||||
application.
|
||||
|
||||
Use the following code to instantiate a new Nhost client and link it to your
|
||||
Nhost backend:
|
||||
|
||||
```jsx title="pages/_app.js"
|
||||
import { UserProvider } from '../UserProvider';
|
||||
// highlight-start
|
||||
import { NhostNextProvider, NhostClient } from '@nhost/nextjs';
|
||||
// highlight-end
|
||||
|
||||
// highlight-start
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: process.env.NEXT_PUBLIC_NHOST_BACKEND_URL || '',
|
||||
});
|
||||
// highlight-end
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
{/* highlight-next-line */}
|
||||
<NhostNextProvider nhost={nhost} initial={pageProps.nhostSession}>
|
||||
<UserProvider>
|
||||
{/* ... */}
|
||||
</UserProvider>
|
||||
{/* highlight-next-line */}
|
||||
</NhostNextProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Finally, make sure to create an environment variable named
|
||||
`NEXT_PUBLIC_NHOST_BACKEND_URL` to store your Nhost backend URL:
|
||||
|
||||
```yaml title=".env.development"
|
||||
NEXT_PUBLIC_NHOST_BACKEND_URL=YOUR_NHOST_BACKEND_URL
|
||||
```
|
||||
|
||||
You can find your Nhost backend URL for your project from [your dashboard](https://app.nhost.io) as shown below:
|
||||
|
||||

|
||||
|
||||
:::caution
|
||||
Don't forget to restart your Next.js server after saving your `.env.development`
|
||||
file to load your new environment variable.
|
||||
:::
|
||||
|
||||
## Build the app
|
||||
|
||||
### Add authentication
|
||||
|
||||
#### 1. Sign-up
|
||||
|
||||
The next step is to allow our users to authenticate into our application.
|
||||
Let's start with implementing the sign-up process.
|
||||
|
||||
For that, we'll use the `useSignUpEmailPassword` hook provided by the Nhost
|
||||
Next.js SDK within our `SignUp` component.
|
||||
|
||||
So, open up the corresponding file from your project, and use the following
|
||||
code:
|
||||
|
||||
```jsx title="components/SignUp.js"
|
||||
import styles from '../styles/components/SignUp.module.css'
|
||||
import { useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSignUpEmailPassword } from '@nhost/nextjs'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import Input from './Input'
|
||||
import Spinner from './Spinner'
|
||||
|
||||
const SignUp = () => {
|
||||
const [firstName, setFirstName] = useState('')
|
||||
const [lastName, setLastName] = useState('')
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const { signUpEmailPassword, isLoading, isSuccess, needsEmailVerification, isError, error } =
|
||||
useSignUpEmailPassword()
|
||||
|
||||
const handleOnSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
await signUpEmailPassword(email, password, {
|
||||
displayName: `${firstName} ${lastName}`.trim(),
|
||||
metadata: {
|
||||
firstName,
|
||||
lastName
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (isSuccess) {
|
||||
router.push('/')
|
||||
return null
|
||||
}
|
||||
|
||||
const disableForm = isLoading || needsEmailVerification
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.card}>
|
||||
<div className={styles['logo-wrapper']}>
|
||||
<Image src="/logo.svg" alt="logo" layout="fill" objectFit="contain" />
|
||||
</div>
|
||||
|
||||
{needsEmailVerification ? (
|
||||
<p className={styles['verification-text']}>
|
||||
Please check your mailbox and follow the verification link to verify your email.
|
||||
</p>
|
||||
) : (
|
||||
<form onSubmit={handleOnSubmit} className={styles.form}>
|
||||
<div className={styles['input-group']}>
|
||||
<Input
|
||||
label="First name"
|
||||
value={firstName}
|
||||
onChange={(e) => setFirstName(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
label="Last name"
|
||||
value={lastName}
|
||||
onChange={(e) => setLastName(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
type="email"
|
||||
label="Email address"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
label="Create password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
|
||||
<button type="submit" disabled={disableForm} className={styles.button}>
|
||||
{isLoading ? <Spinner size="sm" /> : 'Create account'}
|
||||
</button>
|
||||
|
||||
{isError ? <p className={styles['error-text']}>{error?.message}</p> : null}
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className={styles.text}>
|
||||
Already have an account?{' '}
|
||||
<Link href="/sign-in">
|
||||
<a className={styles.link}>Sign in</a>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignUp
|
||||
```
|
||||
|
||||
By default, the user must verify his email address before fully signing up. You can change this setting from your Nhost dashboard.
|
||||
|
||||
#### 2. Sign-in
|
||||
|
||||
Now that new users can sign up for our application, let's see how to allow
|
||||
existing users to sign in with email and password.
|
||||
|
||||
For that, we will use the Nhost hook named `useSignInEmailPassword` inside our
|
||||
`SignIn` component the same way we did with our `SignUp` component. So, here's
|
||||
what your component should look like after applying the changes for the sign-in
|
||||
logic:
|
||||
|
||||
```jsx title="components/SignIn.js"
|
||||
import styles from '../styles/components/SignIn.module.css'
|
||||
import { useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSignInEmailPassword } from '@nhost/nextjs'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import Input from './Input'
|
||||
import Spinner from './Spinner'
|
||||
|
||||
const SignIn = () => {
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const { signInEmailPassword, isLoading, isSuccess, needsEmailVerification, isError, error } =
|
||||
useSignInEmailPassword()
|
||||
|
||||
const handleOnSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
await signInEmailPassword(email, password)
|
||||
}
|
||||
|
||||
if (isSuccess) {
|
||||
router.push('/')
|
||||
return null
|
||||
}
|
||||
|
||||
const disableForm = isLoading || needsEmailVerification
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.card}>
|
||||
<div className={styles['logo-wrapper']}>
|
||||
<Image src="/logo.svg" alt="logo" layout="fill" objectFit="contain" />
|
||||
</div>
|
||||
|
||||
{needsEmailVerification ? (
|
||||
<p className={styles['verification-text']}>
|
||||
Please check your mailbox and follow the verification link to verify your email.
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<form onSubmit={handleOnSubmit} className={styles.form}>
|
||||
<Input
|
||||
type="email"
|
||||
label="Email address"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
label="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
|
||||
<button type="submit" disabled={disableForm} className={styles.button}>
|
||||
{isLoading ? <Spinner size="sm" /> : 'Sign in'}
|
||||
</button>
|
||||
|
||||
{isError ? <p className={styles['error-text']}>{error?.message}</p> : null}
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className={styles.text}>
|
||||
No account yet?{' '}
|
||||
<Link href="/sign-up">
|
||||
<a className={styles.link}>Sign up</a>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignIn
|
||||
```
|
||||
|
||||
#### 3. Sign-out
|
||||
|
||||
Finally, to allow the users to sign out from the app, we can use the Nhost
|
||||
`useSignOut` hook:
|
||||
|
||||
```jsx title="components/Layout.js"
|
||||
import { useSignOut } from '@nhost/nextjs'
|
||||
|
||||
const Layout = ({ children = null }) => {
|
||||
const { signOut } = useSignOut()
|
||||
|
||||
const menuItems = [
|
||||
//..
|
||||
{
|
||||
label: 'Logout',
|
||||
onClick: signOut,
|
||||
icon: LogoutIcon
|
||||
}
|
||||
]
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
### Protect routes
|
||||
|
||||
Now that we have implemented authentication, we can easily decide who can access
|
||||
certain parts of our application.
|
||||
|
||||
In our case, we'll only allow authenticated users to have access to the `/` and
|
||||
`/profile` routes. All the other users should be redirected to the `/sign-in`
|
||||
page if they try to access those routes.
|
||||
|
||||
To do so, we can check the authentication status of the current user using the
|
||||
Nhost SDK by creating a
|
||||
[high-order component](https://reactjs.org/docs/higher-order-components.html):
|
||||
|
||||
```jsx title="withAuth.js"
|
||||
import styles from './styles/pages/ProtectedRoute.module.css'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useAuthenticationStatus } from '@nhost/nextjs'
|
||||
import Spinner from './components/Spinner'
|
||||
|
||||
export default function withAuth(Component) {
|
||||
return function AuthProtected(props) {
|
||||
const router = useRouter()
|
||||
const { isLoading, isAuthenticated } = useAuthenticationStatus()
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Spinner />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
router.push('/sign-in')
|
||||
return null
|
||||
}
|
||||
|
||||
return <Component {...props} />
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then, wrap our Next.js pages, `index.js` and `profile.js`, with it:
|
||||
|
||||
<Tabs
|
||||
defaultValue="index"
|
||||
values={[
|
||||
{label: 'pages/index.js', value: 'index'},
|
||||
{label: 'pages/profile.js', value: 'profile'},
|
||||
]}>
|
||||
<TabItem value="index">
|
||||
|
||||
```js
|
||||
import withAuth from '../withAuth'
|
||||
|
||||
const Home = () => {
|
||||
//...
|
||||
}
|
||||
|
||||
export default withAuth(Home)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="profile">
|
||||
|
||||
```js
|
||||
import withAuth from '../withAuth'
|
||||
|
||||
const Profile = () => {
|
||||
//...
|
||||
}
|
||||
|
||||
export default withAuth(Profile)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Retrieve user data
|
||||
|
||||
Finally, let's display the information of the authenticated user throughout his
|
||||
dashboard to make the app more personalized.
|
||||
|
||||
Getting the current authenticated user data is quite easy. We
|
||||
can use the `useUserData` hook provided by Nhost to do it.
|
||||
|
||||
So, open the `UserProvider.js` file and use this hook like so:
|
||||
|
||||
```js
|
||||
import React, { useContext } from 'react'
|
||||
// highlight-next-line
|
||||
import { useUserData } from '@nhost/nextjs'
|
||||
|
||||
const UserContext = React.createContext(null)
|
||||
|
||||
export function UserProvider({ children = null }) {
|
||||
// highlight-next-line
|
||||
const user = useUserData()
|
||||
return <UserContext.Provider value={{ user }}>{children}</UserContext.Provider>
|
||||
}
|
||||
|
||||
export function useUserContext() {
|
||||
return useContext(UserContext)
|
||||
}
|
||||
```
|
||||
|
||||
That's it! The JSX code for rendering the user data (email, display name, etc.)
|
||||
is already included in your components as part of the example repository you've
|
||||
bootstrapped at the beginning of this guide.
|
||||
|
||||
### Update user data
|
||||
|
||||
Nhost provides a GraphQL API through Hasura so that we can query and mutate our
|
||||
data instantly.
|
||||
|
||||
In this tutorial, we'll use the
|
||||
[Apollo GraphQL client](https://www.apollographql.com/) for interacting with
|
||||
this GraphQL API.
|
||||
|
||||
So, start by installing the following dependencies:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm install @nhost/react-apollo @apollo/client
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn add @nhost/react-apollo @apollo/client
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Then, add the `NhostApolloProvider` from `@nhost/react-apollo` into your
|
||||
`_app_.js` file.
|
||||
|
||||
```jsx title="pages/_app.js"
|
||||
import { NhostApolloProvider } from '@nhost/react-apollo'
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
<NhostNextProvider nhost={nhost} initial={pageProps.nhostSession}>
|
||||
<NhostApolloProvider nhost={nhost}>{/* ... */}</NhostApolloProvider>
|
||||
</NhostNextProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
From there, we can construct our GraphQL query and use the Apollo `useMutation`
|
||||
hook to execute that query when the user submits the form from the profile page:
|
||||
|
||||
```js title="pages/profile.js"
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
const UPDATE_USER_MUTATION = gql`
|
||||
mutation ($id: uuid!, $displayName: String!, $metadata: jsonb) {
|
||||
updateUser(pk_columns: { id: $id }, _set: { displayName: $displayName, metadata: $metadata }) {
|
||||
id
|
||||
displayName
|
||||
metadata
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Profile = () => {
|
||||
const [mutateUser, { loading: updatingProfile }] = useMutation(UPDATE_USER_MUTATION)
|
||||
|
||||
const updateUserProfile = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
try {
|
||||
await mutateUser({
|
||||
variables: {
|
||||
id: user.id,
|
||||
displayName: `${firstName} ${lastName}`.trim(),
|
||||
metadata: {
|
||||
firstName,
|
||||
lastName
|
||||
}
|
||||
}
|
||||
})
|
||||
toast.success('Updated successfully', { id: 'updateProfile' })
|
||||
} catch (error) {
|
||||
toast.error('Unable to update profile', { id: 'updateProfile' })
|
||||
}
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, since Hasura has an **allow nothing by default** policy, and we haven't
|
||||
set any permissions yet, our GraphQL mutations would fail.
|
||||
|
||||
So, open the Hasura console from the **Data** tab of your project from [your Nhost dashboard](https://app.nhost.io/). Then, go to the **permissions** tab of the `users` table, type in `user` in the role
|
||||
cell, and click the edit icon on the `select` operation:
|
||||
|
||||

|
||||
|
||||
To restrict the user to read his data only, specify a condition with the
|
||||
user's ID and the `X-Hasura-User-ID` session variable, which is passed with each
|
||||
requests.
|
||||
|
||||

|
||||
|
||||
Next, select the columns you'd like the users to have access to, and click
|
||||
**Save Permissions**.
|
||||
|
||||

|
||||
|
||||
Repeat the same steps on the `update` operation for the `user` role to allow
|
||||
users to update their `displayName` and `metadata` only.
|
||||
|
||||
Finally, to add caching, synchronizing, and updating server state in your Next.js app, let's refactor the user data fetching by using the Apollo client and our GraphQL API instead.
|
||||
|
||||
So, first add the following GraphQL query to retrieve the current user data from the `UserProvider.js` file:
|
||||
|
||||
```js title="UserProvider.js"
|
||||
import { gql } from '@apollo/client'
|
||||
|
||||
const GET_USER_QUERY = gql`
|
||||
query GetUser($id: uuid!) {
|
||||
user(id: $id) {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
metadata
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export function UserProvider() {
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Then, replace the `useUserData` hook with the `useUserId` hook to retrieve the current user's ID only.
|
||||
|
||||
```js title="UserProvider.js"
|
||||
import { useUserId } from '@nhost/nextjs'
|
||||
|
||||
export function UserProvider() {
|
||||
const id = useUserId()
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, we can run our GraphQL query using the `useQuery` hook and the current user's ID.
|
||||
|
||||
```jsx title="UserProvider.js"
|
||||
// highlight-next-line
|
||||
import { gql, useQuery } from '@apollo/client'
|
||||
|
||||
export function UserProvider({ children = null }) {
|
||||
const id = useUserId()
|
||||
// highlight-start
|
||||
const { loading, error, data } = useQuery(GET_USER_QUERY, {
|
||||
variables: { id }
|
||||
})
|
||||
const user = data?.user
|
||||
// highlight-end
|
||||
|
||||
// highlight-start
|
||||
if (error) {
|
||||
return <p>Something went wrong. Try to refresh the page.</p>
|
||||
}
|
||||
if (loading) {
|
||||
return null
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
return <UserContext.Provider value={{ user }}>{children}</UserContext.Provider>
|
||||
}
|
||||
```
|
||||
|
||||
You now have a fully functional Next.js application. Congratulations!
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Did you enjoy Nhost? Give us a star ⭐ on [Github](https://github.com/nhost/nhost). Thank you!
|
||||
- Check out our more in-depth [examples](https://github.com/nhost/nhost/tree/main/examples).
|
||||
- Build your next app with [Nhost](https://app.nhost.io/)!
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
title: 'Quickstart: React'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
This is a quickstart guide for React with Nhost.
|
||||
674
docs/docs/platform/quickstarts/react.mdx
Normal file
674
docs/docs/platform/quickstarts/react.mdx
Normal file
@@ -0,0 +1,674 @@
|
||||
---
|
||||
title: 'Quickstart: React'
|
||||
sidebar_position: 1
|
||||
image: /img/og/platform/quickstart-react.png
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
# Quickstart: React
|
||||
|
||||
## Introduction
|
||||
|
||||
This quickstart guide provides the steps you need to build a simple React app
|
||||
powered by Nhost for the backend. It includes:
|
||||
|
||||
- Database: [PostgreSQL](https://www.postgresql.org/)
|
||||
- Instant GraphQL API: [Hasura](https://hasura.io/)
|
||||
- Authentication: [Hasura Auth](https://github.com/nhost/hasura-auth/)
|
||||
- Storage: [Hasura Storage](https://hub.docker.com/r/nhost/hasura-storage)
|
||||
|
||||
By the end of this guide, you'll have a full-stack app that allows users to log
|
||||
in to access a protected dashboard and update their profile information.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before getting started, let's make sure that your development environment is
|
||||
ready.
|
||||
|
||||
You'll need **Node.js** version 14 or later: [install it from here](https://nodejs.org/en/).
|
||||
|
||||
## Project setup
|
||||
|
||||
### Create a new Nhost app
|
||||
|
||||
import CreateApp from '@site/src/components/create-nhost-app.mdx'
|
||||
|
||||
<CreateApp />
|
||||
|
||||
:::info
|
||||
You can also connect your Nhost app to a GitHub repository. When you do this, any updates you push to your code will automatically be deployed. [Learn more](https://docs.nhost.io/platform/github-integration).
|
||||
:::
|
||||
|
||||
## Initialize the app
|
||||
|
||||
### Create a React app
|
||||
|
||||
The simplest way to create a new React application is by using the tool called
|
||||
`create-react-app`, which bootstraps a React app for you without the hassle of
|
||||
configuring everything yourself.
|
||||
|
||||
So, open your terminal, and run the following command:
|
||||
|
||||
```bash
|
||||
npx create-react-app my-nhost-app --template nhost-quickstart
|
||||
```
|
||||
|
||||
:::info
|
||||
This command uses an [existing template](https://github.com/nhost/cra-template-nhost-quickstart), through the `--template` flag, which already contains the React components and pages we'll use for this guide.
|
||||
:::
|
||||
|
||||
You can now `cd` into your project directory:
|
||||
|
||||
```bash
|
||||
cd my-nhost-app
|
||||
```
|
||||
|
||||
And run the development server with the following command:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
If everything is working fine, your React development server should be running
|
||||
on port 3000. Open [http://localhost:3000](http://localhost:3000) from your
|
||||
browser to check this out.
|
||||
|
||||
### Configure Nhost with React
|
||||
|
||||
To work with Nhost from within our React app, we'll use the
|
||||
[React SDK](https://github.com/nhost/nhost/tree/main/packages/react) provided
|
||||
by Nhost. It's a wrapper around the
|
||||
[Nhost JavaScript SDK](https://github.com/nhost/nhost/tree/main/packages/nhost-js) which
|
||||
gives us a way to interact with our Nhost backend using React hooks.
|
||||
|
||||
You can install the Nhost React SDK with:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm install @nhost/react graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn add @nhost/react graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Next, open your `App.js` file as we'll now configure Nhost inside our app.
|
||||
|
||||
The Nhost React SDK comes with a React provider named `NhostReactProvider` that
|
||||
makes the authentication state and all the provided React hooks available in our
|
||||
application.
|
||||
|
||||
Use the following code to instantiate a new Nhost client and link it to your
|
||||
Nhost backend:
|
||||
|
||||
```jsx title="src/App.js"
|
||||
import { NhostClient, NhostReactProvider } from '@nhost/react'
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: process.env.REACT_APP_NHOST_BACKEND_URL || ''
|
||||
})
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<NhostReactProvider nhost={nhost}>
|
||||
<BrowserRouter>{/* ... */}</BrowserRouter>
|
||||
</NhostReactProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
|
||||
Finally, make sure to create an environment variable named
|
||||
`REACT_APP_NHOST_BACKEND_URL` to store your Nhost backend URL:
|
||||
|
||||
```yaml title=".env.local"
|
||||
REACT_APP_NHOST_BACKEND_URL=YOUR_NHOST_BACKEND_URL
|
||||
```
|
||||
|
||||
You can find your Nhost backend URL for your project from [your dashboard](https://app.nhost.io) as shown below:
|
||||
|
||||

|
||||
|
||||
:::caution
|
||||
Don't forget to restart your React server after saving your `.env.local`
|
||||
file to load your new environment variable.
|
||||
:::
|
||||
|
||||
## Build the app
|
||||
|
||||
### Add authentication
|
||||
|
||||
#### 1. Sign-up
|
||||
|
||||
The next step is to allow our users to authenticate into our application.
|
||||
Let's start with implementing the sign-up process.
|
||||
|
||||
For that, we'll use the `useSignUpEmailPassword` hook provided by the Nhost
|
||||
React SDK within our `SignUp` component.
|
||||
|
||||
So, open up the corresponding file from your project, and use the following
|
||||
code:
|
||||
|
||||
```jsx title="src/components/SignUp.js"
|
||||
import styles from '../styles/components/SignUp.module.css'
|
||||
import { useState } from 'react'
|
||||
import { useSignUpEmailPassword } from '@nhost/react'
|
||||
import { Link, Navigate } from 'react-router-dom'
|
||||
import Input from './Input'
|
||||
import Spinner from './Spinner'
|
||||
|
||||
const SignUp = () => {
|
||||
const [firstName, setFirstName] = useState('')
|
||||
const [lastName, setLastName] = useState('')
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
const { signUpEmailPassword, isLoading, isSuccess, needsEmailVerification, isError, error } =
|
||||
useSignUpEmailPassword()
|
||||
|
||||
const handleOnSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
signUpEmailPassword(email, password, {
|
||||
displayName: `${firstName} ${lastName}`.trim(),
|
||||
metadata: {
|
||||
firstName,
|
||||
lastName
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (isSuccess) {
|
||||
return <Navigate to="/" replace={true} />
|
||||
}
|
||||
|
||||
const disableForm = isLoading || needsEmailVerification
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.card}>
|
||||
<div className={styles['logo-wrapper']}>
|
||||
<img src={process.env.PUBLIC_URL + 'logo.svg'} alt="logo" />
|
||||
</div>
|
||||
|
||||
{needsEmailVerification ? (
|
||||
<p className={styles['verification-text']}>
|
||||
Please check your mailbox and follow the verification link to verify your email.
|
||||
</p>
|
||||
) : (
|
||||
<form onSubmit={handleOnSubmit} className={styles.form}>
|
||||
<div className={styles['input-group']}>
|
||||
<Input
|
||||
label="First name"
|
||||
value={firstName}
|
||||
onChange={(e) => setFirstName(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
label="Last name"
|
||||
value={lastName}
|
||||
onChange={(e) => setLastName(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
type="email"
|
||||
label="Email address"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
label="Create password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
|
||||
<button type="submit" disabled={disableForm} className={styles.button}>
|
||||
{isLoading ? <Spinner size="sm" /> : 'Create account'}
|
||||
</button>
|
||||
|
||||
{isError ? <p className={styles['error-text']}>{error?.message}</p> : null}
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className={styles.text}>
|
||||
Already have an account?{' '}
|
||||
<Link to="/sign-in" className={styles.link}>
|
||||
Sign in
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignUp
|
||||
```
|
||||
|
||||
By default, the user must verify his email address before fully signing up. You can change this setting from your Nhost dashboard.
|
||||
|
||||
#### 2. Sign-in
|
||||
|
||||
Now that new users can sign up for our application, let's see how to allow
|
||||
existing users to sign in with email and password.
|
||||
|
||||
For that, we will use the Nhost hook named `useSignInEmailPassword` inside our
|
||||
`SignIn` component the same way we did with our `SignUp` component. So, here's
|
||||
what your component should look like after applying the changes for the sign-in
|
||||
logic:
|
||||
|
||||
```jsx title="src/components/SignIn.js"
|
||||
import styles from '../styles/components/SignIn.module.css'
|
||||
import { useState } from 'react'
|
||||
import { useSignInEmailPassword } from '@nhost/react'
|
||||
import { Link, Navigate } from 'react-router-dom'
|
||||
import Input from './Input'
|
||||
import Spinner from './Spinner'
|
||||
|
||||
const SignIn = () => {
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
const { signInEmailPassword, isLoading, isSuccess, needsEmailVerification, isError, error } =
|
||||
useSignInEmailPassword()
|
||||
|
||||
const handleOnSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
signInEmailPassword(email, password)
|
||||
}
|
||||
|
||||
if (isSuccess) {
|
||||
return <Navigate to="/" replace={true} />
|
||||
}
|
||||
|
||||
const disableForm = isLoading || needsEmailVerification
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.card}>
|
||||
<div className={styles['logo-wrapper']}>
|
||||
<img src={process.env.PUBLIC_URL + 'logo.svg'} alt="logo" />
|
||||
</div>
|
||||
|
||||
{needsEmailVerification ? (
|
||||
<p className={styles['verification-text']}>
|
||||
Please check your mailbox and follow the verification link to verify your email.
|
||||
</p>
|
||||
) : (
|
||||
<form onSubmit={handleOnSubmit} className={styles.form}>
|
||||
<Input
|
||||
type="email"
|
||||
label="Email address"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
label="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={disableForm}
|
||||
required
|
||||
/>
|
||||
|
||||
<button type="submit" disabled={disableForm} className={styles.button}>
|
||||
{isLoading ? <Spinner size="sm" /> : 'Sign in'}
|
||||
</button>
|
||||
|
||||
{isError ? <p className={styles['error-text']}>{error?.message}</p> : null}
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className={styles.text}>
|
||||
No account yet?{' '}
|
||||
<Link to="/sign-up" className={styles.link}>
|
||||
Sign up
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignIn
|
||||
```
|
||||
|
||||
#### 3. Sign-out
|
||||
|
||||
Finally, to allow the users to sign out from the app, we can use the Nhost
|
||||
`useSignOut` hook:
|
||||
|
||||
```jsx title="src/components/Layout.js"
|
||||
import { useSignOut } from '@nhost/react'
|
||||
|
||||
const Layout = () => {
|
||||
const { signOut } = useSignOut()
|
||||
|
||||
const menuItems = [
|
||||
//..
|
||||
{
|
||||
label: 'Logout',
|
||||
onClick: signOut,
|
||||
icon: LogoutIcon
|
||||
}
|
||||
]
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
### Protect routes
|
||||
|
||||
Now that we have implemented authentication, we can easily decide who can access
|
||||
certain parts of our application.
|
||||
|
||||
In our case, we'll only allow authenticated users to have access to the `/` and
|
||||
`/profile` routes. All the other users should be redirected to the `/sign-in`
|
||||
page if they try to access those routes.
|
||||
|
||||
To do so, we can create a wrapper component (`ProtectedRoute`) to check the authentication status of the current user using the Nhost SDK:
|
||||
|
||||
```jsx title="src/components/ProtectedRoute.js"
|
||||
import styles from '../styles/components/ProtectedRoute.module.css'
|
||||
import { useAuthenticationStatus } from '@nhost/react'
|
||||
import { Navigate, useLocation } from 'react-router-dom'
|
||||
import Spinner from './Spinner'
|
||||
|
||||
const ProtectedRoute = ({ children }) => {
|
||||
const { isAuthenticated, isLoading } = useAuthenticationStatus()
|
||||
const location = useLocation()
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Spinner />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return <Navigate to="/sign-in" state={{ from: location }} replace />
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
export default ProtectedRoute
|
||||
```
|
||||
|
||||
Then, we can use a [layout route](https://reactrouter.com/docs/en/v6/getting-started/concepts#layout-routes) in our `App.js` file, to wrap the `ProtectedRoute` component around the routes we want to protect:
|
||||
|
||||
```jsx title="src/App.js"
|
||||
import ProtectedRoute from './components/ProtectedRoute'
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<NhostReactProvider nhost={nhost}>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="sign-up" element={<SignUp />} />
|
||||
<Route path="sign-in" element={<SignIn />} />
|
||||
<Route
|
||||
path="/"
|
||||
// highlight-start
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<Layout />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
// highlight-end
|
||||
>
|
||||
<Route index element={<Dashboard />} />
|
||||
<Route path="profile" element={<Profile />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</NhostReactProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Retrieve user data
|
||||
|
||||
Finally, let's display the information of the authenticated user throughout his
|
||||
dashboard to make the app more personalized.
|
||||
|
||||
Getting the current authenticated user data is quite easy. Indeed, we
|
||||
can use the `useUserData` hook provided by Nhost to do it.
|
||||
|
||||
So, open the `components/Layout.js` file and use this hook like so:
|
||||
|
||||
```js
|
||||
import { useUserData } from '@nhost/react'
|
||||
|
||||
const Layout = () => {
|
||||
const user = useUserData()
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
That's it! The JSX code for rendering the user data (email, display name, etc.)
|
||||
is already included in your components as part of the template you've
|
||||
bootstraped at the beginning of this guide.
|
||||
|
||||
### Update user data
|
||||
|
||||
Nhost provides a GraphQL API through Hasura so that we can query and mutate our
|
||||
data instantly.
|
||||
|
||||
In this tutorial, we'll use the
|
||||
[Apollo GraphQL client](https://www.apollographql.com/) for interacting with
|
||||
this GraphQL API.
|
||||
|
||||
So, start by installing the following dependencies:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm install @nhost/react-apollo @apollo/client
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn add @nhost/react-apollo @apollo/client
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Then, add the `NhostApolloProvider` from `@nhost/react-apollo` into your
|
||||
`App.js` file.
|
||||
|
||||
```jsx title="src/App.js"
|
||||
import { NhostApolloProvider } from '@nhost/react-apollo'
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<NhostReactProvider nhost={nhost}>
|
||||
<NhostApolloProvider nhost={nhost}>{/* ... */}</NhostApolloProvider>
|
||||
</NhostReactProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
From there, we can construct our GraphQL query and use the Apollo `useMutation`
|
||||
hook to execute that query when the user submits the form from the profile page:
|
||||
|
||||
```js title="src/pages/Profile.js"
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
const UPDATE_USER_MUTATION = gql`
|
||||
mutation ($id: uuid!, $displayName: String!, $metadata: jsonb) {
|
||||
updateUser(pk_columns: { id: $id }, _set: { displayName: $displayName, metadata: $metadata }) {
|
||||
id
|
||||
displayName
|
||||
metadata
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Profile = () => {
|
||||
const [mutateUser, { loading: updatingProfile }] = useMutation(UPDATE_USER_MUTATION)
|
||||
|
||||
const updateUserProfile = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
try {
|
||||
await mutateUser({
|
||||
variables: {
|
||||
id: user.id,
|
||||
displayName: `${firstName} ${lastName}`.trim(),
|
||||
metadata: {
|
||||
firstName,
|
||||
lastName
|
||||
}
|
||||
}
|
||||
})
|
||||
toast.success('Updated successfully', { id: 'updateProfile' })
|
||||
} catch (error) {
|
||||
toast.error('Unable to update profile', { id: 'updateProfile' })
|
||||
}
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, since Hasura has an **allow nothing by default** policy, and we haven't
|
||||
set any permissions yet, our GraphQL mutations would fail.
|
||||
|
||||
So, open the Hasura console from the **Data** tab of your project from [your Nhost dashboard](https://app.nhost.io/). Then, go to the **permissions** tab of the `users` table, type in `user` in the role
|
||||
cell, and click the edit icon on the `select` operation:
|
||||
|
||||

|
||||
|
||||
To restrict the user to read his own data only, specify a condition with the
|
||||
user's ID and the `X-Hasura-User-ID` session variable, which is passed with each
|
||||
requests.
|
||||
|
||||

|
||||
|
||||
Next, select the columns you'd like the users to have access to, and click
|
||||
**Save Permissions**.
|
||||
|
||||

|
||||
|
||||
Repeat the same steps on the `update` operation for the `user` role to allow
|
||||
users to update their `displayName` and `metadata` only.
|
||||
|
||||
Finally, to add caching, synchronizing, and updating server state in your React app, let's refactor the user data fetching using the Apollo client and our GraphQL API instead.
|
||||
|
||||
So, first add the following GraphQL query to retrieve the current user data from the `Layout` component:
|
||||
|
||||
```js title="src/components/Layout.js"
|
||||
import { gql } from '@apollo/client'
|
||||
|
||||
const GET_USER_QUERY = gql`
|
||||
query GetUser($id: uuid!) {
|
||||
user(id: $id) {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
metadata
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Layout = () => {
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Then, replace the `useUserData` hook with the `useUserId` hook to retrieve the current user's ID.
|
||||
|
||||
```js title="src/components/Layout.js"
|
||||
import { useUserId } from '@nhost/react'
|
||||
|
||||
const Layout = () => {
|
||||
const id = useUserId()
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, we can run our GraphQL query using the `useQuery` hook and the current user's ID.
|
||||
|
||||
```jsx title="src/components/Layout.js"
|
||||
// highlight-next-line
|
||||
import { gql, useQuery } from '@apollo/client'
|
||||
|
||||
const Layout = () => {
|
||||
const id = useUserId()
|
||||
// highlight-start
|
||||
const { loading, error, data } = useQuery(GET_USER_QUERY, {
|
||||
variables: { id }
|
||||
})
|
||||
const user = data?.user
|
||||
// highlight-end
|
||||
|
||||
//...
|
||||
|
||||
return (
|
||||
<div>
|
||||
<header>{/* ... */}</header>
|
||||
|
||||
<main className={styles.main}>
|
||||
<div className={styles['main-container']}>
|
||||
{/* highlight-start */}
|
||||
{error ? (
|
||||
<p>Something went wrong. Try to refresh the page.</p>
|
||||
) : !loading ? (
|
||||
<Outlet context={{ user }} />
|
||||
) : null}
|
||||
{/* highlight-end */}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
You now have a fully functional React application. Congratulations!
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Did you enjoy Nhost? Give us a star ⭐ on [Github](https://github.com/nhost/nhost). Thank you!
|
||||
- Check out our more in-depth [examples](https://github.com/nhost/nhost/tree/main/examples).
|
||||
- Build your next app with [Nhost](https://app.nhost.io/)!
|
||||
1190
docs/docs/platform/quickstarts/redwoodjs.mdx
Normal file
1190
docs/docs/platform/quickstarts/redwoodjs.mdx
Normal file
File diff suppressed because it is too large
Load Diff
714
docs/docs/platform/quickstarts/vue.mdx
Normal file
714
docs/docs/platform/quickstarts/vue.mdx
Normal file
@@ -0,0 +1,714 @@
|
||||
---
|
||||
title: 'Quickstart: Vue'
|
||||
sidebar_position: 3
|
||||
image: /img/og/platform/quickstart-vue.png
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
# Quickstart: Vue
|
||||
|
||||
## Introduction
|
||||
|
||||
This quickstart guide provides the steps you need to build a simple Vue app
|
||||
powered by Nhost for the backend. It includes:
|
||||
|
||||
- Database: [PostgreSQL](https://www.postgresql.org/)
|
||||
- Instant GraphQL API: [Hasura](https://hasura.io/)
|
||||
- Authentication: [Hasura Auth](https://github.com/nhost/hasura-auth/)
|
||||
- Storage: [Hasura Storage](https://hub.docker.com/r/nhost/hasura-storage)
|
||||
|
||||
By the end of this guide, you'll have a full-stack app that allows users to log
|
||||
in to access a protected dashboard and update their profile information.
|
||||
|
||||
:::tip
|
||||
You can see the result of this quickstart [in our main repository](https://github.com/nhost/nhost/tree/main/examples/vue-quickstart).
|
||||
|
||||
You can also preview it in the browser: [](https://stackblitz.com/github/nhost/nhost/tree/main/examples/vue-quickstart)
|
||||
:::
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before getting started, let's make sure that your development environment is
|
||||
ready.
|
||||
|
||||
You'll need **Node.js** version 14 or later: [install it from here](https://nodejs.org/en/).
|
||||
|
||||
## Project setup
|
||||
|
||||
### Create a new Nhost app
|
||||
|
||||
import CreateApp from '@site/src/components/create-nhost-app.mdx'
|
||||
|
||||
<CreateApp />
|
||||
|
||||
:::info
|
||||
You can also connect your Nhost app to a GitHub repository. When you do this, any updates you push to your code will automatically be deployed. [Learn more](https://docs.nhost.io/platform/github-integration).
|
||||
:::
|
||||
|
||||
## Initialize the app
|
||||
|
||||
### Create a Vue app
|
||||
|
||||
We will use a simple adaptation of [Vitesse Lite](https://github.com/antfu/vitesse-lite), a ready-to-deploy Vite template by Anthony Fu. We can scaffold it with [degit](https://github.com/Rich-Harris/degit).
|
||||
|
||||
Open your terminal, and run the following command:
|
||||
|
||||
```bash
|
||||
npx degit nhost/vue-quickstart my-nhost-app
|
||||
```
|
||||
|
||||
You can now go into your project directory, install dependencies, and start the development server:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
cd my-nhost-app
|
||||
npm install
|
||||
npm dev
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
cd my-nhost-app
|
||||
yarn
|
||||
yarn dev
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
|
||||
```bash
|
||||
cd my-nhost-app
|
||||
pnpm install
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
If everything is working fine, your Vue development server should be running
|
||||
on port 3000. Open [http://localhost:3000](http://localhost:3000) from your
|
||||
browser to check this out.
|
||||
|
||||
### Configure Nhost with Vue
|
||||
|
||||
To work with Nhost from within our Vue app, we'll use the
|
||||
[Vue SDK](https://github.com/nhost/nhost/tree/main/packages/react) provided
|
||||
by Nhost. It's a wrapper around the
|
||||
[Nhost JavaScript SDK](https://github.com/nhost/nhost/tree/main/packages/nhost-js) which
|
||||
gives us a way to interact with our Nhost backend using Vue composables.
|
||||
|
||||
You can install the Nhost Vue SDK with:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm install @nhost/vue graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn add @nhost/vue graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
|
||||
```bash
|
||||
pnpm add @nhost/vue graphql
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Next, open your `src/main.ts` file as we'll now configure Nhost inside our app.
|
||||
|
||||
The Nhost Vue SDK comes with a `NhostClient` that can be loaded into the Vue application as a plugin.
|
||||
It makes the authentication state and all the provided Vue composables available in our
|
||||
application.
|
||||
|
||||
Use the following code to instantiate a new Nhost client and link it to your
|
||||
Nhost backend:
|
||||
|
||||
```tsx title="src/main.ts"
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import routes from 'virtual:generated-pages'
|
||||
// highlight-start
|
||||
import { NhostClient } from '@nhost/vue'
|
||||
// highlight-end
|
||||
import App from './App.vue'
|
||||
|
||||
import '@unocss/reset/tailwind.css'
|
||||
import './styles/main.css'
|
||||
import 'uno.css'
|
||||
|
||||
// highlight-start
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: import.meta.env.VITE_NHOST_URL
|
||||
})
|
||||
// highlight-end
|
||||
|
||||
const app = createApp(App)
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes
|
||||
})
|
||||
app
|
||||
.use(router)
|
||||
// highlight-start
|
||||
.use(nhost)
|
||||
// highlight-end
|
||||
app.mount('#app')
|
||||
```
|
||||
|
||||
Finally, make sure to create an environment variable named
|
||||
`VITE_NHOST_URL` to store your Nhost backend URL:
|
||||
|
||||
```bash title=".env"
|
||||
VITE_NHOST_URL=YOUR_NHOST_BACKEND_URL
|
||||
```
|
||||
|
||||
You can find your Nhost backend URL for your project from [your dashboard](https://app.nhost.io) as shown below:
|
||||
|
||||

|
||||
|
||||
## Build the app
|
||||
|
||||
### Add authentication
|
||||
|
||||
#### 1. Sign-up
|
||||
|
||||
The next step is to allow our users to authenticate into our application.
|
||||
Let's start with implementing the sign-up process.
|
||||
|
||||
For that, we'll use the `useSignUpEmailPassword` composable provided by the Nhost
|
||||
Vue SDK within a `/sign-up` page.
|
||||
|
||||
Let's create a new page in your project using the following code:
|
||||
|
||||
```markup title="src/pages/sign-up.vue"
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useSignUpEmailPassword } from '@nhost/vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const { signUpEmailPassword, needsEmailVerification } = useSignUpEmailPassword()
|
||||
const router = useRouter()
|
||||
const firstName = ref('')
|
||||
const lastName = ref('')
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const handleSubmit = async (event: Event) => {
|
||||
event.preventDefault()
|
||||
const { isSuccess } = await signUpEmailPassword(email, password, {
|
||||
metadata: { firstName, lastName }
|
||||
})
|
||||
if (isSuccess) router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p v-if="needsEmailVerification">
|
||||
Please check your mailbox and follow the verification link to verify your email.
|
||||
</p>
|
||||
|
||||
<form v-else @submit="handleSubmit">
|
||||
<input v-model="firstName" placeholder="First name" class="input" /><br />
|
||||
<input v-model="lastName" placeholder="Last name" class="input" /><br />
|
||||
<input v-model="email" type="email" placeholder="Email" class="input" /><br />
|
||||
<input v-model="password" type="password" placeholder="Password" class="input" /><br />
|
||||
|
||||
<button class="btn-submit" type="submit">Sign up</button>
|
||||
</form>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### 2. Sign-in
|
||||
|
||||
Now that new users can sign up for our application, let's see how to allow
|
||||
existing users to sign in with email and password.
|
||||
|
||||
For that, we will use the Nhost composable named `useSignInEmailPassword` inside a new
|
||||
`sign-in` page the same way we did with our `sign-up` page. Let's create a `src/pages/sign-in.vue` component:
|
||||
|
||||
```markup title="src/pages/sign-in.vue"
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useSignInEmailPassword } from '@nhost/vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const { signInEmailPassword, needsEmailVerification } = useSignInEmailPassword()
|
||||
const router = useRouter()
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const handleSubmit = async (event: Event) => {
|
||||
event.preventDefault()
|
||||
const { isSuccess } = await signInEmailPassword(email, password)
|
||||
if (isSuccess) router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p v-if="needsEmailVerification">
|
||||
Your email is not yet verified. Please check your mailbox and follow the verification link
|
||||
finish registration.
|
||||
</p>
|
||||
|
||||
<form v-else @submit="handleSubmit">
|
||||
<input v-model="email" type="email" placeholder="Email" class="input" /><br />
|
||||
<input v-model="password" type="password" placeholder="Password" class="input" /><br />
|
||||
|
||||
<button class="btn-submit" type="submit">Sign in</button>
|
||||
<p>No account yet? <router-link to="/sign-up"> Sign up </router-link></p>
|
||||
</form>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### 3. Home page
|
||||
|
||||
Let's also add links to sign up and sign in in our index page.
|
||||
|
||||
```markup title="src/pages/index.vue"
|
||||
<template>
|
||||
<div>
|
||||
<div i-carbon-home text-4xl inline-block />
|
||||
<p>Nhost with Vue</p>
|
||||
<p>
|
||||
<em text-sm op75>Quickstart</em>
|
||||
</p>
|
||||
<div py-4 />
|
||||
<router-link class="btn" to="/sign-up"> Sign Up </router-link><br />
|
||||
<router-link class="btn" to="/sign-in"> Sign In </router-link>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### 4. Sign-out
|
||||
|
||||
Finally, to allow the users to sign out from the app, we can use the Nhost
|
||||
`useSignOut` composable. We'll also use `useAuthenticationStatus` to show the button only when the user is authenticated:
|
||||
|
||||
```markup title="src/components/Footer.vue"
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuthenticated, useSignOut } from '@nhost/vue'
|
||||
import { isDark, toggleDark } from '~/composables'
|
||||
const isAuthenticated = useAuthenticated()
|
||||
const { signOut } = useSignOut()
|
||||
const router = useRouter()
|
||||
const handleSignOut = () => {
|
||||
signOut()
|
||||
router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav text-xl mt-6 inline-flex gap-2>
|
||||
<button class="icon-btn !outline-none" @click="toggleDark()">
|
||||
<div v-if="isDark" i-carbon-moon />
|
||||
<div v-else i-carbon-sun />
|
||||
</button>
|
||||
|
||||
<button v-if="isAuthenticated" class="icon-btn !outline-none" @click="handleSignOut">
|
||||
<div i-carbon-logout />
|
||||
</button>
|
||||
</nav>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Protect routes
|
||||
|
||||
Now that we have implemented authentication, we can easily decide who can access
|
||||
certain parts of our application.
|
||||
|
||||
Let's create a profile page that will be only accessible to authenticated users. If an unauthenticated user attempts to load it, it will redirect them to the `/sign-up` page:
|
||||
|
||||
```markup title="src/pages/profile.vue"
|
||||
<template>
|
||||
<div>
|
||||
<div i-carbon-home text-4xl inline-block />
|
||||
<p>Profile page</p>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
Then, we can use a [beforeEach navigation guqes](https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards) in our `main.ts` file:
|
||||
|
||||
```tsx title="src/main.ts"
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import routes from 'virtual:generated-pages'
|
||||
import { NhostClient } from '@nhost/vue'
|
||||
import App from './App.vue'
|
||||
|
||||
import '@unocss/reset/tailwind.css'
|
||||
import './styles/main.css'
|
||||
import 'uno.css'
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: import.meta.env.VITE_NHOST_URL
|
||||
})
|
||||
|
||||
const app = createApp(App)
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes
|
||||
})
|
||||
|
||||
// highlight-start
|
||||
router.beforeEach(async (to) => {
|
||||
if (to.path === '/profile' && !(await nhost.auth.isAuthenticatedAsync())) {
|
||||
return '/sign-in'
|
||||
}
|
||||
return true
|
||||
})
|
||||
// highlight-end
|
||||
|
||||
app.use(router).use(nhost)
|
||||
app.mount('#app')
|
||||
```
|
||||
|
||||
Let's finally add a link to the profile page in the index page `/`:
|
||||
|
||||
```markup title="src/pages/index.vue"
|
||||
<template>
|
||||
<div>
|
||||
<div i-carbon-home text-4xl inline-block />
|
||||
<p>Nhost with Vue</p>
|
||||
<p>
|
||||
<em text-sm op75>Quickstart</em>
|
||||
</p>
|
||||
<div py-4 />
|
||||
<!-- highlight-start -->
|
||||
<router-link class="btn" to="/profile"> Profile </router-link><br />
|
||||
<!-- highlight-end -->
|
||||
<router-link class="btn" to="/sign-up"> Sign Up </router-link><br />
|
||||
<router-link class="btn" to="/sign-in"> Sign In </router-link>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Retrieve user data
|
||||
|
||||
Finally, let's display the information of the authenticated user throughout his
|
||||
dashboard to make the app more personalized.
|
||||
|
||||
Getting the current authenticated user data is quite easy. Indeed, we
|
||||
can use the `useUserData` composable provided by Nhost to do it. When the user is authenticated, it returns the information fetched from the `users` table, such as the display name, the email, or the user's roles. This composable returns `null` until the user is effectively authenticated.
|
||||
|
||||
Let's update the profile page to use it:
|
||||
|
||||
```markup title="src/pages/profile.vue"
|
||||
<!-- highlight-start -->
|
||||
<script setup lang="ts">
|
||||
import { useUserData } from '@nhost/vue'
|
||||
const user = useUserData()
|
||||
</script>
|
||||
<!-- highlight-end -->
|
||||
<template>
|
||||
<div>
|
||||
<div i-carbon-home text-4xl inline-block />
|
||||
<p>Profile page</p>
|
||||
<p>
|
||||
<em text-sm op75>Quickstart</em>
|
||||
</p>
|
||||
<!-- highlight-start -->
|
||||
<div v-if="user" py-4>
|
||||
<p>Hello, {{ user?.displayName }}. Your email is {{ user?.email }}.</p>
|
||||
<!-- highlight-end -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Update user data
|
||||
|
||||
Nhost provides a GraphQL API through Hasura so that we can query and mutate our
|
||||
data instantly.
|
||||
|
||||
In this tutorial, we'll use [Vue Apollo v4](https://v4.apollo.vuejs.org) for interacting with
|
||||
this GraphQL API. Nhost comes with a custom Apollo client that syncs the Apollo client with the authentication status of your users.
|
||||
|
||||
So, start by installing the following dependencies:
|
||||
|
||||
<Tabs groupId="package-manager">
|
||||
<TabItem value="npm" label="npm" default>
|
||||
|
||||
```bash
|
||||
npm install @nhost/apollo @apollo/client graphql graphql-tag @vue/apollo-composable
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn">
|
||||
|
||||
```bash
|
||||
yarn add @nhost/apollo @apollo/client graphql graphql-tag @vue/apollo-composable
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
|
||||
```bash
|
||||
pnpm add @nhost/apollo @apollo/client graphql graphql-tag @vue/apollo-composable
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Then, create the Apollo client in your `src/main.ts` file, and provide it to your Vue app:
|
||||
|
||||
```tsx title="src/main.ts"
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import routes from 'virtual:generated-pages'
|
||||
import { NhostClient } from '@nhost/vue'
|
||||
// highlight-start
|
||||
import { createApolloClient } from '@nhost/apollo'
|
||||
import { DefaultApolloClient } from '@vue/apollo-composable'
|
||||
// highlight-end
|
||||
import App from './App.vue'
|
||||
|
||||
import '@unocss/reset/tailwind.css'
|
||||
import './styles/main.css'
|
||||
import 'uno.css'
|
||||
|
||||
const nhost = new NhostClient({
|
||||
backendUrl: import.meta.env.VITE_NHOST_URL
|
||||
})
|
||||
|
||||
const app = createApp(App)
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes
|
||||
})
|
||||
|
||||
router.beforeEach(async (to) => {
|
||||
if (to.path === '/profile' && !(await nhost.auth.isAuthenticatedAsync())) {
|
||||
return '/sign-in'
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// highlight-start
|
||||
const apolloClient = createApolloClient({ nhost })
|
||||
// highlight-end
|
||||
|
||||
app
|
||||
.use(router)
|
||||
.use(nhost)
|
||||
// highlight-start
|
||||
.provide(DefaultApolloClient, apolloClient)
|
||||
// highlight-end
|
||||
.mount('#app')
|
||||
```
|
||||
|
||||
From there, we can construct our GraphQL query and use the Apollo `useMutation`
|
||||
composable to execute that query when the user submits the form from the profile page:
|
||||
|
||||
```markup title="src/pages/profile.vue"
|
||||
<script setup lang="ts">
|
||||
import { gql } from '@apollo/client/core'
|
||||
import { useNhostClient, useUserData } from '@nhost/vue'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { ref } from 'vue'
|
||||
const user = useUserData()
|
||||
const { nhost } = useNhostClient()
|
||||
const UPDATE_USER_MUTATION = gql`
|
||||
mutation ($id: uuid!, $displayName: String!, $metadata: jsonb) {
|
||||
updateUser(
|
||||
pk_columns: { id: $id }
|
||||
_set: { displayName: $displayName, metadata: $metadata }
|
||||
) {
|
||||
id
|
||||
displayName
|
||||
metadata
|
||||
}
|
||||
}
|
||||
`
|
||||
const firstName = ref('')
|
||||
const lastName = ref('')
|
||||
const { mutate, loading, error } = useMutation(UPDATE_USER_MUTATION)
|
||||
|
||||
const updateUserProfile = async (event: Event) => {
|
||||
event.preventDefault()
|
||||
if (user.value) {
|
||||
await mutate({
|
||||
id: user.value.id,
|
||||
displayName: `${firstName.value} ${lastName.value}`.trim(),
|
||||
metadata: {
|
||||
firstName: firstName.value,
|
||||
lastName: lastName.value
|
||||
}
|
||||
})
|
||||
await client.auth.refreshSession()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div i-carbon-home text-4xl inline-block />
|
||||
<p>Profile page</p>
|
||||
<p>
|
||||
<em text-sm op75>Quickstart</em>
|
||||
</p>
|
||||
<div v-if="user" py-4>
|
||||
<p>Hello, {{ user?.displayName }}. Your email is {{ user?.email }}.</p>
|
||||
<form @submit="updateUserProfile">
|
||||
<input v-model="firstName" placeholder="First name" class="input" /><br />
|
||||
<input v-model="lastName" placeholder="Last name" class="input" /><br />
|
||||
<button className="m-3 text-sm btn" :disabled="loading">Save</button>
|
||||
<div v-if="error">{{ error.message }}</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
:::tip
|
||||
You probably have noticed that we are calling `client.auth.refreshSession()` after we updated the user using the GraphQL mutation. The Nhost client only extracts user information from the access token (JWT), that is kept in memory and refreshed every 15 minutes. As user information has been updated, we force an access token refresh so it is kept up to date.
|
||||
:::
|
||||
|
||||
Finally, since Hasura has an **allow nothing by default** policy, and we haven't
|
||||
set any permissions yet, our GraphQL mutations would fail.
|
||||
|
||||
So, open the Hasura console from the **Data** tab of your project from [your Nhost dashboard](https://app.nhost.io/). Then, go to the **permissions** tab of the `users` table, type in `user` in the role cell, and click the edit icon on the `select` operation:
|
||||
|
||||

|
||||
|
||||
To restrict the user to read his own data only, specify a condition with the user's ID and the `X-Hasura-User-ID` session variable, which is passed with each requests.
|
||||
|
||||

|
||||
|
||||
Next, select the columns you'd like the users to have access to, and click
|
||||
**Save Permissions**.
|
||||
|
||||

|
||||
|
||||
Repeat the same steps on the `update` operation for the `user` role to allow
|
||||
users to update their `displayName` and `metadata` only.
|
||||
|
||||
Finally, to add real-time caching, synchronizing, and updating server state in your Vue app, let's refactor the user data fetching using the Apollo client and our GraphQL API instead.
|
||||
|
||||
First add the following GraphQL subscription to retrieve the current user data component:
|
||||
|
||||
```ts title="src/pages/profile.vue"
|
||||
import { gql } from '@apollo/client/core'
|
||||
|
||||
const GET_USER_SUBSCRIPTION = gql`
|
||||
subscription GetUser($id: uuid!) {
|
||||
user(id: $id) {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
metadata
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
`
|
||||
```
|
||||
|
||||
Then, replace the `useUserData` composable with the `useUserId` composable to retrieve the current user's ID.
|
||||
|
||||
```ts title="src/pages/profile.vue"
|
||||
import { useUserId } from '@nhost/vue'
|
||||
|
||||
const id = useUserId()
|
||||
```
|
||||
|
||||
Finally, we can run our GraphQL subscription using the `useSubscription` composable and the current user's ID. Here is the full `profile.vue` page:
|
||||
|
||||
```markup title="src/pages/profile.vue"
|
||||
<script setup lang="ts">
|
||||
import { gql } from '@apollo/client/core'
|
||||
import { useNhostClient, useUserId } from '@nhost/vue'
|
||||
import { useMutation, useSubscription } from '@vue/apollo-composable'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const { nhost } = useNhostClient()
|
||||
const GET_USER_SUBSCRIPTION = gql`
|
||||
subscription GetUser($id: uuid!) {
|
||||
user(id: $id) {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
metadata
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
`
|
||||
const id = useUserId()
|
||||
|
||||
const { result } = useSubscription(
|
||||
GET_USER_SUBSCRIPTION,
|
||||
computed(() => ({ id: id.value }))
|
||||
)
|
||||
const user = computed(() => result.value?.user)
|
||||
|
||||
const UPDATE_USER_MUTATION = gql`
|
||||
mutation ($id: uuid!, $displayName: String!, $metadata: jsonb) {
|
||||
updateUser(
|
||||
pk_columns: { id: $id }
|
||||
_set: { displayName: $displayName, metadata: $metadata }
|
||||
) {
|
||||
id
|
||||
displayName
|
||||
metadata
|
||||
}
|
||||
}
|
||||
`
|
||||
const firstName = ref('')
|
||||
const lastName = ref('')
|
||||
const { mutate, loading, error } = useMutation(UPDATE_USER_MUTATION)
|
||||
|
||||
const updateUserProfile = async (event: Event) => {
|
||||
event.preventDefault()
|
||||
if (user.value) {
|
||||
await mutate({
|
||||
id: user.value.id,
|
||||
displayName: `${firstName.value} ${lastName.value}`.trim(),
|
||||
metadata: {
|
||||
firstName: firstName.value,
|
||||
lastName: lastName.value
|
||||
}
|
||||
})
|
||||
await nhost.auth.refreshSession()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div i-carbon-home text-4xl inline-block />
|
||||
<p>Profile page</p>
|
||||
<p>
|
||||
<em text-sm op75>Quickstart</em>
|
||||
</p>
|
||||
<div v-if="user" py-4>
|
||||
<p>Hello, {{ user.displayName }}. Your email is {{ user.email }}.</p>
|
||||
<form @submit="updateUserProfile">
|
||||
<input v-model="firstName" placeholder="First name" class="input" /><br />
|
||||
<input v-model="lastName" placeholder="Last name" class="input" /><br />
|
||||
<button className="m-3 text-sm btn" :disabled="loading">Save</button>
|
||||
<div v-if="error">{{ error.message }}</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
You now have a fully functional Vue application. Congratulations!
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Did you enjoy Nhost? Give us a star ⭐ on [Github](https://github.com/nhost/nhost). Thank you!
|
||||
- Check out our more in-depth [examples](https://github.com/nhost/nhost/tree/main/examples).
|
||||
- Build your next app with [Nhost](https://app.nhost.io/)!
|
||||
106
docs/docs/platform/serverless-functions.mdx
Normal file
106
docs/docs/platform/serverless-functions.mdx
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: 'Serverless Functions'
|
||||
sidebar_position: 8
|
||||
image: /img/og/platform/serverless-functions.png
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
With Nhost, you can deploy Serverless Functions to execute custom code. Each serverless function is its HTTP endpoint.
|
||||
|
||||
Serverless functions can be used to handle [event triggers](/platform/database/event-triggers), form submission, integrations (e.g. Stripe, Slack, etc), and more.
|
||||
|
||||
---
|
||||
|
||||
## Creating a Serverless Function
|
||||
|
||||
Every `.js` (JavaScript) and `.ts` (TypeScript) file in the `functions/` folder of your Nhost app is its own serverless function.
|
||||
|
||||
<Tabs groupId="language">
|
||||
<TabItem value="ts" label="TypeScript" default>
|
||||
|
||||
```ts title=functions/test.ts
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
export default (req: Request, res: Response) => {
|
||||
res.status(200).send(`Hello ${req.query.name}!`)
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
You **MUST** install `express` locally in the base directory of your Nhost app.
|
||||
|
||||
```bash
|
||||
npm install -d express @types/express
|
||||
# or yarn
|
||||
yarn add -d express @types/express
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="js" label="JavaScript">
|
||||
|
||||
```js title=functions/test.js
|
||||
export default (req, res) => {
|
||||
res.status(200).send(`Hello ${req.query.name}!`)
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
You **MUST** install `express` locally in the base directory of your Nhost app.
|
||||
|
||||
```bash
|
||||
npm install -d express
|
||||
# or yarn
|
||||
yarn add -D express
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Deploying Serverless Functions
|
||||
|
||||
Serverless functions are automatically deployed using Nhost's [GitHub integration](/platform/github-integration).
|
||||
|
||||
You can prepend files and folders with an underscore (`_`) to prevent them from being treated as serverless functions and be turned into HTTP endpoints. This is useful if you have, for example, a utils file (`functions/_utils.js`) or a utils folder (`functions/_utils/[multiple-files].js`).
|
||||
|
||||
## Routing
|
||||
|
||||
HTTP endpoints are automatically generated based on the file structure inside `functions/`.
|
||||
|
||||
Here's an example of four serverless functions with their files and their HTTP endpoints:
|
||||
|
||||
| File | HTTP Endpoint |
|
||||
| --------------------------- | ------------------------------------------------------------- |
|
||||
| `functions/index.js` | `https://[app-subdomain].nhost.run/v1/functions/` |
|
||||
| `functions/users/index.ts` | `https://[app-subdomain].nhost.run/v1/functions/users` |
|
||||
| `functions/users/active.ts` | `https://[app-subdomain].nhost.run/v1/functions/users/active` |
|
||||
| `functions/my-company.js` | `https://[app-subdomain].nhost.run/v1/functions/my-company` |
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
[Environment variables](/platform/environment-variables) are available inside your serverless functions. Both in production and when running Nhost locally using the [Nhost CLI](/platform/cli).
|
||||
|
||||
## Billing
|
||||
|
||||
Serverless functions are billed per GB-sec or GB-hour. 1 GB-hour is 3600 GB-seconds.
|
||||
|
||||
1 GB-sec is 1 function with 1 GB of RAM running for 1 second. If 1 function with 1 GB of RAM runs for 3600 seconds, that's the equivalent of 1 GB-hour.
|
||||
|
||||
All serverless functions in production are running with 128 MB of RAM.
|
||||
|
||||
## Node Version
|
||||
|
||||
All serverless functions in production are running Node.js version 14.x.
|
||||
|
||||
## Regions
|
||||
|
||||
Serverless Functions are always deployed to the same region as your app.
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"label": "Serverless Functions",
|
||||
"position": 8
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
---
|
||||
title: 'Event triggers'
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
Event triggers are HTTP webhooks that fire on a database event, such as insert, update, or delete. These events are usually a result of GraphQL mutations, but any other database operation will also fire events.
|
||||
|
||||
**Example:** Imagine you want to send an email every time a user makes a new order in an e-commerce application. To achieve that, you would create an event trigger on **insert** for the **orders table**. Every time an order is created, an event trigger will send a webhook with the order information, and the webhook can send out an email to the customer.
|
||||
|
||||
---
|
||||
|
||||
## Creating event triggers
|
||||
|
||||
Event triggers are managed in Hasura. Go to Hasura, then select **Events** in the main menu and press "Create".
|
||||
|
||||

|
||||
|
||||
Nhost's [environment variables](/platform/environment-variables) can be used in event trigger headers. For example, you can attach `NHOST_WEBHOOK_SECRET` to an outgoing webhook here.
|
||||
|
||||
---
|
||||
|
||||
## Serverless functions
|
||||
|
||||
It's a common pattern to write a serverless function to catch a webhook fired by an event. When creating webhooks that are meant for your own serverless functions, use the following URL:
|
||||
|
||||
```bash
|
||||
https://[app-subdomain].nhost.run/v1/functions/my-endpoint
|
||||
```
|
||||
|
||||
The environment variable `NHOST_BACKEND_URL` will have the correct value.
|
||||
|
||||
```bash
|
||||
{{NHOST_BACKEND_URL}}/v1/functions/my-endpoint
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
In your serverless function, you need to make sure the request actually comes from your Hasura instance. To do this, you must do two things:
|
||||
|
||||
- Add the header `nhost-webhook-secret` when creating the event in Hasura. Set this to `NHOST_WEBHOOK_SECRET`.
|
||||
- Check the header in the serverless function. It should match the environment variable `NHOST_WEBHOOK_SECRET`.
|
||||
|
||||
```js
|
||||
export default async function handler(req, res) {
|
||||
|
||||
// Check webhook secret to make sure the request is valid
|
||||
if (
|
||||
req.headers['nhost-webhook-secret'] !== process.env.NHOST_WEBHOOK_SECRET
|
||||
) {
|
||||
return res.status(400).send('Incorrect webhook secret')
|
||||
}
|
||||
|
||||
// Do something
|
||||
// Example:
|
||||
// - Send an email
|
||||
// - Create a subscription in Stripe
|
||||
// - Generate a PDF
|
||||
// - Send a message to Slack or Discord
|
||||
// - Update some data in the database
|
||||
|
||||
console.log(JSON.stringify(req.body, null, 2))
|
||||
|
||||
return res.send('OK')
|
||||
}
|
||||
```
|
||||
@@ -1,62 +0,0 @@
|
||||
---
|
||||
title: 'Serverless Functions'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Nhost gives you the option to run serverless functions on the following runtimes:
|
||||
|
||||
- `Node.js 14` (Both JavaScript and Typescript)
|
||||
|
||||
---
|
||||
|
||||
## Creating functions
|
||||
|
||||
Every `.js` and `.ts` file in the `functions/` folder of your Nhost app will be exposed as an HTTP endpoint. You have to
|
||||
|
||||
```js
|
||||
// In functions/hello/[name].js
|
||||
export default (req, res) => {
|
||||
res.status(200).send(`Hello ${req.query.name}!`);
|
||||
};
|
||||
```
|
||||
|
||||
Or, if you prefer TypeScript:
|
||||
|
||||
```ts
|
||||
// In functions/hello/[name].ts
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
export default (req: Request, res: Response) => {
|
||||
res.status(200).send(`Hello ${req.query.name}!`);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Routing
|
||||
|
||||
HTTP endpoints are automatically generated based on the file structure under `functions/`.
|
||||
|
||||
As such, given this file structure:
|
||||
|
||||
```text
|
||||
functions/index.js
|
||||
functions/users/index.ts
|
||||
functions/active.ts
|
||||
functions/my-company.js
|
||||
```
|
||||
|
||||
The following endpoints will be available:
|
||||
|
||||
- https://yourappid.nhost.run/v1/functions/ from `functions/index.js`.
|
||||
- https://yourappid.nhost.run/v1/functions/users from `functions/users/index.ts`.
|
||||
- https://yourappid.nhost.run/v1/functions/users/active from `functions/users/active.ts`.
|
||||
- https://yourappid.nhost.run/v1/functions/my-company from `functions/my-company.js`.
|
||||
|
||||
If you've used Netlify or Vercel, this routing logic will be familiar to you.
|
||||
|
||||
---
|
||||
|
||||
## Infrastructure
|
||||
|
||||
Serverless Functions are deployed to AWS Lambda in the same region as your app.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
title: 'Storage'
|
||||
sidebar_position: 7
|
||||
image: /img/og/platform/storage.png
|
||||
---
|
||||
|
||||
Nhost stores and serves files of any type in your backend.
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"label": "Storage",
|
||||
"position": 7
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"label": "CLI",
|
||||
"position": 4,
|
||||
"link": { "type": "generated-index", "slug": "/reference/cli" }
|
||||
}
|
||||
16
docs/docs/reference/cli/down.mdx
Normal file
16
docs/docs/reference/cli/down.mdx
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: 'down'
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
Delete all containers created by `nhost up`
|
||||
|
||||
```bash
|
||||
nhost down
|
||||
```
|
||||
|
||||
To delete all containers **and the local database**, append `--data` to the command.
|
||||
|
||||
```bash
|
||||
nhost down --data
|
||||
```
|
||||
22
docs/docs/reference/cli/global-flags.mdx
Normal file
22
docs/docs/reference/cli/global-flags.mdx
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: 'Global Flags'
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
### `--debug`, `-d`
|
||||
|
||||
Turn on debug output.
|
||||
|
||||
```bash
|
||||
nhost up --debug
|
||||
nhost init -d
|
||||
```
|
||||
|
||||
### `--log-file`, `-f`
|
||||
|
||||
Save output to a given file.
|
||||
|
||||
```bash
|
||||
nhost up -d --log-file some-file.txt
|
||||
nhost logs -f some-file.txt
|
||||
```
|
||||
6
docs/docs/reference/cli/index.mdx
Normal file
6
docs/docs/reference/cli/index.mdx
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: 'CLI'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
This section is a reference for the commands available in the [Nhost CLI](/platform/cli).
|
||||
18
docs/docs/reference/cli/init.mdx
Normal file
18
docs/docs/reference/cli/init.mdx
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: 'init'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Intialize a local Nhost app in the current working directory.
|
||||
|
||||
```
|
||||
nhost init
|
||||
```
|
||||
|
||||
If you already have a Nhost app in Nhost Cloud you can use that app as a starting point by appending `--remote` to the command.
|
||||
|
||||
This will pull the database migrations and Hasura metadata from the Nhost Cloud app locally for you to use as a starting point.
|
||||
|
||||
```
|
||||
nhost init --remote
|
||||
```
|
||||
10
docs/docs/reference/cli/link.mdx
Normal file
10
docs/docs/reference/cli/link.mdx
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: 'link'
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
Link the local Nhost app in your working directory to an app in Nhost Cloud.
|
||||
|
||||
```bash
|
||||
nhost link
|
||||
```
|
||||
10
docs/docs/reference/cli/list.mdx
Normal file
10
docs/docs/reference/cli/list.mdx
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: 'list'
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
List all your Nhost apps in Nhost Cloud.
|
||||
|
||||
```bash
|
||||
nhost list
|
||||
```
|
||||
10
docs/docs/reference/cli/login.mdx
Normal file
10
docs/docs/reference/cli/login.mdx
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: 'login'
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
Authenticate the CLI with your Nhost user.
|
||||
|
||||
```bash
|
||||
nhost login
|
||||
```
|
||||
10
docs/docs/reference/cli/logout.mdx
Normal file
10
docs/docs/reference/cli/logout.mdx
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: 'logout'
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
Remove authentication for the CLI.
|
||||
|
||||
```bash
|
||||
nhost logout
|
||||
```
|
||||
10
docs/docs/reference/cli/logs.mdx
Normal file
10
docs/docs/reference/cli/logs.mdx
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: 'logs'
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
Output logs of any service container
|
||||
|
||||
```bash
|
||||
nhost logs
|
||||
```
|
||||
@@ -1,97 +0,0 @@
|
||||
---
|
||||
title: 'Nhost CLI'
|
||||
---
|
||||
|
||||
Run `nhost help` in your terminal to get a detailed listing of all available commands.
|
||||
|
||||
---
|
||||
|
||||
## `nhost`
|
||||
|
||||
Run the Nhost development environment. If the current directory has not been initialized as an Nhost app, `nhost` will run you through the initialization.
|
||||
|
||||
```bash
|
||||
nhost
|
||||
```
|
||||
|
||||
### Frontend templates
|
||||
|
||||
The `nhost` command will offer you the option of cloning frontend templates for framework of your choice (Nuxt, Next.js, React).
|
||||
|
||||
The frontend template will be cloned in the `web/` directory of your app root. It will have the Nhost SDK preinstalled and configured.
|
||||
|
||||
---
|
||||
|
||||
## `nhost dev`
|
||||
|
||||
Launch the development environment for your app.
|
||||
|
||||
```bash
|
||||
nhost dev
|
||||
```
|
||||
|
||||
To trace all output and debug issues, run `nhost dev --debug`.
|
||||
|
||||
```bash
|
||||
nhost dev --debug
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `nhost init`
|
||||
|
||||
Intialize a blank local app in current working directory:
|
||||
|
||||
```bash
|
||||
nhost init
|
||||
```
|
||||
|
||||
Or clone an existing app from [nhost.io](https://nhost.io):
|
||||
|
||||
```bash
|
||||
nhost init --remote
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `nhost link`
|
||||
|
||||
Link the local Nhost app in your working directory to [nhost.io](https://nhost.io).
|
||||
|
||||
```bash
|
||||
nhost link
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `nhost logs`
|
||||
|
||||
Check real-time logs of any service container
|
||||
|
||||
You can run this command in parallel, while your local environment is already running. Use `-f` to save output to a file.
|
||||
|
||||
```bash
|
||||
nhost logs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Global flags
|
||||
|
||||
Turn on debug output.
|
||||
|
||||
#### `--debug`, `-d`
|
||||
|
||||
```bash
|
||||
nhost dev --debug
|
||||
nhost init -d
|
||||
```
|
||||
|
||||
#### `--log-file`, `-f`
|
||||
|
||||
Save output to a given file.
|
||||
|
||||
```bash
|
||||
nhost dev -d --log-file some-file.txt
|
||||
nhost logs -f some-file.txt
|
||||
```
|
||||
14
docs/docs/reference/cli/up.mdx
Normal file
14
docs/docs/reference/cli/up.mdx
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: 'up'
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
Launch the development environment for your app. Once the environment is up, the command will:
|
||||
|
||||
- Apply database migrations.
|
||||
- Apply the Hasura metadata.
|
||||
- Apply seed data.
|
||||
|
||||
```bash
|
||||
nhost up
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user