Skip to content

Commit

Permalink
Migrate from Firestore to PostgreSQL (#286)
Browse files Browse the repository at this point in the history
  • Loading branch information
koistya authored Mar 11, 2018
1 parent c16a06d commit e255dfc
Show file tree
Hide file tree
Showing 35 changed files with 845 additions and 351 deletions.
16 changes: 9 additions & 7 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ jobs:
- yarn-v1-
- run:
name: Install NPM modules
command: yarn
command: |
yarn install
yarn add firebase-tools --dev
- save_cache:
key: yarn-v1-{{ checksum "yarn.lock" }}
paths:
Expand All @@ -30,10 +32,10 @@ jobs:
command: |
yarn lint
yarn test --forceExit
- run:
name: Deploy
command: |
if [ "${CIRCLE_BRANCH}" == "master" ]; then
firebase deploy --token=$FIREBASE_TOKEN
fi
# - run:
# name: Deploy
# command: |
# if [ "${CIRCLE_BRANCH}" == "master" ]; then
# ./node_modules/firebase-tools/bin/firebase deploy --token=$FIREBASE_TOKEN
# fi

16 changes: 6 additions & 10 deletions .env
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
# JSON string with a service account credentials for Firebase Admin SDK
# https://firebase.google.com/docs/admin/setup
# Service Account Key for Firebase Admin SDK
# https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk

FIREBASE_CREDENTIALS=
FIREBASE_KEY=


# Firebase config for the client-side app

REACT_APP_FIREBASE={"apiKey":"AIzaSyAsuqpqt29-TIwBAu01Nbt5QnC3FIKO4A4","authDomain":"react-firebase-graphql.firebaseapp.com","databaseURL":"https://react-firebase-graphql.firebaseio.com","projectId":"react-firebase-graphql","storageBucket":"react-firebase-graphql.appspot.com","messagingSenderId":"564620986275"}


# PostgreSQL settings
# PostgreSQL connection settings
# https://www.postgresql.org/docs/current/static/libpq-envars.html

PGHOST=localhost
PGUSER=postgres
PGDATABASE=app
PGPASSWORD=
PGPORT=5432
PGSSLMODE=disable
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ This project was bootstraped with [React Starter Kit for Firebase][rfs] by [Kria
### Tech Stack

* [Create React App][cra] for development and test infrastructure (see [user guide][cradocs])
* [Firestore][firestore] and/or [Cloud SQL][cloudsql] (PostgreSQL) hosted database service
* [GraphQL][gqljs] and [Relay][relay] for declarative data fetching and maximum performance
* [Cloud SQL][cloudsql] (PostgreSQL edition) hosted database service
* [Material UI][mui] to reduce development time by integrating Google's [Material Design][material]
* [Styled Components][sc] for component friendly CSS styles ([docs][scdocs])
* [Firebase][firebase] for serverless architecture, authentication and free CDN hosting ([docs][fbdocs])
Expand All @@ -51,6 +51,7 @@ Also, you need to be familiar with [HTML][html], [CSS][css], [JavaScript][js] ([
│ ├── app.browser.js # Client-side rendering, e.g. ReactDOM.render(<App />, container)
│ ├── app.node.js # Server-side rendering, e.g. ReactDOMServer.renderToString(<App />)
│ ├── auth.js # Authentication manager
│ ├── authenticate.js # Authentication middleware for Express.js
│ ├── createRelay.browser.js # Relay factory method for browser envrironment
│ ├── createRelay.node.js # Relay factory method for Node.js envrironment
│ ├── router.js # Universal application router
Expand All @@ -71,16 +72,17 @@ Also, you need to be familiar with [HTML][html], [CSS][css], [JavaScript][js] ([
* [VS Code][vc] editor (preferred) + [Project Snippets][vcsnippets], [EditorConfig][vceditconfig],
[ESLint][vceslint], [Flow][vcflow], [Prettier][vcprettier], and [Babel JavaScript][vcjs] plug-ins
* [Watchman][watchman] file watcher used by Relay Modern
* [PostgreSQL][postgres] v9.6 or newer

### Getting Started

Just clone the repo, update environment variables in `.env`, and start hacking:
Just clone the repo, update environment variables in `.env` and/or `.env.local` file, and start
hacking:

```bash
$ git clone https://github.com/kriasoft/react-firebase-starter.git MyApp
$ cd MyApp
$ yarn install # Install project dependencies listed in package.json
$ yarn relay # Compile GraphQL queries
$ yarn setup # Installs dependencies; creates PostgreSQL database
$ yarn start # Compile the app and opens it in a browser with "live reload"
```

Expand All @@ -100,7 +102,7 @@ $ yarn db-rollback # Rollback the latest migration

```bash
$ yarn lint # Check JavaScript and CSS code for potential issues
$ yarn fix # Attempt to automatically fix ESLint warnings
$ yarn lint-fix # Attempt to automatically fix ESLint warnings
$ yarn test # Run unit tests. Or, `yarn test -- --watch`
```

Expand All @@ -127,8 +129,8 @@ yarn install
yarn relay
```

_NOTE: Try to merge as soon as the new changes land on the master branch in Node.js API Starter
repository, otherwise your project may differ too much from the base/upstream repo._
_NOTE: Try to merge as soon as the new changes land on the master branch in the upstream repository,
otherwise your project may differ too much from the base/upstream repo._

### How to Contribute

Expand Down Expand Up @@ -170,7 +172,6 @@ and [contributors](https://github.com/kriasoft/react-firebase-starter/graphs/con
[telegram]: https://t.me/ReactStarter
[cra]: https://github.com/facebook/create-react-app
[cradocs]: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md
[firestore]: https://cloud.google.com/firestore/
[cloudsql]: https://cloud.google.com/sql/
[gqljs]: http://graphql.org/graphql-js/
[relay]: http://facebook.github.io/relay/
Expand Down Expand Up @@ -201,3 +202,4 @@ and [contributors](https://github.com/kriasoft/react-firebase-starter/graphs/con
[vcprettier]: https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode
[vcjs]: https://marketplace.visualstudio.com/items?itemName=mgmcdermott.vscode-language-babel
[watchman]: https://github.com/facebook/watchman
[postgres]: https://www.postgresql.org/
4 changes: 2 additions & 2 deletions firebase.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"functions": {
"source": "."
"source": ".",
"ignore": ["**", "!build/**", "!ssl/**", "!package.json", "build/public/**"]
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"hosting": {
"public": "build/public",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "/graphql",
Expand Down
25 changes: 24 additions & 1 deletion knexfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,33 @@
* Copyright (c) 2015-present Kriasoft | MIT License
*/

require('dotenv').config();
require('dotenv').config({ path: '.env.local' });
require('dotenv').config({ path: '.env' });

const fs = require('fs');
const path = require('path');

function read(file) {
const filename = path.resolve(__dirname, `ssl/${file}`);
return fs.readFileSync(filename, 'utf8');
}

// Knex configuration
// http://knexjs.org/#knexfile
module.exports = {
client: 'pg',
migrations: {
tableName: 'migrations',
},
connection: {
ssl:
(process.env.PGSSLMODE || 'disable') !== 'disable'
? {
rejectUnauthorized: false,
ca: read('server-ca.pem'),
key: read('client-key.pem'),
cert: read('client-cert.pem'),
}
: undefined,
},
};
11 changes: 8 additions & 3 deletions migrations/20180101000000_initial.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@
exports.up = async db => {
await db.schema.createTable('users', table => {
table.uuid('id').notNullable().defaultTo(db.raw('uuid_generate_v4()')).primary();
table.string('username', 50).notNullable().unique();
table.string('email', 100);
table.string('display_name', 100);
table.string('photo_url', 200);
table.string('photo_url', 250);
table.boolean('is_admin').notNullable().defaultTo(false);
table.timestamps(false, true);
});

await db.schema.createTable('stories', table => {
table.uuid('id').notNullable().defaultTo(db.raw('uuid_generate_v4()')).primary();
table.uuid('author_id').notNullable().references('id').inTable('users').onDelete('CASCADE').onUpdate('CASCADE');
table.string('slug', 80).notNullable();
table.string('title', 80).notNullable();
table.string('url', 200);
table.text('text');
table.string('text', 2000);
table.boolean('is_url').notNullable().defaultTo(false);
table.boolean('approved').notNullable().defaultTo(false);
table.timestamps(false, true);
});

Expand Down
29 changes: 19 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,35 @@
"@babel/runtime": "^7.0.0-beta.40",
"@firebase/app": "^0.1.10",
"@firebase/auth": "^0.3.4",
"@firebase/firestore": "^0.3.4",
"body-parser": "^1.18.2",
"cookie": "^0.3.1",
"cookie-parser": "^1.4.3",
"ejs": "^2.5.7",
"express": "^4.16.2",
"express-graphql": "^0.6.12",
"firebase-admin": "^5.9.1",
"firebase-admin": "^5.10.0",
"firebase-functions": "^0.8.1",
"graphql": "^0.13.1",
"graphql-relay": "^0.5.4",
"history": "^4.7.2",
"idx": "^2.2.0",
"knex": "^0.14.4",
"material-ui": "1.0.0-beta.35",
"material-ui": "1.0.0-beta.36",
"pg": "^7.4.1",
"prop-types": "^15.6.1",
"query-string": "^5.1.0",
"raw-loader": "^0.5.1",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-relay": "^1.5.0",
"relay-runtime": "^1.5.0",
"request": "^2.83.0",
"request-promise-native": "^1.0.5",
"serialize-javascript": "^1.4.0",
"slug": "^0.9.1",
"styled-components": "^3.1.6",
"shortid": "^2.2.8",
"styled-components": "^3.2.1",
"universal-router": "^6.0.0",
"uuid": "^3.2.1",
"validator": "^9.4.1"
},
"devDependencies": {
Expand All @@ -59,19 +61,20 @@
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-flowtype": "^2.46.1",
"eslint-plugin-prettier": "^2.6.0",
"flow-bin": "^0.66.0",
"flow-bin": "^0.67.1",
"gh-pages": "^1.1.0",
"husky": "^0.15.0-rc.8",
"lint-staged": "^7.0.0",
"prettier": "^1.11.1",
"raw-loader": "^1.0.0-beta.0",
"react-app-tools": "2.0.0-beta.13",
"relay-compiler": "^1.5.0",
"stylelint": "^9.1.1",
"stylelint-config-primer": "^2.2.4",
"stylelint-config-standard": "^18.2.0",
"stylelint-config-styled-components-processor": "^0.1.1",
"stylelint-order": "^0.8.1",
"stylelint-processor-styled-components": "^1.3.0"
"stylelint-processor-styled-components": "^1.3.1"
},
"lint-staged": {
"*.js": [
Expand All @@ -83,18 +86,24 @@
"git add --force"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"scripts": {
"update-schema": "node ./scripts/update-schema",
"relay": "relay-compiler --src ./src --schema ./src/schema.graphql --include \"**/*.js\"",
"setup": "node ./scripts/setup",
"start": "react-app start",
"build": "react-app build",
"test": "react-app test --env=jsdom",
"test": "react-scripts test --env=jsdom",
"lint": "eslint --ignore-path .gitignore --ignore-pattern \"!**/.*\" . && stylelint \"src/**/*.js\"",
"fix": "eslint --ignore-path .gitignore --ignore-pattern \"!**/.*\" --fix .",
"precommit": "lint-staged",
"lint-fix": "eslint --ignore-path .gitignore --ignore-pattern \"!**/.*\" --fix .",
"db-change": "knex migrate:make",
"db-migrate": "knex migrate:latest",
"db-rollback": "knex migrate:rollback",
"db-seed": "knex seed:run",
"deploy": "yarn install && yarn build && firebase use dev && firebase deploy",
"deploy-prod": "yarn install && yarn build && firebase use prod && firebase deploy"
}
Expand Down
61 changes: 61 additions & 0 deletions scripts/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* React Starter Kit for Firebase
* https://github.com/kriasoft/react-firebase-starter
* Copyright (c) 2015-present Kriasoft | MIT License
*/

'use script';

const cp = require('child_process');

let db;
let status;

(async () => {
// Install Node.js dependencies
({ status } = cp.spawnSync('yarn', ['install'], { stdio: 'inherit' }));
if (status !== 0) process.exit(status);

const knex = require('knex');
const config = require('../knexfile');

// Check if database already exists
const database = process.env.PGDATABASE;
delete process.env.PGDATABASE;

db = new knex(config);

const { rowCount } = await db.raw(
'SELECT 1 FROM pg_database WHERE datname = ?',
[database],
);

// If the database doesn't exist, create a new one
if (!rowCount) {
console.log(`Creating a new database "${database}"...`);
await db.raw('CREATE DATABASE ??', [database]);
}

await db.destroy();

db = knex(config);
process.env.PGDATABASE = database;

// Make sure that the required PostgreSQL extensions are installed
await db.raw('CREATE EXTENSION IF NOT EXISTS ??', ['uuid-ossp']);
await db.raw('CREATE EXTENSION IF NOT EXISTS ??', ['hstore']);

await db.destroy();

// Migrate database schema to the latest version
({ status } = cp.spawnSync('yarn', ['db-migrate'], { stdio: 'inherit' }));
if (status !== 0) process.exit(status);

// Pre-compile GraphQL queries
({ status } = cp.spawnSync('yarn', ['relay'], { stdio: 'inherit' }));
if (status !== 0) process.exit(status);
})().catch(async err => {
console.error(err);
if (db) await db.destroy();
process.exit(1);
});
3 changes: 3 additions & 0 deletions scripts/update-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ const fs = require('fs');
const path = require('path');
const graphql = require('graphql');
const schema = require('../src/graphql/schema').default;
const db = require('../src/graphql/db').default;

fs.writeFileSync(
path.resolve(__dirname, '../src/schema.graphql'),
graphql.printSchema(schema, { commentDescriptions: true }),
'utf8',
);

db.destroy();
3 changes: 2 additions & 1 deletion src/app.browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import App from './components/App';
import createRelay from './createRelay.browser';
import * as serviceWorker from './serviceWorker';

firebase.initializeApp(JSON.parse(process.env.REACT_APP_FIREBASE));
firebase.initializeApp(window.config.firebase);
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);

ReactDOM.render(
<App history={createHistory()} createRelay={createRelay} />,
Expand Down
8 changes: 3 additions & 5 deletions src/app.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ import firebase from 'firebase-admin';
import api from './graphql';
import ssr from './ssr';

// JSON key with service account credentials
// https://firebase.google.com/docs/admin/setup
const credentials = JSON.parse(process.env.FIREBASE_CREDENTIALS);

if (!firebase.apps.length) {
// JSON key with service account credentials
// https://firebase.google.com/docs/admin/setup
firebase.initializeApp({
credential: firebase.credential.cert(credentials),
credential: firebase.credential.cert(JSON.parse(process.env.FIREBASE_KEY)),
});
}

Expand Down
Loading

0 comments on commit e255dfc

Please sign in to comment.