Skip to content

Commit e1a02cb

Browse files
authored
Initialize app (#78)
* Clone georgwittberger/next-app-router-template * Add favicon * Add pg instructions * update db explanation * Update license * Add catalan translations * Add save-exact * Add prettier * Format all files * Install @typescript-eslint/eslint-plugin * Change db schema to use places instead of todos * Use mysql instead of postgresql * Show current db in header * Enable planetscale push * Add foreign keys relation * Add foreign keys relation
1 parent eb2e354 commit e1a02cb

Some content is hidden

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

66 files changed

+5910
-671
lines changed

.eslintrc.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": [
3+
"eslint:recommended",
4+
"plugin:@typescript-eslint/recommended",
5+
"next/core-web-vitals",
6+
"prettier"
7+
],
8+
"parser": "@typescript-eslint/parser",
9+
"plugins": ["@typescript-eslint"]
10+
}

.gitignore

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
27+
# local env files
28+
.env*.local
29+
30+
# vercel
31+
.vercel
32+
33+
# typescript
34+
*.tsbuildinfo
35+
next-env.d.ts

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
save-exact=true

.vscode/launch.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"type": "chrome",
9+
"request": "launch",
10+
"name": "Launch Chrome against localhost:3000",
11+
"url": "http://localhost:3000",
12+
"webRoot": "${workspaceFolder}"
13+
}
14+
]
15+
}

LICENSE

