├── src
├── containers
│ └── Homepage.tsx
└── pages
│ ├── Index.tsx
│ ├── Homepage.tsx
│ ├── api
│ ├── people.ts
│ ├── vehicles.ts
│ ├── vehicle
│ │ └── [id].ts
│ └── person
│ │ └── [id]
│ │ ├── vehicles.ts
│ │ └── index.ts
│ ├── List.tsx
│ └── [vehicle]
│ └── [person].tsx
├── next-env.d.ts
├── mydb.sqlite
├── public
└── favicon.ico
├── api
└── VehiclePerson.ts
├── .gitignore
├── README.md
├── database-test.js
├── tsconfig.json
├── package.json
├── migrations
└── 001-helloworld.sql
└── db.json
/src/containers/Homepage.tsx:
--------------------------------------------------------------------------------
1 | export function HomePage(){
2 | return
Hello
3 | }
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/mydb.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bmvantunes/youtube-2020-march-nextjs-part4/HEAD/mydb.sqlite
--------------------------------------------------------------------------------
/src/pages/Index.tsx:
--------------------------------------------------------------------------------
1 | import { HomePage } from '../containers/Homepage';
2 |
3 | export default HomePage;
--------------------------------------------------------------------------------
/src/pages/Homepage.tsx:
--------------------------------------------------------------------------------
1 | import { HomePage } from '../containers/Homepage';
2 |
3 | export default HomePage;
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bmvantunes/youtube-2020-march-nextjs-part4/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/api/VehiclePerson.ts:
--------------------------------------------------------------------------------
1 | export interface VehiclePerson {
2 | details: string;
3 | ownerName: string;
4 | vehicle: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/pages/api/people.ts:
--------------------------------------------------------------------------------
1 | import { NextApiRequest, NextApiResponse } from 'next';
2 | import sqlite from 'sqlite';
3 |
4 | export default async function getPeople(req: NextApiRequest, res: NextApiResponse) {
5 | const db = await sqlite.open('./mydb.sqlite');
6 | const people = await db.all('select * from person');
7 |
8 | res.json(people);
9 | }
--------------------------------------------------------------------------------
/src/pages/api/vehicles.ts:
--------------------------------------------------------------------------------
1 | import { NextApiRequest, NextApiResponse } from 'next';
2 | import sqlite from 'sqlite';
3 |
4 | export default async function getAllVehicles(req: NextApiRequest, res: NextApiResponse) {
5 | const db = await sqlite.open('./mydb.sqlite');
6 | const vehicle = await db.all('select * from vehicle');
7 |
8 | res.json(vehicle);
9 | }
--------------------------------------------------------------------------------
/src/pages/api/vehicle/[id].ts:
--------------------------------------------------------------------------------
1 | import { NextApiRequest, NextApiResponse } from 'next';
2 | import sqlite from 'sqlite';
3 |
4 | export default async function getVehicleById(req: NextApiRequest, res: NextApiResponse) {
5 | const db = await sqlite.open('./mydb.sqlite');
6 | const vehicle = await db.get('select * from vehicle where id = ?', [req.query.id]);
7 | res.json(vehicle);
8 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | .env*
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
--------------------------------------------------------------------------------
/src/pages/api/person/[id]/vehicles.ts:
--------------------------------------------------------------------------------
1 | import { NextApiRequest, NextApiResponse } from 'next';
2 | import sqlite from 'sqlite';
3 |
4 | export default async function getAllVehiclesByPersonId(req: NextApiRequest, res: NextApiResponse) {
5 | const db = await sqlite.open('./mydb.sqlite');
6 | const allVehicles = await db.all('select * from vehicle where ownerId = ?', [req.query.id]);
7 | res.json(allVehicles);
8 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # youtube-2020-march-nextjs-part4
2 | Next.js API Routes with SQL Database
3 |
4 | Continuation of [part 3](https://github.com/bmvantunes/youtube-2020-march-nextjs-part3)
5 |
6 | Slides available at: https://docs.google.com/presentation/d/1izjQBjUYiF5PCSdDuHcsEAMFYkc--_BTLW-iCby70oI/edit?usp=sharing
7 |
8 |
9 | Youtube Video: https://youtu.be/PxiQDo0CmDE
10 |
11 | Let me know if you can access the slides!
12 |
--------------------------------------------------------------------------------
/database-test.js:
--------------------------------------------------------------------------------
1 | const sqlite = require('sqlite');
2 |
3 | async function setup() {
4 | const db = await sqlite.open('./mydb.sqlite');
5 | await db.migrate({force: 'last'});
6 |
7 | const people = await db.all('SELECT * FROM person');
8 | console.log('ALL PEOPLE', JSON.stringify(people, null, 2));
9 |
10 | const vehicles = await db.all('SELECT * FROM vehicle');
11 | console.log('ALL VEHICLES', JSON.stringify(vehicles, null, 2));
12 | }
13 |
14 | setup();
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": false,
10 | "skipLibCheck": true,
11 | "strict": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "noEmit": true,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "jsx": "preserve"
20 | },
21 | "exclude": [
22 | "node_modules"
23 | ],
24 | "include": [
25 | "next-env.d.ts",
26 | "**/*.ts",
27 | "**/*.tsx"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "youtube-2020-march-nextjs-part4",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "node ./database-test.js && next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "mock-server": "json-server --watch ./db.json -p 4001 -d 1500"
10 | },
11 | "dependencies": {
12 | "isomorphic-unfetch": "3.0.0",
13 | "next": "9.2.1",
14 | "react": "16.12.0",
15 | "react-dom": "16.12.0",
16 | "sqlite": "3.0.3",
17 | "swr": "0.1.17"
18 | },
19 | "devDependencies": {
20 | "@types/node": "13.7.7",
21 | "@types/react": "16.9.23",
22 | "json-server": "0.16.1",
23 | "typescript": "3.8.3"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/pages/api/person/[id]/index.ts:
--------------------------------------------------------------------------------
1 | import { NextApiRequest, NextApiResponse } from 'next';
2 | import sqlite from 'sqlite';
3 |
4 | export default async function getPersonById(
5 | req: NextApiRequest,
6 | res: NextApiResponse
7 | ) {
8 | const db = await sqlite.open('./mydb.sqlite');
9 |
10 | if (req.method === 'PUT') {
11 | const statement = await db.prepare(
12 | 'UPDATE person SET name= ?, email = ? where id = ?'
13 | );
14 | const result = await statement.run(
15 | req.body.name,
16 | req.body.email,
17 | req.query.id
18 | );
19 | result.finalize();
20 | }
21 |
22 | const person = await db.get('select * from person where id = ?', [
23 | req.query.id
24 | ]);
25 | res.json(person);
26 | }
27 |
--------------------------------------------------------------------------------
/migrations/001-helloworld.sql:
--------------------------------------------------------------------------------
1 | -- Up
2 | CREATE TABLE Person (
3 | id INTEGER PRIMARY KEY AUTOINCREMENT,
4 | name TEXT,
5 | email TEXT
6 | );
7 |
8 | CREATE TABLE Vehicle (
9 | id INTEGER PRIMARY KEY AUTOINCREMENT,
10 | brand TEXT,
11 | model TEXT,
12 | ownerId INTEGER REFERENCES Person(id)
13 | );
14 |
15 | INSERT INTO Person (name, email) values ('bruno', 'bruno@antunes.pt');
16 | INSERT INTO Person (name, email) values ('jack', 'jack@antunes.pt');
17 |
18 | INSERT INTO Vehicle (brand, model, ownerId) values('audi', 'R8', 1);
19 | INSERT INTO Vehicle (brand, model, ownerId) values('audi', 'R6', 1);
20 | INSERT INTO Vehicle (brand, model, ownerId) values('mercedes', 'benz', 2);
21 |
22 | -- Down
23 | DROP TABLE Person;
24 | DROP TABLE Vehicle;
--------------------------------------------------------------------------------
/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "vehicles": [
3 | {
4 | "vehicle": "Car",
5 | "ownerName": "Bruno Antunes",
6 | "details": "some detail about Bruno's Car"
7 | },
8 | {
9 | "vehicle": "Bike",
10 | "ownerName": "Bruno Antunes",
11 | "details": "some detail about Bruno's Bike"
12 | },
13 | {
14 | "vehicle": "Bike",
15 | "ownerName": "John Doe",
16 | "details": "some detail bile"
17 | },
18 | {
19 | "vehicle": "Airplane",
20 | "ownerName": "Bill Gates",
21 | "details": "some detail Bill Gates"
22 | } ,
23 | {
24 | "vehicle": "SpaceX",
25 | "ownerName": "Elon Musk",
26 | "details": "some detail Elon"
27 | }
28 | ]
29 | }
--------------------------------------------------------------------------------
/src/pages/List.tsx:
--------------------------------------------------------------------------------
1 | import fetch from 'isomorphic-unfetch';
2 | import Link from 'next/link';
3 | import { VehiclePerson } from '../../api/VehiclePerson';
4 | import MyComp from './bruno';
5 |
6 | export interface ListProps {
7 | ownersList?: VehiclePerson[];
8 | }
9 |
10 | export default function List({ownersList}: ListProps) {
11 | return (
12 |
13 | {ownersList?.map((e, index) => (
14 |
22 | ))}
23 |
24 | );
25 | }
26 |
27 | List.getInitialProps = async () => {
28 | const response = await fetch('http://localhost:4001/vehicles');
29 | const ownersList: VehiclePerson[] | undefined = await response.json();
30 | return {ownersList: ownersList}
31 | }
--------------------------------------------------------------------------------
/src/pages/[vehicle]/[person].tsx:
--------------------------------------------------------------------------------
1 | import fetch from 'isomorphic-unfetch';
2 | import { NextPageContext } from 'next';
3 | import { useRouter } from 'next/router';
4 | import { useEffect, useState } from 'react';
5 | import { VehiclePerson } from '../../../api/VehiclePerson';
6 |
7 | export interface PersonProps {
8 | ownersList?: VehiclePerson[];
9 | }
10 |
11 | export default function Person({ ownersList }: PersonProps) {
12 | const router = useRouter();
13 |
14 | const [owners, setOwners] = useState(ownersList);
15 | useEffect(() => {
16 | async function loadData() {
17 | const response = await fetch(
18 | 'http://localhost:4001/vehicles?ownerName=' +
19 | router.query.person +
20 | '&vehicle=' +
21 | router.query.vehicle
22 | );
23 | const ownersList: VehiclePerson[] | undefined = await response.json();
24 | setOwners(ownersList);
25 | }
26 |
27 | if (ownersList?.length == 0) {
28 | loadData();
29 | }
30 | }, []);
31 |
32 | if (!owners?.[0]) {
33 | return loading...
;
34 | }
35 |
36 | return {owners[0]?.details};
37 | }
38 |
39 | Person.getInitialProps = async ({query, req}: NextPageContext) => {
40 | if (!req) {
41 | return { ownersList: [] };
42 | }
43 |
44 | const response = await fetch(
45 | 'http://localhost:4001/vehicles?ownerName=' +
46 | query.person +
47 | '&vehicle=' +
48 | query.vehicle
49 | );
50 | const ownersList: VehiclePerson[] | undefined = await response.json();
51 | return { ownersList: ownersList };
52 | };
53 |
--------------------------------------------------------------------------------