Skip to content

Commit 54fd7aa

Browse files
committed
use common in vercel example
1 parent f7421c2 commit 54fd7aa

22 files changed

+766
-94
lines changed

vercel-postgresql-neon/.dockerignore

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ Dockerfile
22
.dockerignore
33
node_modules
44
npm-debug.log
5-
README.md
65
.next
76
.git
87
.env.local

vercel-postgresql-neon/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,6 @@ next-env.d.ts
3838
.state
3939
db.sqlite
4040
fly.toml
41+
42+
# wrangler cache
43+
.wrangler

vercel-postgresql-neon/README.md

+15-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
# Northwind Traders (by subZero)
22
### Running on Vercel + PostgreSQL (Neon)
33
This is a demo of the Northwind dataset, see it live at [northwind-postgresql.vercel.app/](https://northwind-postgresql.vercel.app/).
4-
- Frontend is implemented in NextJS</li>
4+
- Frontend is implemented in NextJS
55
- Backend is implemented in Typescript and leverages subZero as a library to automatically expose a PostgREST compatible backend on top of the underlying database
6-
- Data is stored in a PostgreSQL database
6+
- Data is stored in a PostgreSQL database hosted on [Neon](https://neon.tech/)
77
- Everything is deployed to [Vercel](https://vercel.com)
88

9-
This dataset was sourced from [northwind-SQLite3](https://github.com/jpwhite3/northwind-SQLite3)
10-
119
## Running locally
1210
- Clone the repo
1311
- Install dependencies (you will need to have SQLite installed on your machine)
@@ -32,7 +30,7 @@ This dataset was sourced from [northwind-SQLite3](https://github.com/jpwhite3/no
3230
- Setup a PostgreSQL database on [Neon](https://neon.tech) and get a connection string
3331
- Provision the database
3432
```bash
35-
psql <db_connection_string> -f northwindtraders.sql
33+
psql <db_connection_string> -f northwindtraders-postgresql.sql
3634
```
3735
- Link the current directory to a Vercel project
3836
```bash
@@ -47,26 +45,29 @@ This dataset was sourced from [northwind-SQLite3](https://github.com/jpwhite3/no
4745
vercel --prod
4846
```
4947

50-
51-
52-
53-
54-
5548
## Implementation details
5649

5750
Most of the files in this directory are your basic NextJS setup with some configuration for tailwindcss and typescript.
5851
The interesting files are:
5952

60-
- The file implementing [the backend](pages/api/[...path].ts)
53+
- The file implementing [the backend](https://github.com/subzerocloud/showcase/blob/main/vercel-postgresql-neon/pages/api/%5B...path%5D.ts)
6154
Most of the code deals with the configuration of the backend, and 99% of the functionality is withing these lines:
6255
```typescript
6356
// parse the Request object into and internal AST representation
64-
let subzeroRequest = await subzero.parse(publicSchema, `${urlPrefix}/`, role, request)
57+
let subzeroRequest = await subzero.parse(publicSchema, `${urlPrefix}/`, role, req)
6558
// .....
6659
// generate the SQL query from the AST representation
67-
const { query, parameters } = subzero.fmt_main_query(subzeroRequest, queryEnv)
60+
const { query, parameters } = subzero.fmtMainQuery(subzeroRequest, queryEnv)
6861
// .....
6962
// execute the query
70-
const r = await db.query(query, parameters)
63+
result = (await db.query(query, parameters)).rows[0]
64+
// .....
65+
// send the response back to the client
66+
res.send(result.body)
7167
```
7268
69+
### Credits
70+
71+
- This dataset was sourced from [northwind-SQLite3](https://github.com/jpwhite3/northwind-SQLite3)
72+
- Inspired by [Cloudflare D1 Demo](https://northwind.d1sql.com/)
73+
+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
declare module '*.sql' {
22
const content: string;
33
export default content;
4+
}
5+
6+
declare module '*.md' {
7+
const content: string;
8+
export default content;
49
}

vercel-postgresql-neon/docker-compose.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ services:
1111
- POSTGRES_DB=app
1212

1313
volumes:
14-
- "./northwindtraders.sql:/docker-entrypoint-initdb.d/northwindtraders.sql"
14+
- "./northwindtraders-postgres.sql:/docker-entrypoint-initdb.d/northwindtraders-postgres.sql"

vercel-postgresql-neon/next.config.js

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ const nextConfig = {
33
reactStrictMode: true,
44
swcMinify: true,
55
output: 'standalone',
6+
webpack: (config) => {
7+
config.module.rules.push({
8+
test: /\.md$/,
9+
use: 'raw-loader'
10+
})
11+
return config
12+
}
613
}
714

815
module.exports = nextConfig

vercel-postgresql-neon/package.json

+8-5
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@
88
"start": "next start"
99
},
1010
"dependencies": {
11-
"@headlessui/react": "^1.7.3",
12-
"@heroicons/react": "^2.0.12",
13-
"@types/yargs": "^17.0.13",
11+
"pg": "^8.8.0",
1412
"itty-router": "^2.6.6",
1513
"next": "12.3.1",
16-
"pg": "^8.8.0",
1714
"react": "18.2.0",
1815
"react-dom": "18.2.0",
16+
"react-markdown": "^8.0.3",
1917
"subzerocloud": "^0.1.3",
2018
"swr": "^1.3.0"
2119
},
2220
"devDependencies": {
21+
"@headlessui/react": "^1.7.3",
22+
"@heroicons/react": "^2.0.12",
23+
"@tailwindcss/typography": "^0.5.7",
24+
"@types/yargs": "^17.0.13",
2325
"@tailwindcss/forms": "^0.5.3",
2426
"@types/node": "18.11.0",
2527
"@types/pg": "^8.6.5",
@@ -28,6 +30,7 @@
2830
"autoprefixer": "^10.4.12",
2931
"postcss": "^8.4.18",
3032
"tailwindcss": "^3.1.8",
31-
"typescript": "4.8.4"
33+
"typescript": "4.8.4",
34+
"raw-loader": "^4.0.2"
3235
}
3336
}

vercel-postgresql-neon/pages/api/[...path].ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,7 @@ router.all('/:table', async (req: NextApiRequest, res: NextApiResponse) => {
123123
try {
124124
const txMode = method === 'GET' ? 'READ ONLY' : 'READ WRITE'
125125
db.query(`BEGIN ISOLATION LEVEL READ COMMITTED ${txMode}`)
126-
const r = await db.query(query, parameters)
127-
result = r.rows[0]
126+
result = (await db.query(query, parameters)).rows[0]
128127
db.query('COMMIT')
129128
} catch (e) {
130129
throw e

vercel-postgresql-neon/pages/customers.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { NextPage } from 'next'
22
import {useState} from 'react'
3-
import useSWR from 'swr'
43
import Table from '../components/table'
5-
import { fetcher } from '../utils/utils'
4+
import { getCustomers } from '../utils/api'
65

76
const limit = 20
87
const title = 'Customers'
@@ -19,8 +18,7 @@ const columns = [
1918
const Customers: NextPage = () => {
2019
const [page, setPage] = useState(1)
2120
const offset = (page - 1) * limit
22-
const options = {headers: {'Prefer': 'count=exact'}}
23-
const { data, error } = useSWR<any>([`/api/Customers?limit=${limit}&offset=${offset}`, options], fetcher)
21+
const { data, error } = getCustomers(offset, limit)
2422
const { first, last, total, rows } = data || {}
2523

2624
return (

vercel-postgresql-neon/pages/dash.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { NextPage } from 'next'
22
import {useState} from 'react'
3-
import useSWR from 'swr'
43
import Table from '../components/table'
5-
import { fetcher } from '../utils/utils'
4+
import { getStats } from '../utils/api'
65

76
const limit = 5
87
const title = 'Dashboard'
@@ -21,8 +20,7 @@ const columns = [
2120
const Dashboard: NextPage = () => {
2221
const [page, setPage] = useState(1)
2322
const offset = (page - 1) * limit
24-
const randomInt = Math.floor(Math.random() * 1000)
25-
const { data, error } = useSWR<any>(`/api/stats`, fetcher)
23+
const { data, error } = getStats()
2624
const { rows:all_rows } = data || {}
2725
const total = all_rows?.length || 0
2826
const first = offset

vercel-postgresql-neon/pages/employees.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { NextPage } from 'next'
22
import {useState} from 'react'
3-
import useSWR from 'swr'
43
import Table from '../components/table'
5-
import { fetcher } from '../utils/utils'
4+
import { getEmployees } from '../utils/api'
65

76
const limit = 20
87
const title = 'Employees'
@@ -18,8 +17,7 @@ const columns = [
1817
const Employees: NextPage = () => {
1918
const [page, setPage] = useState(1)
2019
const offset = (page - 1) * limit
21-
const options = {headers: {'Prefer': 'count=exact'}}
22-
const { data, error } = useSWR<any>([`/api/Employees?select=Name:$concat(FirstName, LastName),Title,Address,City,Country&limit=${limit}&offset=${offset}`, options], fetcher)
20+
const { data, error } = getEmployees(offset, limit)
2321
const { first, last, total, rows } = data || {}
2422

2523
return (

vercel-postgresql-neon/pages/index.tsx

+7-22
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,14 @@
1-
/* eslint-disable @next/next/no-img-element */
21
import type { NextPage } from 'next'
2+
import ReactMarkdown from 'react-markdown'
3+
import readme from '../README.md'
34
const Home: NextPage = () => {
45
return (
56
<div className="px-8 mt-5">
6-
<div>
7-
<h1 className="text-xl font-semibold text-gray-900">Welcome to Northwind Traders (by subZero)</h1>
8-
</div>
9-
<div className="mx-auto py-4 text-base card-content">
10-
11-
<h2 className='text-xl text-gray-400 mb-5'>Running on Varcel and Neon</h2>
12-
<img alt='' className='float-right object-scale-down w-96' src="dbimage.webp" />
13-
<p className='pt-4'>This is a demo of the Northwind dataset</p>
14-
<p className='pt-4'>You can see the <a className='link' href='https://github.com/subzerocloud/showcase/tree/main/vercel-postgresql-neon'>source code</a> and additional implementation details on Github</p>
15-
16-
<ul>
17-
<li className='pt-4'>Frontend is implemented in NextJS</li>
18-
<li className='pt-4'>Backend is implemented in Typescript and leverages subZero as a library to automatically expose a PostgREST compatible backend on top of the underlying database</li>
19-
<li className='pt-4'>Data is stored in a PostgreSql database and hosted on <a href="https://neon.tech/" className='link'>Neon</a> </li>
20-
<li className='pt-4'>Everything is deployed to <a href="https://vercel.com/" className='link'>Vercel</a></li>
21-
</ul>
22-
<p className='pt-4'>This dataset was sourced from <a className='link' href="https://github.com/jpwhite3/northwind-SQLite3">northwind-SQLite3</a>.</p>
23-
24-
<p className='pt-4'>You can use the UI to explore Supplies, Orders, Customers, Employees and Products, or you can use search if you know what you&apos;re looking for.</p>
25-
26-
</div>
7+
<article className="prose prose-slate">
8+
<ReactMarkdown
9+
children={readme}
10+
/>
11+
</article>
2712
</div>
2813
);
2914
}

vercel-postgresql-neon/pages/orders.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { NextPage } from 'next'
22
import {useState} from 'react'
3-
import useSWR from 'swr'
43
import Table from '../components/table'
5-
import { fetcher } from '../utils/utils'
4+
import { getOrders } from '../utils/api'
65

76
const limit = 20
87
const title = 'Orders'
@@ -21,8 +20,7 @@ const columns = [
2120
const Orders: NextPage = () => {
2221
const [page, setPage] = useState(1)
2322
const offset = (page - 1) * limit
24-
const options = {headers: {'Prefer': 'count=exact'}}
25-
const { data, error } = useSWR<any>([`/api/Orders?select=OrderID,ShippedDate:ShippedDate::date,ShipName,ShipCity,ShipCountry,details:Order Details(ProductID,UnitPrice,Quantity)&limit=${limit}&offset=${offset}`, options], fetcher)
23+
const { data, error } = getOrders(offset, limit)
2624
const { first, last, total, rows } = data || {}
2725

2826
return (

vercel-postgresql-neon/pages/products.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { NextPage } from 'next'
22
import {useState} from 'react'
3-
import useSWR from 'swr'
43
import Table from '../components/table'
5-
import { fetcher } from '../utils/utils'
4+
import { getProducts } from '../utils/api'
65

76
const limit = 20
87
const title = 'Products'
@@ -18,8 +17,7 @@ const columns = [
1817
const Products: NextPage = () => {
1918
const [page, setPage] = useState(1)
2019
const offset = (page - 1) * limit
21-
const options = {headers: {'Prefer': 'count=exact'}}
22-
const { data, error } = useSWR<any>([`/api/Products?limit=${limit}&offset=${offset}`, options], fetcher)
20+
const { data, error } = getProducts(offset, limit)
2321
const { first, last, total, rows } = data || {}
2422

2523
return (

vercel-postgresql-neon/pages/suppliers.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { NextPage } from 'next'
22
import {useState} from 'react'
3-
import useSWR from 'swr'
43
import Table from '../components/table'
5-
import { fetcher } from '../utils/utils'
4+
import { getSuppliers } from '../utils/api'
65

76
const limit = 20
87
const title = 'Suppliers'
@@ -19,8 +18,7 @@ const columns = [
1918
const Suppliers: NextPage = () => {
2019
const [page, setPage] = useState(1)
2120
const offset = (page - 1) * limit
22-
const options = {headers: {'Prefer': 'count=exact'}}
23-
const { data, error } = useSWR<any>([`/api/Suppliers?limit=${limit}&offset=${offset}`, options], fetcher)
21+
const { data, error } = getSuppliers(offset, limit)
2422
const { first, last, total, rows } = data || {}
2523

2624
return (

vercel-postgresql-neon/relations.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// While the introspection query can detect most relations automaticlly based on foreign keys,
22
// in situations where they are not detected (ex: views in sqlite).
33
// Custom relations can be defined here
4+
45
export default [
56
// {
67
// "constraint_name": "tasks_project_id_fkey",

vercel-postgresql-neon/tailwind.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = {
99
],
1010
plugins: [
1111
require('@tailwindcss/forms'),
12+
require('@tailwindcss/typography'),
1213
],
1314
theme: {},
1415
};

vercel-postgresql-neon/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@
1717
"sourceMap": true
1818
},
1919
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
20-
"exclude": ["node_modules"]
20+
"exclude": ["node_modules", "functions/**/*"]
2121
}

vercel-postgresql-neon/utils/api.ts

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { parseRangeHeader } from 'subzerocloud'
2+
import useSWR from 'swr'
3+
interface FetcherResult {
4+
status: number
5+
first: number
6+
last: number
7+
total: number
8+
rows: any
9+
}
10+
const apiBaseUrl = '/api'
11+
const common_options = { headers: { 'Prefer': 'count=exact' } };
12+
13+
export async function fetcher(url: string, options?: any): Promise<FetcherResult> {
14+
return await fetch(url, options).then(async (res) => {
15+
const status = res.status
16+
const rows:any = await res.json()
17+
18+
if (status >= 400) {
19+
throw new Error(rows.message)
20+
}
21+
const { first, last, total } = parseRangeHeader(res.headers.get('Content-Range') || '')
22+
return { status, first, last, total, rows }
23+
})
24+
}
25+
26+
export function getSuppliers(offset: number, limit: number) {
27+
return useSWR<any>([`${apiBaseUrl}/Suppliers?limit=${limit}&offset=${offset}`, common_options], fetcher)
28+
}
29+
30+
export function getProducts(offset: number, limit: number) {
31+
return useSWR<any>([`${apiBaseUrl}/Products?limit=${limit}&offset=${offset}`, common_options], fetcher)
32+
}
33+
34+
export function getOrders(offset: number, limit: number) {
35+
const options = { headers: { 'Prefer': 'count=exact' } }
36+
return useSWR<any>([`${apiBaseUrl}/Orders?select=OrderID,ShippedDate:ShippedDate::date,ShipName,ShipCity,ShipCountry,details:Order Details(ProductID,UnitPrice,Quantity)&limit=${limit}&offset=${offset}`, common_options], fetcher)
37+
}
38+
39+
export function getEmployees(offset: number, limit: number) {
40+
return useSWR<any>([`${apiBaseUrl}/Employees?select=Name:$concat(FirstName, LastName),Title,Address,City,Country&limit=${limit}&offset=${offset}`, common_options], fetcher)
41+
}
42+
43+
export function getStats() {
44+
return useSWR<any>(`${apiBaseUrl}/stats`, fetcher)
45+
}
46+
47+
export function getCustomers(offset: number, limit: number) {
48+
return useSWR<any>([`${apiBaseUrl}/Customers?limit=${limit}&offset=${offset}`, common_options], fetcher)
49+
}

0 commit comments

Comments
 (0)