Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to Pothos #156

Merged
merged 1 commit into from
Jul 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
DATABASE_URL="postgresql://"
GITHUB_ID=""
GITHUB_SECRET=""
NEXTAUTH_URL="http://localhost:3000/api/auth"
JWT_SECRET=""
DATABASE_URL=postgresql://
GITHUB_ID=
GITHUB_SECRET=
NEXTAUTH_URL=http://localhost:3000/api/auth
NEXTAUTH_SECRET=
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.graphql
1 change: 0 additions & 1 deletion .graphqlrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ generates:
src/lib/graphql/generated/:
preset: near-operation-file
presetConfig:
folder: generated
baseTypesPath: types.ts
plugins:
- typescript-operations
Expand Down
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx pretty-quick --staged
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
save-exact=true
6 changes: 3 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
"silent": false
},
{
"match": "./src/lib/nexus/*",
"cmd": "yarn generate:nexus"
"match": "./src/lib/pothos/*",
"cmd": "yarn schema:generate"
},
{
"match": "./src/lib/graphql/*",
"cmd": "yarn generate:hooks"
"cmd": "yarn hooks:generate"
}
]
}
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Nextjs-auth-prisma boilerplate
# Next.js-auth-prisma boilerplate

Build bleeding-edge full-stack applications using **Next.js**, **GraphQL**, **TypeScript** and **Prisma**.

