From 7d419f885e6f5c7979c63eedcf6975f67d6452e7 Mon Sep 17 00:00:00 2001 From: fauna-brecht <56540741+fauna-brecht@users.noreply.github.com> Date: Wed, 22 Jan 2020 21:16:56 +0100 Subject: [PATCH] Let users define their own database as easily as possible. (#10209) * Let users define their own client token. * Make sure users know that GraphQL schema can also be done via the UI * Update README.md * Update setup.js Co-authored-by: Matthew Sweeney Co-authored-by: Joe Haddad --- examples/with-graphql-faunadb/README.md | 16 ++- examples/with-graphql-faunadb/next.config.js | 2 +- examples/with-graphql-faunadb/package.json | 8 +- .../with-graphql-faunadb/scripts/setup.js | 103 ++++++++++++++++++ 4 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 examples/with-graphql-faunadb/scripts/setup.js diff --git a/examples/with-graphql-faunadb/README.md b/examples/with-graphql-faunadb/README.md index 68815c44ce86dad..9201dce913aafb9 100644 --- a/examples/with-graphql-faunadb/README.md +++ b/examples/with-graphql-faunadb/README.md @@ -16,7 +16,21 @@ By importing a `.gql` or `.graphql` schema into FaunaDB ([see our sample schema You can start with this template [using `create-next-app`](#using-create-next-app) or by [downloading the repository manually](#download-manually). -To use a live FaunaDB database, create one and import this example's `schema.gql` file using the FaunaDB console. Create a client secret, then paste it into `next.config.js`. +To use a live FaunaDB database, create a database at [dashboard.fauna.com](https://dashboard.fauna.com/) and generate a server token by going to the **Security** tab on the left and then click **New Key**. Give the new key a name and select the 'Server' Role. Copy the token since the setup script will ask for it. Do not use it in the frontend, it has superpowers which you don't want to give to your users. + +The database can then be set up with the delivered setup by running: + +``` +yarn setup +``` + +This script will ask for the server token. Once you provide it with a valid token, this is what the script automatically does for you: + +- **Import the GraphQL schema**, by importing a GraphQL schema in FaunaDB, FaunaDB automatically sets up collections and indexes to support your queries. This is now done for you with this script but can also be done from the [dashboard.fauna.com](https://dashboard.fauna.com/) UI by going to the GraphQL tab +- **Create a role suitable for the Client**, FaunaDB has a security system that allows you to define which resources can be accessed for a specific token. That's how we limit our clients powers, feel free to look at the scripts/setup.js script to see how we make roles and tokens. +- **Create a token for that role** which is printed, this is the token to be used in the frontend. + +At the end, the newly generated client token will be printed and should be used to replace the '< GRAPHQL_SECRET >' placeholder in the next.config.js config. ### Using `create-next-app` diff --git a/examples/with-graphql-faunadb/next.config.js b/examples/with-graphql-faunadb/next.config.js index 53ded0ee42327ba..b80a98e8de9b981 100644 --- a/examples/with-graphql-faunadb/next.config.js +++ b/examples/with-graphql-faunadb/next.config.js @@ -8,7 +8,7 @@ module.exports = { | https://docs.fauna.com/fauna/current/security/ |-------------------------------------------------- */ - faunaDbSecret: 'fnADcnWRUcACE_6uDSw05MspruDdWKk88ZSmsm2a', + faunaDbSecret: '< GRAPHQL_SECRET >', faunaDbGraphQlEndpoint: 'https://graphql.fauna.com/graphql', }, } diff --git a/examples/with-graphql-faunadb/package.json b/examples/with-graphql-faunadb/package.json index 021dea565629111..7e5b79e34468ec7 100644 --- a/examples/with-graphql-faunadb/package.json +++ b/examples/with-graphql-faunadb/package.json @@ -5,11 +5,17 @@ "scripts": { "dev": "next", "build": "next build", - "start": "next start" + "start": "next start", + "setup": "node ./scripts/setup.js" }, "dependencies": { "next": "latest", "react": "^16.10.2", "react-dom": "^16.10.2" + }, + "devDependencies": { + "faunadb": "2.11.1", + "request": "2.88.0", + "stream-to-promise": "2.2.0" } } diff --git a/examples/with-graphql-faunadb/scripts/setup.js b/examples/with-graphql-faunadb/scripts/setup.js new file mode 100644 index 000000000000000..f4410feda221fb2 --- /dev/null +++ b/examples/with-graphql-faunadb/scripts/setup.js @@ -0,0 +1,103 @@ +// This script sets up the database to be used for this example application. +// Look at the code to see what is behind the magic +const faunadb = require('faunadb') +const q = faunadb.query +const request = require('request') +const fs = require('fs') +const streamToPromise = require('stream-to-promise') + +const readline = require('readline').createInterface({ + input: process.stdin, + output: process.stdout, +}) + +// In order to set up a database, we need a server key, so let's ask the user for a key. +readline.question(`Please provide the FaunaDB server key\n`, serverKey => { + // A graphql schema can be imported in override or merge mode: 'https://docs.fauna.com/fauna/current/api/graphql/endpoints#import' + const options = { + model: 'merge', + uri: 'https://graphql.fauna.com/import', + headers: { Authorization: `Bearer ${serverKey}` }, + } + const stream = fs.createReadStream('./schema.gql').pipe(request.post(options)) + + streamToPromise(stream) + .then(res => { + const readableResult = res.toString() + if (readableResult.startsWith('Invalid authorization header')) { + console.error('You need to provide a secret, closing. Try again') + return readline.close() + } else if (readableResult.startsWith('Invalid database secret')) { + console.error( + 'The secret you have provided is not valid, closing. Try again' + ) + return readline.close() + } else if (readableResult.includes('success')) { + console.log('1. Successfully imported schema') + return readline.close() + } + }) + .catch(err => { + console.error(err) + console.error(`Could not import schema, closing`) + }) + .then(res => { + // The GraphQL schema is important, this means that we now have a GuestbookEntry Colleciton and an entries index. + // Then we create a token that can only read and write to that index and collection + var client = new faunadb.Client({ secret: serverKey }) + return client + .query( + q.CreateRole({ + name: 'GuestbookRole', + privileges: [ + { + resource: q.Collection('GuestbookEntry'), + actions: { read: true, write: true }, + }, + { + resource: q.Index('entries'), + actions: { read: true }, + }, + ], + }) + ) + .then(res => { + console.log( + '2. Successfully created role to read and write guestbook entries' + ) + }) + .catch(err => { + if (err.toString().includes('instance already exists')) { + console.log('2. Role already exists.') + } else { + throw err + } + }) + }) + .catch(err => { + console.error(err) + console.error(`Failed to create role, closing`) + }) + .then(res => { + // The GraphQL schema is important, this means that we now have a GuestbookEntry Colleciton and an entries index. + // Then we create a token that can only read and write to that index and collection + var client = new faunadb.Client({ secret: serverKey }) + return client + .query( + q.CreateKey({ + role: q.Role('GuestbookRole'), + }) + ) + .then(res => { + console.log('3. Created key to use in client') + console.log( + 'Replace the < GRAPHQL_SECRET > placehold in next.config.js with:' + ) + console.log(res.secret) + }) + }) + .catch(err => { + console.error(err) + console.error(`Failed to create key, closing`) + }) +})