Lines changed: 4 additions & 671 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Next.js App Router Template
2+
3+
Fullstack [Next.js](https://nextjs.org/) project template using App Router.
4+
5+
## Features
6+
7+
- Based on new [Next.js](https://nextjs.org/) App Router and [Server Components](https://nextjs.org/docs/getting-started/react-essentials)
8+
- Internationalization (i18n) using [next-intl](https://next-intl-docs.vercel.app/) including support for Server Components
9+
- Type-safe keys for translation messages in React Components
10+
- Client-side data fetching with end-to-end type-safety using [tRPC](https://trpc.io/) + [superjson](https://github.com/blitz-js/superjson) + [React Query](https://tanstack.com/query)
11+
- Client-side form validation using [React Hook Form](https://www.react-hook-form.com/) + [Zod](https://zod.dev/) schemas
12+
- Data persistence using [Drizzle ORM](https://orm.drizzle.team/) including database migrations
13+
- Authentication using [NextAuth.js](https://next-auth.js.org/) with database session storage using [Drizzle ORM adapter](https://authjs.dev/reference/adapter/drizzle)
14+
- Personalized server-side rendering (using NextAuth.js session in Server Components)
15+
- Type-safe environment variables with validation using Zod (inspired by [Create T3 App](https://create.t3.gg/))
16+
17+
## Getting Started
18+
19+
### Prepare Database
20+
21+
The database is required for persistence of app data and NextAuth.js sessions.
22+
23+
1. Install Docker.
24+
2. Run `pnpm db:run` to run the database
25+
26+
### Prepare GitHub OAuth Client
27+
28+
The OAuth client is required to sign in with GitHub account. You can swap providers in `src/server/auth.ts` if you want to use different identity providers.
29+
30+
1. Go to <https://github.com/settings/apps>.
31+
2. Create a GitHub App.
32+
- Homepage URL: <http://localhost:3000>
33+
- Callback URL: <http://localhost:3000/api/auth/callback/github>
34+
- Account permissions - Email addresses: Read only
35+
3. Save the client ID and client secret for next step.
36+
37+
### Create Local Environment
38+
39+
1. Create a file `.env.local` in the project directory with the following variables.
40+
41+
```bash
42+
# Client ID of GitHub app
43+
GITHUB_CLIENT_ID=tobechanged
44+
# Client secret of GitHub app
45+
GITHUB_CLIENT_SECRET=tobechanged
46+
# Absolute base URL of Next.js app
47+
NEXTAUTH_URL=http://localhost:3000
48+
# NextAuth.js secret, e.g. generate with "openssl rand -hex 32"
49+
NEXTAUTH_SECRET=tobechanged
50+
51+
USE_LOCAL_DB=false
52+
# Planetscale DB connection settings
53+
DATABASE_HOST=tobechanged
54+
DATABASE_USERNAME=tobechanged
55+
DATABASE_PASSWORD=tobechanged
56+
```
57+
58+
2. Install [pnpm](https://pnpm.io/) package manager.
59+
3. Run dependencies installation in the project directory.
60+
61+
```bash
62+
pnpm install
63+
```
64+
65+
4. Run database migrations in the project directory.
66+
67+
```bash
68+
pnpm db:migrate
69+
```
70+
71+
5. Run development server in the project directory.
72+
73+
```bash
74+
pnpm dev
75+
```
76+
77+
Open <http://localhost:3000> with your browser.
78+
79+
### Create Production Server
80+
81+
1. Run production build in the project directory.
82+
83+
```bash
84+
pnpm build
85+
```
86+
87+
2. Run production server in the project directory.
88+
89+
```bash
90+
pnpm start
91+
```
92+
93+
## Project Structure
94+
95+
### General Structure
96+
97+
- `.vscode`: Project-specific configuration for Visual Studio Code. Contains a launch configuration for debugging in Chrome.
98+
- `drizzle`: Drizzle ORM migrations directory. Should only be modified using [Drizzle Kit](https://orm.drizzle.team/kit-docs/overview) commands.
99+
- `public`: Public assets like favicons to include in Next.js app.
100+
- `src/app`: Next.js app directory containing routes. See below for more details.
101+
- `src/components`: Contains React components shared by the whole Next.js app.
102+
- `src/messages`: Contains next-intl translation files for the supported locales.
103+
- `src/schemas`: Contains Zod schemas shared by tRPC procedures and client-side form validation.
104+
- `src/server`: Contains server-side implementation like tRPC router, Drizzle database client and NextAuth.js configuration. See below for more details.
105+
- `src/auth.ts`: Authentication-specific helpers shared by the whole Next.js app (client-side and server-side).
106+
- `src/env.mjs`: Provides type-safe access to environment variables. Variables must be declared here with appropriate Zod schema.
107+
- `src/globals.css`: Global stylesheet loaded by root layouts.
108+
- `src/i18n.ts`: Internationalization-specific configuration and helpers shared by the whole Next.js app (client-side and server-side).
109+
- `src/messages.d.ts`: Type declaration to enable type-safe keys for next-intl functions like `useTranslations`. Infers type from `src/messages/en.json`.
110+
- `src/middleware.ts`: Next.js middleware executed for every server request except API routes and static ressources. See below for more details.
111+
- `src/next-auth.d.ts`: Type declaration to augment NextAuth.js session interface to reflect user data provided by `session` callback in auth options.
112+
- `src/trpc.ts`: tRPC client based on React Query. Used for type-safe calls to tRPC procedures from React client components.
113+
114+
### App Directory Structure
115+
116+
- `src/app/[locale]`: Localized base route for all pages of the app.
117+
- `src/app/[locale]/_components`: Contains React components shared by all pages or layouts, e.g. page layout component with common React context providers.
118+
- `src/app/[locale]/_providers`: Contains React context providers shared by all pages or layouts.
119+
- `src/app/[locale]/(auth)`: Route group for authentication-related pages, e.g. sign-in page.
120+
- `src/app/[locale]/(auth)/layout.tsx`: Root layout component for authentication-related pages. Demonstrates how to apply different root layout to some pages.
121+
- `src/app/[locale]/(auth)/auth/signin`: Contains sign-in page component and its related React components.
122+
- `src/app/[locale]/(default)`: Route group for all other pages.
123+
- `src/app/[locale]/(default)/layout.tsx`: Root layout component for all other pages.
124+
- `src/app/[locale]/(default)/page.tsx`: Home page component.
125+
- `src/app/[locale]/(default)/todos`: Contains todos page component and its related React components and hooks.
126+
- `src/app/api`: Base route for all API endpoints of the app.
127+
- `src/app/api/auth/[...nextauth]/route.ts`: Route handler for NextAuth.js to process authentication.
128+
- `src/app/api/trpc/[trpc]/route.ts`: Route handler for tRPC to process calls to tRPC procedures.
129+
130+
### Server Directory Structure
131+
132+
- `src/server/api`: Contains tRPC routers implementing the tRPC procedures.
133+
- `src/server/api/router.ts`: Root API router merging all partial routers to one tRPC router.
134+
- `src/server/api/todos/router.ts`: Partial tRPC router for todos procedures.
135+
- `src/server/db`: Contains Drizzle ORM database client, schema definition and migration script.
136+
- `src/server/db/db.ts`: Drizzle ORM database client used to perform database operations in the app.
137+
- `src/server/db/migrate.ts`: Node.js script to run database migrations.
138+
- `src/server/db/schema.ts`: Drizzle ORM database schema defining tables and its relations.
139+
- `src/server/auth-i18n-middleware.ts`: Partial middleware function to handle i18n on sign-in page. See below for more details.
140+
- `src/server/auth.ts`: NextAuth.js configuration defining identity providers, database adapter and more settings.
141+
- `src/server/i18n-middleware.ts`: next-intl middleware with i18n configuration.
142+
- `src/server/i18n.ts`: next-intl server request config providing messages for React Server Components.
143+
- `src/server/trpc.ts`: tRPC server configuration with NextAuth.js session integration for protected procedures.
144+
145+
### Middleware
146+
147+
[Next.js middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) is required for two i18n-related features in this project.
148+
149+
1. Since NextAuth.js has no concept for multi-language support on its pages yet invocations of `signIn()` will send users to the exact same route `/ca/auth/signin` (configured in NextAuth.js settings) no matter which locale they are currently using. Users coming from a locale other than `ca` would switch to Catalan on the sign-in page. The middleware handles this scenario by checking if the incoming request matches the sign-in page and contains a callback URL with a locale. If so, it extracts the locale from the callback URL (which is the original locale of the user) and redirects to the sign-in page using this original locale. Thus, users coming from the locale `en` will eventually land on `/en/auth/signin` and see the English version of the sign-in page.
150+
2. Next.js has dropped built-in support for internationalized routing with the app router. Instead, developers are supposed to [build their own solution](https://nextjs.org/docs/app/building-your-application/routing/internationalization) which involves creating middleware logic to handle redirects to the user's preferred language. Luckily, next-intl provides a middleware implementation to handle everything for us.
151+
152+
## License
153+
154+
[MIT](https://opensource.org/licenses/MIT)

drizzle.config.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Config } from 'drizzle-kit'
2+
import dotenv from 'dotenv'
3+
import path from 'node:path'
4+
5+
dotenv.config({ path: path.resolve(process.cwd(), '.env.local') })
6+
dotenv.config({ path: path.resolve(process.cwd(), '.env') })
7+
8+
if (process.env.USE_LOCAL_DB !== 'true') {
9+
if (
10+
!process.env.DATABASE_HOST ||
11+
!process.env.DATABASE_USERNAME ||
12+
!process.env.DATABASE_PASSWORD ||
13+
!process.env.DATABASE_NAME
14+
) {
15+
throw new Error('Missing environment variables for database')
16+
}
17+
}
18+
export default {
19+
schema: './src/server/db/schema/*',
20+
out: './drizzle',
21+
driver: 'mysql2',
22+
dbCredentials:
23+
process.env.USE_LOCAL_DB === 'true'
24+
? {
25+
host: '127.0.0.1',
26+
user: 'root',
27+
password: 'unsafePaswordOnlyForLocalhost',
28+
database: 'descobreix-begur-app',
29+
}
30+
: {
31+
connectionString: `mysql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}/${process.env.DATABASE_NAME}?ssl={"rejectUnauthorized":true}`,
32+
},
33+
} satisfies Config

drizzle/0000_nebulous_mauler.sql

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
CREATE TABLE `place` (
2+
`id` serial AUTO_INCREMENT NOT NULL,
3+
`title` text NOT NULL,
4+
`createdAt` timestamp NOT NULL DEFAULT (now()),
5+
CONSTRAINT `place_id` PRIMARY KEY(`id`)
6+
);
7+
--> statement-breakpoint
8+
CREATE TABLE `account` (
9+
`userId` varchar(255) NOT NULL,
10+
`type` varchar(255) NOT NULL,
11+
`provider` varchar(255) NOT NULL,
12+
`providerAccountId` varchar(255) NOT NULL,
13+
`refresh_token` varchar(255),
14+
`access_token` varchar(255),
15+
`expires_at` int,
16+
`token_type` varchar(255),
17+
`scope` varchar(255),
18+
`id_token` varchar(255),
19+
`session_state` varchar(255),
20+
CONSTRAINT `account_provider_providerAccountId` PRIMARY KEY(`provider`,`providerAccountId`)
21+
);
22+
--> statement-breakpoint
23+
CREATE TABLE `session` (
24+
`sessionToken` varchar(255) NOT NULL,
25+
`userId` varchar(255) NOT NULL,
26+
`expires` timestamp NOT NULL,
27+
CONSTRAINT `session_sessionToken` PRIMARY KEY(`sessionToken`)
28+
);
29+
--> statement-breakpoint
30+
CREATE TABLE `user` (
31+
`id` varchar(255) NOT NULL,
32+
`name` varchar(255),
33+
`email` varchar(255) NOT NULL,
34+
`emailVerified` timestamp(3) DEFAULT (now()),
35+
`image` varchar(255),
36+
CONSTRAINT `user_id` PRIMARY KEY(`id`)
37+
);
38+
--> statement-breakpoint
39+
CREATE TABLE `verificationToken` (
40+
`identifier` varchar(255) NOT NULL,
41+
`token` varchar(255) NOT NULL,
42+
`expires` timestamp NOT NULL,
43+
CONSTRAINT `verificationToken_identifier_token` PRIMARY KEY(`identifier`,`token`)
44+
);

0 commit comments

Comments
 (0)