😗Next.js Tutorial Typescript, sqlite create, update Full (ok)
Part 1 pages\List.tsx, pages[vehicle][person].tsx
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252FJg1NNKbATxGBVrI6EzH5%252Fimage.png%3Falt%3Dmedia%26token%3D47cc58c8-e1f2-4b65-8487-ecd9dc16ce1e&width=768&dpr=4&quality=100&sign=59014a69&sv=2)
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./pages/*.{ts,tsx}",
"./pages/**/*.{ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
styles\globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
pages\List.tsx
import Link from 'next/link';
const people = [
{ v: 'car', name: 'bruno' },
{ v: 'bike', name: 'John' },
{ v: 'airplane', name: 'Mick' }
]
export default function List() {
return (
<div className="container">
{people.map(e => (
<Link className='block' key={e.v} as={`/${e.v}/${e.name}`} href="/[vehicle]/[person]">
Navigate to {e.name}'s {e.v}
</Link>
))}
</div>
)
}
pages\[vehicle]\[person].tsx
import { useRouter } from 'next/router';
export default function Person() {
const router = useRouter();
console.log(router.query);
return <h2>{router.query.person}'s {router.query.vehicle}</h2>
}
Part 2 use api
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252F8d4BKJryPnr92JJArQ5g%252Fimage.png%3Falt%3Dmedia%26token%3D98e3d379-2ad4-4f60-8cfe-b98d6a8c6531&width=768&dpr=4&quality=100&sign=77ac600d&sv=2)
pages\list.tsx
— Cách 1: Dùng với useEffect
import React, { useState, useEffect } from 'react';
import Link from 'next/link';
export default function List({ ownersList }: any) {
const [owners, setOwners] = useState([]);
useEffect(() => {
async function loadData() {
const response = await fetch('http://localhost:3000/api/vehicles');
const ownersList = await response.json();
setOwners(ownersList);
}
loadData();
}, []);
return (
<div className='container'>
{owners.map((e: any, index: number) => (
<Link className='block' key={index} as={`/${e.vehicle}/${e.ownerName}`} href="/[vehicle]/[person]">
Navigate to {e.ownerName}'s {e.vehicle}
</Link>
))}
</div>
);
}
— Cách 2: Dùng với cách của Next.js là sử dụng getInitialProps
pages\api\vehicles.ts
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
import data from "../../data/db.json"
export default async function handler(req: NextApiRequest,res: NextApiResponse<any>) {
res.status(200).json(data)
}
data\db.json
[
{
"vehicle": "Car",
"ownerName": "Bruno Antunes",
"details": "some detail about Bruno's Car"
},
{
"vehicle": "Bike",
"ownerName": "Bruno Antunes",
"details": "some detail about Bruno's Bike"
},
{
"vehicle": "Bike",
"ownerName": "John Doe",
"details": "some detail bile"
},
{
"vehicle": "Airplane",
"ownerName": "Bill Gates",
"details": "some detail Bill Gates"
},
{
"vehicle": "SpaceX",
"ownerName": "Elon Musk",
"details": "some detail Elon"
}
]
pages[vehicle][person].tsx
import { useRouter } from 'next/router';
import { NextPageContext } from 'next';
import {useState, useEffect } from 'react';
export interface PersonProps {
ownersList?: any;
}
export default function Person({ ownersList }: PersonProps) {
const [owners, setOwners] = useState(ownersList);
const router = useRouter();
useEffect(() => {
async function loadData() {
const response = await fetch('http://localhost:3000/api/vehicles?ownerName=' + router.query.person + '&vehicle=' + router.query.vehicle);
const ownersList: any[] | undefined = await response.json();
setOwners(ownersList);
}
if (ownersList?.length === 0) {
loadData();
}
}, [])
return <pre>{owners[0]?.details}</pre>;
}
Person.getInitialProps = async ({query, req}: NextPageContext) => {
console.log(query);
if (!req) {
return { ownersList: [] };
}
const response = await fetch('http://localhost:3000/api/vehicles?ownerName=' + query.person + '&vehicle=' + query.vehicle);
const ownersList: any[] | undefined = await response.json();
return { ownersList };
}
Part 3 API Routes using SQL Database
pages\data\mydb.sqlite
pages\api\vehicles.ts
import { NextApiRequest, NextApiResponse } from 'next';
const sqlite = require('sqlite');
const sqlite3 = require('sqlite3');
async function openDb() {
return sqlite.open({
filename: './pages/data/mydb.sqlite',
driver: sqlite3.Database,
});
}
export default async function getAllVehicles(req: NextApiRequest, res: NextApiResponse) {
const db = await openDb();
const vehicle = await db.all('SELECT * FROM Vehicle');
res.json(vehicle);
}
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252FvCiTXrBmkbPctCwjFuf3%252Fimage.png%3Falt%3Dmedia%26token%3D2f3044cf-7a55-4f9b-a5bc-d6c5f00ed2e7&width=768&dpr=4&quality=100&sign=3b721003&sv=2)
pages\api\person\index.ts
import { NextApiRequest, NextApiResponse } from 'next';
const sqlite = require('sqlite');
const sqlite3 = require('sqlite3');
async function openDb() {
return sqlite.open({
filename: './pages/data/mydb.sqlite',
driver: sqlite3.Database,
});
}
export default async function getAllPerson(req: NextApiRequest, res: NextApiResponse) {
const db = await openDb();
const person = await db.all('SELECT * FROM Person');
res.json(person);
}
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252FOJKmZILI4xcsBuoTLE8f%252Fimage.png%3Falt%3Dmedia%26token%3D67517ea4-2465-426d-85e3-9d1f2d138da0&width=768&dpr=4&quality=100&sign=f8ae9484&sv=2)
Part 4 signup && login && update
pages\api\signup.ts
import { NextApiRequest, NextApiResponse } from 'next';
const bcrypt = require('bcrypt');
const sqlite = require('sqlite');
const sqlite3 = require('sqlite3');
async function openDb() {
return sqlite.open({
filename: './pages/data/mydb-pass.sqlite',
driver: sqlite3.Database,
});
}
export default async function signup( req: NextApiRequest, res: NextApiResponse ) {
const db = await openDb();
if (req.method === 'POST') {
bcrypt.hash(req.body.password, 10, async function(err:any, hash:any) {
// Store hash in your password DB.
const statement = await db.prepare( 'INSERT INTO person (name, email, password) values (?, ?, ?)');
const result = await statement.run(req.body.name, req.body.email, hash);
const person = await db.all('select * from person');
res.json(person);
});
} else {
res.status(405).json({ message: 'We only support POST' });
}
}
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252FQwLfdT13Mo9PsRU3MP2v%252Fimage.png%3Falt%3Dmedia%26token%3D16217d10-da91-4afc-a524-382e32069f77&width=768&dpr=4&quality=100&sign=f18b9037&sv=2)
pages\api\secret.ts
export const secret = '0e900be1-0ac5-4e6a-bf4b-38f8b21a189b';
pages\api\login.ts
import { compare, hash } from 'bcrypt';
import { NextApiRequest, NextApiResponse } from 'next';
import { secret } from '../api/secret';
import { sign } from 'jsonwebtoken';
const sqlite = require('sqlite');
const sqlite3 = require('sqlite3');
async function openDb() {
return sqlite.open({
filename: './pages/data/mydb-pass.sqlite',
driver: sqlite3.Database,
});
}
export default async function login(req: NextApiRequest, res: NextApiResponse) {
const db = await openDb();
if (req.method === 'POST') {
const person = await db.get('select * from person where email = ?', [req.body.email]);
compare(req.body.password, person?.password, function(err, result) {
if (!err && result) {
const claims = { sub: person.id, myPersonEmail: person.email };
const jwt = sign(claims, secret, { expiresIn: '1h' });
res.json({ authToken: jwt });
} else {
res.json({ message: 'Ups, something went wrong!' });
}
});
}else {
res.status(405).json({ message: 'We only support POST' });
}
}
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252F2bggmz1l1R33aYCLALoo%252Fimage.png%3Falt%3Dmedia%26token%3D81817fa7-3cf4-430f-88a5-48a2ffd2a5d4&width=768&dpr=4&quality=100&sign=26444acb&sv=2)
pages\api\person[id]\index.ts
import { NextApiRequest, NextApiResponse } from 'next';
const sqlite = require('sqlite');
const sqlite3 = require('sqlite3');
async function openDb() {
return sqlite.open({
filename: './pages/data/mydb.sqlite',
driver: sqlite3.Database,
});
};
export default async function getPersonById(req: NextApiRequest, res: NextApiResponse) {
const db = await openDb();
if (req.method === 'PUT') {
const statement = await db.prepare('UPDATE person SET name= ?, email = ? where id = ?');
await statement.run(req.body.name,req.body.email,req.query.id);
}
const person = await db.get('select * from person where id = ?', req.query.id);
res.json(person);
}
Part 5 authenticated
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252FakRGPwrzhj1WHDYqzcYR%252Fimage.png%3Falt%3Dmedia%26token%3D0d471c5d-6bc8-4cc0-b1f0-f955b84be9df&width=768&dpr=4&quality=100&sign=894979e8&sv=2)
pages\api\login.ts
import { compare, hash } from 'bcrypt';
import { NextApiRequest, NextApiResponse } from 'next';
import { secret } from '../api/secret';
import { sign } from 'jsonwebtoken';
const sqlite = require('sqlite');
const sqlite3 = require('sqlite3');
import * as cookie from 'cookie';
require('dotenv').config()
async function openDb() {
return sqlite.open({
filename: './pages/data/mydb-pass.sqlite',
driver: sqlite3.Database,
});
}
export default async function login(req: NextApiRequest, res: NextApiResponse) {
const db = await openDb();
if (req.method === 'POST') {
const person = await db.get('select * from person where email = ?', [req.body.email]);
compare(req.body.password, person?.password, function(err, result) {
if (!err && result) {
const claims = { sub: person.id, myPersonEmail: person.email };
const jwt = sign(claims, secret, { expiresIn: '1h' });
res.setHeader('Set-Cookie', cookie.serialize('auth', jwt, {
httpOnly: true,
secure: process.env.TEST !== 'development',
sameSite: 'strict',
maxAge: 3600,
path: '/'
}));
res.json({message: 'Welcome back to the app!'});
} else {
res.json({ message: 'Ups, something went wrong!' });
}
});
}else {
res.status(405).json({ message: 'We only support POST' });
}
}
.env
TEST=production
pages\api\people.ts
import { verify } from 'jsonwebtoken';
import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
const sqlite = require('sqlite');
const sqlite3 = require('sqlite3');
import { secret } from '../api/secret';
async function openDb() {
return sqlite.open({
filename: './pages/data/mydb-pass.sqlite',
driver: sqlite3.Database,
});
}
export const authenticated = (fn: NextApiHandler) => async (req: NextApiRequest, res: NextApiResponse) => {
verify(req.cookies.auth!, secret, async function(err, decoded) {
if (!err && decoded) {
return await fn(req, res);
}
res.status(401).json({ message: 'Sorry you are not authenticated' });
});
}
export default authenticated(async function getPeople(req: NextApiRequest,res: NextApiResponse) {
const db = await openDb();
const people = await db.all('select id, email, name from person');
res.json(people);
});
Part 6 getStaticPaths cung cấp dữ liệu khớp với GetStaticProps
pages\microphone[id].tsx
import { GetStaticPaths, GetStaticProps } from 'next';
import { useRouter } from 'next/router';
import { Microphone } from '../model/Microphone';
import openDB from '../../pages/openDB';
export default function MicrophoneDetail({ id, brand, model, price, imageUrl }: Microphone) {
const router = useRouter();
if (router.isFallback) {
return <div>Loading......I'm sorry for the wait!!</div>;
}
return (
<div className="p-3 w-screen">
<div className='text-blue-600/100'>{id}</div>
<div className='text-blue-600/75'>{brand}</div>
<div className='text-blue-600/50'>{model}</div>
<div className='text-blue-600/25'>{price}</div>
<div>{imageUrl}</div>
</div>
);
}
export const getStaticProps: GetStaticProps<Microphone> = async (ctx: any) => {
const id = ctx.params.id as string;
const db = await openDB();
const microphone = await db.get('select * from microphone where id = ?', + id);
return { props: microphone };
};
export const getStaticPaths: GetStaticPaths<{ id: string }> = async () => {
const db = await openDB();
const microphones = await db.all('select * from microphone');
const paths = microphones.map((a) => {
return {
params: {
id: a.id.toString()
}
};
});
return {
paths,
fallback: false
};
};
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252F72hWK8ecHk5tglcp6NAH%252Fimage.png%3Falt%3Dmedia%26token%3Decd53293-d289-41cc-8929-bb0a3fc44855&width=768&dpr=4&quality=100&sign=b554d878&sv=2)
Part 7 getStaticProps and getStaticPaths currentPage hay nói cách khác làm phân trang
pages\index.tsx
import type { GetStaticProps, NextPage } from 'next';
import openDB from '../pages/openDB';
import {Microphone} from './model/Microphone';
export interface IndexProps {
microphones: Microphone[]
}
import styles from '../styles/Home.module.css'
import Link from 'next/link';
const Home: any = ({microphones}: IndexProps) => {
return (
<div className={styles.container}>
<main className={styles.main}>
{
microphones?.map((microphone) => {
return (
<Link className='block' href="/microphone/[id]" as={`/microphone/${microphone.id}`} key={microphone.id}>
{microphone.brand + " " + microphone.model + " " + microphone.price}
</Link>
)
})
}
</main>
</div>
)
}
export default Home
export const getStaticProps: GetStaticProps = async (ctx) => {
const currentPage = ctx.params?.currentPage as string;
const currentPageNumber = +(currentPage || 0);
const min = currentPageNumber * 5;
const max = (currentPageNumber + 1) * 5;
const db = await openDB();
const microphones = await db.all(
'select * from microphone where id > ? and id <= ?',min,max
);
return { props: { microphones } };
};
pages\[currentPage].tsx
import { GetStaticPaths } from 'next';
import openDB from '../pages/openDB';
import Index, { getStaticProps } from './';
export default Index;
export { getStaticProps };
export const getStaticPaths: GetStaticPaths = async () => {
const db = await openDB();
const { total } = await db.get('select count(*) as total from microphone');
const numberOfPages = Math.ceil(total / 5.0);
const paths = Array(numberOfPages - 1).fill('').map((_, index) => {
return {
params: {
currentPage: (index + 1).toString()
}
}
});
return {
fallback: false,
paths: paths,
};
}
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252FqfRnGxeowShtkyZmGh6Q%252Fimage.png%3Falt%3Dmedia%26token%3D9a45b489-87db-4762-bd34-75a98b8c6298&width=768&dpr=4&quality=100&sign=14638771&sv=2)
![](https://learnreact.gitbook.io/~gitbook/image?url=https%3A%2F%2F589574163-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MGMzApU_kEyI7nsYtZs%252Fuploads%252FeWsWKnqtqhbjIb8sbzRZ%252Fimage.png%3Falt%3Dmedia%26token%3D5823ff2f-9b6c-46b5-a551-9109def19885&width=768&dpr=4&quality=100&sign=2348c6c8&sv=2)
Part 8 getServerSideProps bề bản chất nó có những điểm giống GetStaticProps là biến đổi dữ liệu thành props co component sử dụng nhưng điểm khác biệt rõ nhất là một cái là biến thành dữ liệu tĩnh (GetStaticProps) khi cần thay đổi ta phải render lại 😒
pages\index.tsx
import type { GetServerSideProps} from 'next';
import openDB from '../pages/openDB';
import {Microphone} from './model/Microphone';
export interface IndexProps {
microphones: Microphone[]
}
import styles from '../styles/Home.module.css'
import Link from 'next/link';
const Home: any = ({microphones}: IndexProps) => {
return (
<div className={styles.container}>
<main className={styles.main}>
{
microphones?.map((microphone) => {
return (
<Link className='block' href="/microphone/[id]" as={`/microphone/${microphone.id}`} key={microphone.id}>
{microphone.brand + " " + microphone.model + " " + microphone.price}
</Link>
)
})
}
</main>
</div>
)
}
export default Home
export const getServerSideProps: GetServerSideProps<IndexProps> = async (ctx:any) => {
const db = await openDB();
const microphones = await db.all<Microphone[]>('select * from microphone');
return { props: { microphones } };
};
PreviousSự liên quan giữa jwt & base64decode (ok)Next========== Start Next.js Building a Car ============
Last updated
Was this helpful?