Skip to content

Commit 8bc3ed3

Browse files
change fork to its own repo
0 parents  commit 8bc3ed3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2399
-0
lines changed

.env

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Docker specific configs
2+
# use only letters and numbers for the project name
3+
COMPOSE_PROJECT_NAME=postgreststarterkit
4+
5+
6+
# Global configs
7+
DEVELOPMENT=1
8+
JWT_SECRET=reallyreallyreallyreallyverysafe
9+
10+
# DB connection details (used by all containers)
11+
# set PG_VERSION to match your production db major version
12+
PG_VERSION=11.2
13+
DB_HOST=db
14+
DB_PORT=5432
15+
DB_NAME=app
16+
DB_SCHEMA=api
17+
DB_USER=authenticator
18+
DB_PASS=authenticatorpass
19+
20+
# OpenResty
21+
POSTGREST_HOST=postgrest
22+
POSTGREST_PORT=3000
23+
24+
# PostgREST
25+
DB_ANON_ROLE=anonymous
26+
DB_POOL=10
27+
#MAX_ROWS=
28+
#PRE_REQUEST=
29+
SERVER_PROXY_URI=http://localhost:8080/rest
30+
31+
# PostgreSQL container config
32+
# Use this to connect directly to the db running in the container
33+
SUPER_USER=superuser
34+
SUPER_USER_PASSWORD=superuserpass
35+
36+
# RabbitMQ
37+
RABBITMQ_DEFAULT_USER=admin
38+
RABBITMQ_DEFAULT_PASS=adminpass

.gitattributes

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Set the default behavior, in case people don't have core.autocrlf set.
2+
* text=auto
3+
# convert all bash scripts to run with linux line endings by default.
4+
*.sh text eol=lf

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
package-lock.json
3+
.db-data

.snapshotResolver.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = {
2+
// resolves from test to snapshot path
3+
resolveSnapshotPath: (testPath, snapshotExtension) =>
4+
testPath.replace('__tests__', '__snapshots__') + snapshotExtension,
5+
6+
// resolves from snapshot to test path
7+
resolveTestPath: (snapshotFilePath, snapshotExtension) =>
8+
snapshotFilePath
9+
.replace('__snapshots__', '__tests__')
10+
.slice(0, -snapshotExtension.length),
11+
12+
// Example test path, used for preflight consistency check of the implementation above
13+
// testPathForConsistencyCheck: 'some/__tests__/example.test.js',
14+
testPathForConsistencyCheck: 'example.test.js',
15+
}