Expand Down Expand Up @@ -27,28 +27,29 @@ I have created this boilerplate because there was a missing one with all feature
- 🛡 [NextAuth.js](https://github.com/nextauthjs/next-auth) - Authentication for Next.js
- 🦅 [urql](https://github.com/FormidableLabs/urql) - Highly customisable GraphQL client with sensitive defaults
- ⚙️ [GraphQL Code Generator](https://github.com/dotansimha/graphql-code-generator) - Generates code out of GraphQL schema
- 🧬 [GraphQL Helix](https://github.com/contrawork/graphql-helix) - Flexible utility functions for building GraphQL servers
- 💄 [Prettier](https://github.com/prettier/prettier) - Formating your code
- 🧘‍♀️ [GraphQL Yoga](https://github.com/dotansimha/graphql-yoga/) - Fully-featured GraphQL Server
- 💄 [Prettier](https://github.com/prettier/prettier) - Formatting your code
- 🤖 [Dependabot](https://github.com/marketplace/dependabot-preview) - Keeping your dependencies up to date

### Run Prisma migrations on save

This boilerplate works out of the box with automatic migrations for rapid prototyping. I described this in my article [Improve prototyping speed of Prisma](https://huvik.dev/blog/improve-prototyping-speed-of-prisma), you can check how it works under the hood.

![](https://i.imgur.com/clz6RjW.gif)
![](https://i.imgur.com/kF73swy.gif)

### Automatic GraphQL hooks generation

Hooks for GraphQL are automatically generated inside `src/lib/grahql/*` from your GraphQL files. You can customize hooks generation inside `.graphqlrc.yaml`.
![](https://i.imgur.com/gFGF2fB.gif)

![](https://i.imgur.com/xNwz7AA.gif)

### Authentication using NextAuth.js

This boilerplate is configured to use [GitHub](https://next-auth.js.org/providers/github) authentication provider. [NextAuth.js](https://github.com/nextauthjs/next-auth) comes with a lot of different [providers](https://next-auth.js.org/configuration/providers). You can choose, which providers suit your needs most.

### Defining custom authorization rules

You can define authorization rules for your resolvers. For example [isAdmin](https://github.com/huv1k/nextjs-auth-prisma/blob/master/src/lib/nexus/rules.ts) rule for listing [all users](https://github.com/huv1k/nextjs-auth-prisma/blob/master/src/lib/nexus/types/user.ts#L24).
You can define authorization rules for your resolvers. You can follow [Pothos's auth plugin](https://pothos-graphql.dev/docs/plugins/scope-auth) documentation or checkout [example](https://github.com/huv1k/nextjs-auth-prisma/blob/master/src/lib/pothos/builder.ts) in this repository.

### Deployment

Expand All @@ -58,7 +59,7 @@ For deployment, you can use [Vercel](https://vercel.com/), this boilerplate work

### Connect your database

You can follow [Prisma getting started](https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch-typescript-postgres#connect-your-database), which requires to have running PostgreSQL database running. If you don't want to use a local docker setup, I suggest using [Railway.app](https://railway.app/), which has a nice generous free plan for PostgreSQL databases.
This starter could be used with all databases supported by Prisma. I would suggest using [PlanetScale](https://planetscale.com/), which has a nice generous free plan. You can follow [Prisma getting started](https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/connect-your-database-typescript-planetscale) to get your database up and running.

### NextAuth GitHub provider

Expand Down
9 changes: 9 additions & 0 deletions next-auth.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { UserRole, User } from '@prisma/client'
import { JWT, DefaultJWT } from 'next-auth/jwt'

declare module 'next-auth/jwt' {
interface JWT extends DefaultJWT {
role: UserRole
id: User['id']
}
}
5 changes: 4 additions & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
11 changes: 11 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @ts-check

/**
* @type {import('next').NextConfig}
**/
module.exports = {
reactStrictMode: true,
images: {
domains: ['avatars.githubusercontent.com'],
},
}
81 changes: 39 additions & 42 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,58 +1,55 @@
{
"name": "next-auth-prisma-template",
"version": "0.0.1",
"name": "nextjs-auth-prisma-template",
"version": "1.0.0",
"license": "MIT",
"author": {
"name": "Lukáš Huvar",
"email": "lukas@huvar.cz",
"url": "https://huvik.dev/"
},
"scripts": {
"build": "yarn generate:nexus && next build",
"build": "yarn schema:generate && yarn hooks:generate && next build",
"dev": "next",
"start": "next start",
"hooks:generate": "graphql-codegen --config .graphqlrc.yaml",
"lint": "next lint",
"postinstall": "yarn prisma generate && yarn generate:nexus",
"generate:hooks": "graphql-codegen --config .graphqlrc.yaml",
"generate:nexus": "ts-node --skip-project --transpile-only src/lib/nexus/schema --nexus-exit"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
"postinstall": "yarn prisma generate",
"schema:generate": "tsx src/lib/utils/build-schema.ts",
"start": "next start",
"prepare": "husky install"
},
"dependencies": {
"@next-auth/prisma-adapter": "^0.4.4-canary.64",
"@prisma/client": "2.24",
"graphql": "^15.5.0",
"graphql-helix": "1.6.1",
"next": "^11.0.0",
"next-auth": "^3.27.0",
"next-urql": "3.0.0",
"nexus": "1.0.0",
"nexus-prisma": "^0.28.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-is": "17.0.2",
"urql": "2.0.2"
"@graphql-yoga/node": "2.13.3",
"@next-auth/prisma-adapter": "1.0.3",
"@pothos/core": "3.13.0",
"@pothos/plugin-prisma": "3.14.0",
"@pothos/plugin-scope-auth": "3.11.0",
"@prisma/client": "4.0.0",
"graphql": "16.5.0",
"next": "12.2.3",
"next-auth": "4.10.0",
"next-urql": "3.3.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-is": "18.2.0",
"urql": "2.2.2"
},
"devDependencies": {
"@graphql-codegen/cli": "1.21.4",
"@graphql-codegen/near-operation-file-preset": "^1.18.0",
"@graphql-codegen/typed-document-node": "1.18.8",
"@graphql-codegen/typescript": "1.21.0",
"@graphql-codegen/typescript-operations": "1.17.15",
"@graphql-codegen/typescript-urql": "2.0.3",
"@graphql-typed-document-node/core": "3.1.0",
"@types/next-auth": "3.1.26",
"eslint": "^7.28.0",
"eslint-config-next": "^11.0.0",
"graphql-playground-html": "^1.6.29",
"husky": "4.3.8",
"prettier": "2.2.1",
"pretty-quick": "3.1.0",
"prisma": "2.24",
"ts-node": "9.1.1",
"typescript": "^4.3.4"
"@graphql-codegen/cli": "^2.11.3",
"@graphql-codegen/near-operation-file-preset": "2.4.0",
"@graphql-codegen/typed-document-node": "2.3.2",
"@graphql-codegen/typescript": "2.7.2",
"@graphql-codegen/typescript-operations": "2.5.2",
"@graphql-codegen/typescript-urql": "3.6.3",
"@graphql-typed-document-node/core": "3.1.1",
"@types/node": "18.6.3",
"@types/react": "18.0.15",
"eslint": "8.20.0",
"eslint-config-next": "12.2.2",
"husky": "^8.0.0",
"prettier": "2.7.1",
"pretty-quick": "3.1.3",
"prisma": "4.0.0",
"tsx": "3.8.0",
"typescript": "4.7.4"
}
}
59 changes: 29 additions & 30 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
// learn more about it in the docs: https://pris.ly/d/prisma-schema

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
provider = "mysql"
url = env("DATABASE_URL")
referentialIntegrity = "prisma"
}

generator client {
provider = "prisma-client-js"
provider = "prisma-client-js"
previewFeatures = ["referentialIntegrity"]
}

generator nexusPrisma {
provider = "nexus-prisma"
generator pothos {
provider = "prisma-pothos-types"
}

enum Role {
Expand All @@ -21,50 +23,47 @@ enum Role {
}

model Account {
id String @id @default(uuid())
userId String
providerType String
providerId String
providerAccountId String
refreshToken String?
accessToken String?
accessTokenExpires DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?

@@unique([providerId, providerAccountId])
user User @relation(fields: [userId], references: [id], onDelete: Cascade)

@@unique([provider, providerAccountId])
}

model Session {
id String @id @default(uuid())
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
sessionToken String @unique
accessToken String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
id String @id @default(uuid())
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
accounts Account[]
sessions Session[]
role Role @default(USER)
}

model VerificationRequest {
id String @id @default(uuid())
model VerificationToken {
identifier String
token String @unique
expires DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@unique([identifier, token])
}
13 changes: 10 additions & 3 deletions src/components/viewer.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import Image from 'next/image'
import { signOut } from 'next-auth/client'
import { UserDetailFragment } from '../lib/graphql/generated/viewer.generated'
import { signOut } from 'next-auth/react'
import { UserDetailFragment } from '../lib/graphql/viewer.generated'

interface Props {
viewer: UserDetailFragment
}

export const Viewer = ({ viewer }: Props) => (
<div>
<Image width="150px" height="150px" src={viewer.image} alt={viewer.name} />
{viewer.image && (
<Image
width="150px"
height="150px"
src={viewer.image}
alt={viewer.name ?? 'User without a name'}
/>
)}
<h2>{viewer.name}</h2>
<button onClick={() => signOut()}>Sign out</button>
</div>
Expand Down
24 changes: 1 addition & 23 deletions src/lib/graphql/generated/schema.graphql
Original file line number Diff line number Diff line change
@@ -1,30 +1,8 @@
### This file was generated by Nexus Schema
### Do not make changes to this file directly

"""
A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.
"""
scalar DateTime

"""
The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
"""
scalar Json
@specifiedBy(
url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf"
)

type Query {
users: [User]
users: [User!]!
viewer: User
}

enum Role {
ADMIN
SUPERADMIN
USER
}

type User {
id: ID!
image: String
Expand Down
Loading