├── 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 |
15 | 16 | 17 | Navigate to {e.ownerName}'s {e.vehicle} 18 | 19 | 20 | 21 |
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 | --------------------------------------------------------------------------------