LICENSE.txt

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 2017-present Ruslan Talpa, subZero Cloud LLC.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# PostgREST Starter Kit
2+
3+
Base project and tooling for authoring REST API backends with [PostgREST](https://postgrest.com).
4+
5+
![PostgREST Starter Kit](https://raw.githubusercontent.com/wiki/subzerocloud/postgrest-starter-kit/images/postgrest-starter-kit.gif "PostgREST Starter Kit")
6+
7+
8+
## Purpose
9+
10+
PostgREST enables a different way of building data driven API backends. It does "one thing well" and that is to provide you with a REST api over your database, however to build a complex production system that does things like talk to 3rd party systems, sends emails, implements real time updates for browsers, write integration tests, implement authentication, you need additional components. For this reason, some developers either submit feature requests that are not the scope of PostgREST or think of it just as a prototyping utility and not a powerful/flexible production component with excellent performance. This repository aims to be a starting point for all PostgREST based projects and bring all components together under a well defined structure. We also provide tooling that will aid you with iterating on your project and tools/scripts to enable a build pipeline to push everything to production. There are quite a few components in the stack but you can safely comment out pg_amqp_bridge/rabbitmq (or even openresty) instances in docker-compose.yml if you don't need the features/functionality they provide.
11+
12+
## PostgREST+ as a service
13+
Run your PostgREST instance in [subZero cloud](https://subzero.cloud/postgrest-plus.html) and get additional features to the OS version ( [free plan](https://subzero.cloud/pricing.html) available). ALternatively, deploy an [enhanced version](https://docs.subzero.cloud/postgrest-plus/) of PostgREST on your infrastructure using binary and docker distributions.
14+
15+
**Fully Managed** &mdash; subZero automates every part of setup, running and scaling of PostgREST. Let your team focus on what they do best - building your product. Leave PostgREST management and monitoring to the experts.<br>
16+
**Faster Queries** &mdash; Run an [enhanced PostgREST](https://docs.subzero.cloud/postgrest-plus/) version that uses prepared statements instead of inline queries. This results in up to 30% faster response times.<br>
17+
**Custom Relations** &mdash; Define [custom relations](https://docs.subzero.cloud/postgrest-plus/) when automatic detection does not work. This allows you to use the powerful embedding feature even with the most complicated views<br>
18+
**GraphQL API** &mdash; In addition to the REST API you get a GraphQL api with no additional coding. Leverage all the powerful tooling, libraries and integrations for GraphQL in your frontend.<br>
19+
20+
## Features
21+
22+
✓ Cross-platform development on macOS, Windows or Linux inside [Docker](https://www.docker.com/)<br>
23+
[PostgreSQL](https://www.postgresql.org/) database schema boilerplate with authentication and authorization flow<br>
24+
[OpenResty](https://openresty.org/en/) configuration files for the reverse proxy<br>
25+
[RabbitMQ](https://www.rabbitmq.com/) integration through [pg-amqp-bridge](https://github.com/subzerocloud/pg-amqp-bridge)<br>
26+
[Lua](https://www.lua.org/) functions to hook into each stage of the HTTP request and add custom logic (integrate 3rd party systems)<br>
27+
✓ Debugging and live code reloading (sql/configs/lua) functionality using [subzero-cli](https://github.com/subzerocloud/subzero-cli)<br>
28+
✓ Full migration management (migration files are automatically created) through [subzero-cli](https://github.com/subzerocloud/subzero-cli)/[sqitch](http://sqitch.org/)/[apgdiff](https://github.com/subzerocloud/apgdiff)<br>
29+
✓ SQL unit test using [pgTAP](http://pgtap.org/)<br>
30+
✓ Integration tests with [SuperTest / Mocha](https://github.com/visionmedia/supertest)<br>
31+
✓ Docker files for building production images<br>
32+
✓ Community support on [Slack](https://slack.subzero.cloud/)<br>
33+
✓ Compatible with [subZero Starter Kit](https://github.com/subzerocloud/subzero-starter-kit) if you need a GraphQL API and a few [more features](https://github.com/subzerocloud/subzero-starter-kit#features) with no additional work<br>
34+
35+
36+
## Directory Layout
37+
38+
```bash
39+
.
40+
├── db # Database schema source files and tests
41+
│ └── src # Schema definition
42+
│ ├── api # Api entities avaiable as REST endpoints
43+
│ ├── data # Definition of source tables that hold the data
44+
│ ├── libs # A collection modules of used throughout the code
45+
│ ├── authorization # Application level roles and their privileges
46+
│ ├── sample_data # A few sample rows
47+
│ └── init.sql # Schema definition entry point
48+
├── openresty # Reverse proxy configurations and Lua code
49+
│ ├── lua # Application Lua code
50+
│ ├── nginx # Nginx configuration files
51+
│ ├── html # Static frontend files
52+
│ └── Dockerfile # Dockerfile definition for building production images
53+
├── tests # Tests for all the components
54+
│ ├── db # pgTap tests for the db
55+
│ └── rest # REST interface tests
56+
├── docker-compose.yml # Defines Docker services, networks and volumes
57+
└── .env # Project configurations
58+
59+
```
60+
61+
62+
63+
## Installation
64+
65+
### Prerequisites
66+
* [Docker](https://www.docker.com)
67+
* [Node.js](https://nodejs.org/en/)
68+
* [subZero CLI](https://github.com/subzerocloud/subzero-cli#install)
69+
70+
### Create a New Project
71+
subzero-cli provides you with a `base-project` command that lets you create a new project structure:
72+
73+
```bash
74+
subzero base-project
75+
76+
? Enter the directory path where you want to create the project .
77+
? Choose the starter kit (Use arrow keys)
78+
subzero-starter-kit (REST & GraphQL)
79+
❯ postgrest-starter-kit (REST)
80+
```
81+
82+
After the files have been created, you can bring up your application (API).
83+
In the root folder of application, run the docker-compose command
84+
85+
```bash
86+
docker-compose up -d
87+
```
88+
89+
The API server will become available at the following endpoint:
90+
91+
- REST [http://localhost:8080/api/](http://localhost:8080/api/)
92+
93+
Try a simple request
94+
95+
```bash
96+
curl http://localhost:8080/api/todos?select=id,todo
97+
```
98+
99+
100+
## Development workflow and debugging
101+
102+
Execute `subzero dashboard` in the root of your project.<br />
103+
After this step you can view the logs of all the stack components (SQL queries will also be logged) and
104+
if you edit a sql/conf/lua file in your project, the changes will immediately be applied.
105+
106+
107+
## Testing
108+
109+
The starter kit comes with a testing infrastructure setup.
110+
You can write pgTAP tests that run directly in your database, useful for testing the logic that resides in your database (user privileges, Row Level Security, stored procedures).
111+
Integration tests are written in JavaScript.
112+
113+
Here is how you run them
114+
115+
```bash
116+
npm install # Install test dependencies
117+
npm test # Run all tests (db, api)
118+
npm run test_db # Run pgTAP tests
119+
npm run test_api # Run integration tests
120+
```
121+
122+
123+
## Deployment
124+
* [subZero Cloud](http://docs.subzero.cloud/production-infrastructure/subzero-cloud/)
125+
* [Amazon ECS+RDS](http://docs.subzero.cloud/production-infrastructure/aws-ecs-rds/)
126+
* [Amazon Fargate+RDS](http://docs.subzero.cloud/production-infrastructure/aws-fargate-rds/)
127+
* [Dedicated Linux Server](https://docs.subzero.cloud/production-infrastructure/ubuntu-server/)
128+
129+
## Contributing
130+
131+
Anyone and everyone is welcome to contribute.
132+
133+
## Support and Documentation
134+
* [Documentation](https://docs.subzero.cloud/postgrest-starter-kit/)
135+
* [PostgREST API Referance](https://postgrest.com/en/stable/api.html)
136+
* [PostgreSQL Manual](https://www.postgresql.org/docs/current/static/index.html)
137+
* [Slack](https://slack.subzero.cloud/) — Get help, share ideas and feedback
138+
* [GitHub Issues](https://github.com/subzerocloud/postgrest-starter-kit/issues) — Check open issues, send feature requests
139+
140+
## License
141+
142+
Copyright © 2017-present subZero Cloud, LLC.<br />
143+
This source code is licensed under [MIT](https://github.com/subzerocloud/postgrest-starter-kit/blob/master/LICENSE.txt) license<br />
144+
The documentation to the project is licensed under the [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/) license.
145+
146+
147+
148+
## CSRF prevention
149+
[OWASP CSRF cheatsheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#Checking_The_Referer_Header)

db/src/api/.login.spec.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const ROOT = process.cwd()
2+
const { get, post, put, patch, del } = require(`${ROOT}/util/fetch.js`)(
3+
'http://localhost:3000/'
4+
)
5+
6+
describe('/rpc/login', () => {
7+
test('success 200', async () => {
8+
const res = await post('rpc/login')({
9+
body: {
10+
11+
password: 'pass',
12+
},
13+
})
14+
res.body.csrf = 'checked'
15+
res.body.token = 'checked'
16+
res.body.refresh_token = 'checked'
17+
expect(res).toMatchSnapshot()
18+
})
19+
20+
test('fail 404', async () => {
21+
const res = await post('rpc/login')({
22+
body: {
23+
24+
password: 'nonexistent',
25+
},
26+
})
27+
expect(res).toMatchSnapshot()
28+
})
29+
})

db/src/api/.login.spec.js.snap

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`/rpc/login fail 404 1`] = `
4+
Object {
5+
"body": Object {
6+
"code": "28P01",
7+
"details": "Invalid email or password",
8+
"hint": "Make sure your email and password are correct!",
9+
"message": "invalid_password",
10+
},
11+
"status": 403,
12+
"statusText": "Forbidden",
13+
}
14+
`;
15+
16+
exports[`/rpc/login success 200 1`] = `
17+
Object {
18+
"body": Object {
19+
"csrf": "checked",
20+
"email": "[email protected]",
21+
"id": 1,
22+
"name": "alice",
23+
"refresh_token": "checked",
24+
"role": "webuser",
25+
"token": "checked",
26+
},
27+
"status": 200,
28+
"statusText": "OK",
29+
}
30+
`;

db/src/api/.logout.spec.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const ROOT = process.cwd()
2+
const { get, post, put, patch, del } = require(`${ROOT}/util/fetch.js`)(
3+
'http://localhost:3000/'
4+
)
5+
6+
describe('/rpc/logout', () => {
7+
test('success 200 without refresh_token', async () => {
8+
const res = await post('rpc/logout')({
9+
withRole: { id: 1, role: 'webuser' },
10+
})
11+
expect(res).toMatchSnapshot()
12+
})
13+
14+
test('success 200 with refresh_token', async () => {
15+
const res = await post('rpc/logout')({
16+
withRole: { id: 1, role: 'webuser' },
17+
body: {
18+
refresh_token: 'de688895-6181-440f-a25a-8a9ba14b2162',
19+
},
20+
})
21+
expect(res).toMatchSnapshot()
22+
})
23+
24+
test('success 200 with refresh_token which is not in db', async () => {
25+
const res = await post('rpc/logout')({
26+
withRole: { id: 1, role: 'webuser' },
27+
body: {
28+
refresh_token: 'de688895-6181-440f-a25a-000000000000',
29+
},
30+
})
31+
expect(res).toMatchSnapshot()
32+
})
33+
})

db/src/api/.logout.spec.js.snap

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`/rpc/logout success 200 with refresh_token 1`] = `
4+
Object {
5+
"body": "{\\"ok\\" : true}",
6+
"status": 200,
7+
"statusText": "OK",
8+
}
9+
`;
10+
11+
exports[`/rpc/logout success 200 with refresh_token which is not in db 1`] = `
12+
Object {
13+
"body": "{\\"ok\\" : true}",
14+
"status": 200,
15+
"statusText": "OK",
16+
}
17+
`;
18+
19+
exports[`/rpc/logout success 200 without refresh_token 1`] = `
20+
Object {
21+
"body": "{\\"ok\\" : true}",
22+
"status": 200,
23+
"statusText": "OK",
24+
}
25+
`;

db/src/api/.me.spec.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const ROOT = process.cwd()
2+
const { get, post, put, patch, del } = require(`${ROOT}/util/fetch.js`)(
3+
'http://localhost:3000/'
4+
)
5+
6+
describe('/rpc/me', () => {
7+
test('success 200 POST', async () => {
8+
const res = await post('rpc/me')({
9+
withRole: { id: 1, role: 'webuser' },
10+
})
11+
expect(res).toMatchSnapshot()
12+
})
13+
14+
test('success 200 GET', async () => {
15+
const res = await get('rpc/me')({
16+
withRole: { id: 1, role: 'webuser' },
17+
})
18+
expect(res).toMatchSnapshot()
19+
})
20+
21+
test('fail 401', async () => {
22+
const res = await post('rpc/me')({})
23+
expect(res).toMatchSnapshot()
24+
})
25+
})

0 commit comments

Comments
 (0)