├── frontend
├── src
│ ├── assets
│ │ └── .keep
│ ├── pages
│ │ └── App
│ │ │ ├── App.css
│ │ │ ├── index.ts
│ │ │ ├── App.test.tsx
│ │ │ └── App.tsx
│ ├── react-app-env.d.ts
│ ├── components
│ │ └── UserList
│ │ │ ├── User
│ │ │ ├── index.ts
│ │ │ └── User.tsx
│ │ │ ├── index.ts
│ │ │ ├── UserListContainer.tsx
│ │ │ └── UserList.tsx
│ ├── graphql
│ │ ├── fragments
│ │ │ └── user.ts
│ │ ├── query
│ │ │ └── user.ts
│ │ └── mutation
│ │ │ └── user.ts
│ ├── index.css
│ ├── index.tsx
│ ├── types.ts
│ └── serviceWorker.ts
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── manifest.json
│ └── index.html
├── .gitignore
├── tsconfig.json
└── package.json
├── .vscode
└── settings.json
├── backend
├── .gitignore
├── src
│ ├── utils.ts
│ ├── resolvers
│ │ ├── query
│ │ │ ├── index.ts
│ │ │ └── userQuery.ts
│ │ ├── mutation
│ │ │ ├── index.ts
│ │ │ └── userMutation.ts
│ │ └── index.ts
│ ├── index.ts
│ ├── schema.graphql
│ └── generated
│ │ ├── types.ts
│ │ └── prisma-client
│ │ ├── prisma-schema.ts
│ │ └── index.ts
├── prisma
│ ├── datamodel.prisma
│ ├── seed.graphql
│ ├── prisma.yml
│ └── docker-compose.yml
├── codegen.yml
├── tsconfig.json
└── package.json
├── LICENSE
└── README.md
/frontend/src/assets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/pages/App/App.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true
3 | }
--------------------------------------------------------------------------------
/frontend/src/pages/App/index.ts:
--------------------------------------------------------------------------------
1 | import App from "./App"
2 | export default App;
--------------------------------------------------------------------------------
/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | .env*
2 | dist
3 | package-lock.json
4 | node_modules
5 | .idea
6 | .vscode
7 | *.log
--------------------------------------------------------------------------------
/frontend/src/components/UserList/User/index.ts:
--------------------------------------------------------------------------------
1 | import User from "./User";
2 | export default User;
3 |
--------------------------------------------------------------------------------
/backend/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { Prisma } from './generated/prisma-client'
2 |
3 | export interface Context {
4 | prisma: Prisma
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adenh93/react-graphql-typescript-boilerplate/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/src/components/UserList/index.ts:
--------------------------------------------------------------------------------
1 | import UserListContainer from "./UserListContainer";
2 | export default UserListContainer;
3 |
--------------------------------------------------------------------------------
/backend/src/resolvers/query/index.ts:
--------------------------------------------------------------------------------
1 | import userQuery from "./userQuery";
2 |
3 | const Query = {
4 | ...userQuery
5 | };
6 |
7 | export default Query;
8 |
--------------------------------------------------------------------------------
/backend/src/resolvers/mutation/index.ts:
--------------------------------------------------------------------------------
1 | import userMutation from "./userMutation";
2 |
3 | const Mutation = {
4 | ...userMutation
5 | };
6 |
7 | export default Mutation;
8 |
--------------------------------------------------------------------------------
/backend/src/resolvers/index.ts:
--------------------------------------------------------------------------------
1 | import Query from "./query";
2 | import Mutation from "./mutation";
3 |
4 | const resolvers = {
5 | Query,
6 | Mutation
7 | };
8 |
9 | export default resolvers;
10 |
--------------------------------------------------------------------------------
/backend/prisma/datamodel.prisma:
--------------------------------------------------------------------------------
1 | type User {
2 | id: ID! @id
3 | username: String! @unique
4 | email: String! @unique
5 | name: String!
6 | createdAt: DateTime! @createdAt
7 | updatedAt: DateTime! @updatedAt
8 | }
--------------------------------------------------------------------------------
/frontend/src/graphql/fragments/user.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 |
3 | export const userFields = gql`
4 | fragment userFields on User {
5 | id
6 | username
7 | email
8 | name
9 | }
10 | `;
11 |
--------------------------------------------------------------------------------
/backend/codegen.yml:
--------------------------------------------------------------------------------
1 | overwrite: true
2 | schema: "src/schema.graphql"
3 | generates:
4 | src/generated/types.ts:
5 | plugins:
6 | - typescript
7 | ../frontend/src/types.ts:
8 | plugins:
9 | - typescript
10 |
--------------------------------------------------------------------------------
/backend/prisma/seed.graphql:
--------------------------------------------------------------------------------
1 | mutation {
2 | user1: createPost(
3 | data: {
4 | email: "joe.smith@prisma.com"
5 | username: "joe.smith"
6 | name: "Joe Smith"
7 | }
8 | ) {
9 | id
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/backend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "moduleResolution": "node",
5 | "module": "commonjs",
6 | "sourceMap": true,
7 | "rootDir": "src",
8 | "outDir": "dist",
9 | "lib": [
10 | "esnext", "dom"
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/src/pages/App/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/backend/prisma/prisma.yml:
--------------------------------------------------------------------------------
1 | endpoint: http://localhost:4466/
2 | datamodel: datamodel.prisma
3 | databaseType: document
4 |
5 | generate:
6 | - generator: typescript-client
7 | output: ../src/generated/prisma-client/
8 |
9 | seed:
10 | import: seed.graphql
11 |
12 | hooks:
13 | post-deploy:
14 | - prisma generate
15 |
--------------------------------------------------------------------------------
/frontend/src/pages/App/App.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./App.css";
3 | import UserList from "../../components/UserList";
4 |
5 | const App: React.FC = () => {
6 | return (
7 |
8 |
Hello React!
9 |
10 |
11 | );
12 | };
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/backend/src/index.ts:
--------------------------------------------------------------------------------
1 | import { GraphQLServer } from "graphql-yoga";
2 | import { prisma } from "./generated/prisma-client";
3 | import resolvers from "./resolvers";
4 |
5 | const server = new GraphQLServer({
6 | typeDefs: "./src/schema.graphql",
7 | resolvers,
8 | context: { prisma }
9 | });
10 |
11 | server.start(() => console.log("Server is running on http://localhost:4000"));
12 |
--------------------------------------------------------------------------------
/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Hello React!",
3 | "name": "Hello React!",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/src/components/UserList/User/User.tsx:
--------------------------------------------------------------------------------
1 | import React, { SFC } from "react";
2 |
3 | interface Props {
4 | name: string;
5 | username: string;
6 | email: string;
7 | }
8 |
9 | const User: SFC = ({ name, username, email }) => (
10 |
11 |
{name}
12 |
{username}
13 |
{email}
14 |
15 | );
16 |
17 | export default User;
18 |
--------------------------------------------------------------------------------
/backend/src/resolvers/query/userQuery.ts:
--------------------------------------------------------------------------------
1 | import { Context } from "../../utils";
2 | import { QueryUserArgs } from "../../generated/types";
3 |
4 | const userQuery = {
5 | users(parent, args, context: Context) {
6 | return context.prisma.users();
7 | },
8 | user(parent, { id }: QueryUserArgs, context: Context) {
9 | return context.prisma.user({ id });
10 | }
11 | };
12 |
13 | export default userQuery;
14 |
--------------------------------------------------------------------------------
/frontend/src/graphql/query/user.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 | import { userFields } from "../fragments/user";
3 |
4 | export const getUsers = gql`
5 | query {
6 | users {
7 | ...userFields
8 | }
9 | }
10 | ${userFields}
11 | `;
12 |
13 | export const getUser = gql`
14 | query($id: ID!) {
15 | user(id: $id) {
16 | ...userFields
17 | }
18 | }
19 | ${userFields}
20 | `;
21 |
--------------------------------------------------------------------------------
/frontend/.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 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/backend/src/schema.graphql:
--------------------------------------------------------------------------------
1 | type Query {
2 | user(id: ID!): User
3 | users: [User!]!
4 | }
5 |
6 | type Mutation {
7 | createUser(data: CreateUserInput): User
8 | updateUser(id: ID, data: UpdateUserInput): User
9 | deleteUser(id: ID): User
10 | }
11 |
12 | type User {
13 | id: ID!
14 | username: String!
15 | email: String!
16 | name: String!
17 | }
18 |
19 | input CreateUserInput {
20 | username: String!
21 | email: String!
22 | name: String!
23 | }
24 |
25 | input UpdateUserInput {
26 | username: String
27 | email: String
28 | name: String
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./pages/App";
5 | import * as serviceWorker from "./serviceWorker";
6 |
7 | import { ApolloProvider } from "react-apollo";
8 | import ApolloBoost from "apollo-boost";
9 |
10 | const client = new ApolloBoost({
11 | uri: "http://localhost:4000"
12 | });
13 |
14 | ReactDOM.render(
15 |
16 |
17 | ,
18 | document.getElementById("root")
19 | );
20 |
21 | serviceWorker.unregister();
22 |
--------------------------------------------------------------------------------
/frontend/src/components/UserList/UserListContainer.tsx:
--------------------------------------------------------------------------------
1 | import React, { SFC } from "react";
2 | import UserList from "./UserList";
3 | import { useQuery } from "react-apollo";
4 | import { getUsers } from "../../graphql/query/user";
5 | import { Query } from "../../types";
6 |
7 | const UserListContainer: SFC = () => {
8 | const { loading, error, data } = useQuery(getUsers);
9 |
10 | if (loading) return Loading users...
;
11 | if (error) return Error loading users...
;
12 |
13 | return ;
14 | };
15 |
16 | export default UserListContainer;
17 |
--------------------------------------------------------------------------------
/frontend/src/components/UserList/UserList.tsx:
--------------------------------------------------------------------------------
1 | import React, { SFC } from "react";
2 | import UserComponent from "./User";
3 | import { User } from "../../types";
4 |
5 | interface Props {
6 | users: User[];
7 | }
8 |
9 | const UserList: SFC = ({ users }) => (
10 |
11 | {!users.length ? (
12 |
No users to display.
13 | ) : (
14 |
15 | {users.map(({ id, ...user }) => (
16 | -
17 |
18 |
19 | ))}
20 |
21 | )}
22 |
23 | );
24 |
25 | export default UserList;
26 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "react"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/frontend/src/graphql/mutation/user.ts:
--------------------------------------------------------------------------------
1 | import { gql } from "apollo-boost";
2 | import { userFields } from "../fragments/user";
3 |
4 | export const createUser = gql`
5 | mutation($data: CreateUserInput) {
6 | createUser(data: $data) {
7 | ...userFields
8 | }
9 | }
10 | ${userFields}
11 | `;
12 |
13 | export const updateUser = gql`
14 | mutation($id: ID!, $data: UpdateUserInput) {
15 | updateUser(id: $id, data: $data) {
16 | ...userFields
17 | }
18 | }
19 | ${userFields}
20 | `;
21 |
22 | export const deleteUser = gql`
23 | mutation($id: ID!) {
24 | deleteUser(id: $id) {
25 | ...userFields
26 | }
27 | }
28 | ${userFields}
29 | `;
30 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend",
3 | "scripts": {
4 | "start": "nodemon -e json,js,ts,graphql,prisma --ignore 'src/**/*.spec.ts' --exec 'npm run codegen && ts-node' src/index.ts",
5 | "prisma-deploy": "prisma deploy -p prisma/prisma.yml",
6 | "prisma-delete": "prisma delete -p prisma/prisma.yml",
7 | "codegen": "graphql-codegen --config codegen.yml"
8 | },
9 | "dependencies": {
10 | "graphql-yoga": "1.18.3",
11 | "prisma-client-lib": "1.34.10"
12 | },
13 | "devDependencies": {
14 | "@graphql-codegen/cli": "^1.9.1",
15 | "@graphql-codegen/typescript": "^1.9.1",
16 | "nodemon": "^2.0.1",
17 | "prisma": "1.34.10",
18 | "ts-node": "7.0.1",
19 | "typescript": "3.2.2"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/backend/src/resolvers/mutation/userMutation.ts:
--------------------------------------------------------------------------------
1 | import { Context } from "../../utils";
2 | import {
3 | MutationCreateUserArgs,
4 | MutationUpdateUserArgs,
5 | MutationDeleteUserArgs
6 | } from "../../generated/types";
7 |
8 | const userMutation = {
9 | createUser(parent, { data }: MutationCreateUserArgs, { prisma }: Context) {
10 | return prisma.createUser(data);
11 | },
12 | updateUser(
13 | parent,
14 | { id, data }: MutationUpdateUserArgs,
15 | { prisma }: Context
16 | ) {
17 | return prisma.updateUser({ where: { id }, data });
18 | },
19 | deleteUser(parent, { id }: MutationDeleteUserArgs, { prisma }: Context) {
20 | return prisma.deleteUser({ id });
21 | }
22 | };
23 |
24 | export default userMutation;
25 |
--------------------------------------------------------------------------------
/backend/prisma/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | prisma:
4 | image: prismagraphql/prisma:1.34
5 | restart: always
6 | ports:
7 | - "4466:4466"
8 | environment:
9 | PRISMA_CONFIG: |
10 | port: 4466
11 | # uncomment the next line and provide the env var PRISMA_MANAGEMENT_API_SECRET=my-secret to activate cluster security
12 | # managementApiSecret: my-secret
13 | databases:
14 | default:
15 | connector: mongo
16 | uri: 'mongodb://prisma:prisma@mongo'
17 | mongo:
18 | image: mongo:3.6
19 | restart: always
20 | # Uncomment the next two lines to connect to your your database from outside the Docker environment, e.g. using a database GUI like Compass
21 | # ports:
22 | # - "27017:27017"
23 | environment:
24 | MONGO_INITDB_ROOT_USERNAME: prisma
25 | MONGO_INITDB_ROOT_PASSWORD: prisma
26 | ports:
27 | - "27017:27017"
28 | volumes:
29 | - mongo:/var/lib/mongo
30 | volumes:
31 | mongo:
32 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@types/jest": "24.0.23",
7 | "@types/node": "12.12.14",
8 | "@types/react": "16.9.13",
9 | "@types/react-dom": "16.9.4",
10 | "apollo-boost": "^0.4.4",
11 | "graphql": "^14.5.8",
12 | "react": "^16.12.0",
13 | "react-apollo": "^3.1.3",
14 | "react-dom": "^16.12.0",
15 | "react-scripts": "3.2.0",
16 | "typescript": "3.7.2"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": "react-app"
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | },
39 | "devDependencies": {}
40 | }
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aden Herold
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/frontend/src/types.ts:
--------------------------------------------------------------------------------
1 | export type Maybe = T | null;
2 | /** All built-in and custom scalars, mapped to their actual values */
3 | export type Scalars = {
4 | ID: string,
5 | String: string,
6 | Boolean: boolean,
7 | Int: number,
8 | Float: number,
9 | };
10 |
11 | export type CreateUserInput = {
12 | username: Scalars['String'],
13 | email: Scalars['String'],
14 | name: Scalars['String'],
15 | };
16 |
17 | export type Mutation = {
18 | __typename?: 'Mutation',
19 | createUser?: Maybe,
20 | updateUser?: Maybe,
21 | deleteUser?: Maybe,
22 | };
23 |
24 |
25 | export type MutationCreateUserArgs = {
26 | data?: Maybe
27 | };
28 |
29 |
30 | export type MutationUpdateUserArgs = {
31 | id?: Maybe,
32 | data?: Maybe
33 | };
34 |
35 |
36 | export type MutationDeleteUserArgs = {
37 | id?: Maybe
38 | };
39 |
40 | export type Query = {
41 | __typename?: 'Query',
42 | user?: Maybe,
43 | users: Array,
44 | };
45 |
46 |
47 | export type QueryUserArgs = {
48 | id: Scalars['ID']
49 | };
50 |
51 | export type UpdateUserInput = {
52 | username?: Maybe,
53 | email?: Maybe,
54 | name?: Maybe,
55 | };
56 |
57 | export type User = {
58 | __typename?: 'User',
59 | id: Scalars['ID'],
60 | username: Scalars['String'],
61 | email: Scalars['String'],
62 | name: Scalars['String'],
63 | };
64 |
--------------------------------------------------------------------------------
/backend/src/generated/types.ts:
--------------------------------------------------------------------------------
1 | export type Maybe = T | null;
2 | /** All built-in and custom scalars, mapped to their actual values */
3 | export type Scalars = {
4 | ID: string,
5 | String: string,
6 | Boolean: boolean,
7 | Int: number,
8 | Float: number,
9 | };
10 |
11 | export type CreateUserInput = {
12 | username: Scalars['String'],
13 | email: Scalars['String'],
14 | name: Scalars['String'],
15 | };
16 |
17 | export type Mutation = {
18 | __typename?: 'Mutation',
19 | createUser?: Maybe,
20 | updateUser?: Maybe,
21 | deleteUser?: Maybe,
22 | };
23 |
24 |
25 | export type MutationCreateUserArgs = {
26 | data?: Maybe
27 | };
28 |
29 |
30 | export type MutationUpdateUserArgs = {
31 | id?: Maybe,
32 | data?: Maybe
33 | };
34 |
35 |
36 | export type MutationDeleteUserArgs = {
37 | id?: Maybe
38 | };
39 |
40 | export type Query = {
41 | __typename?: 'Query',
42 | user?: Maybe,
43 | users: Array,
44 | };
45 |
46 |
47 | export type QueryUserArgs = {
48 | id: Scalars['ID']
49 | };
50 |
51 | export type UpdateUserInput = {
52 | username?: Maybe,
53 | email?: Maybe,
54 | name?: Maybe,
55 | };
56 |
57 | export type User = {
58 | __typename?: 'User',
59 | id: Scalars['ID'],
60 | username: Scalars['String'],
61 | email: Scalars['String'],
62 | name: Scalars['String'],
63 | };
64 |
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
23 | Hello React!
24 |
25 |
26 |
27 |
28 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-graphql-typescript-boilerplate
2 |
3 | ## **What is it**
4 |
5 | This repository is intended to be used as a boilerplate for any of my projects utilizing a React + Apollo frontend, and a Node/GraphQL backend. Both the frontend and backend projects are bootstrapped with, and take advantage of, Typescript. You are free to use this boilerplate for your own projects if you desire.
6 |
7 | ## **Prerequisites**
8 |
9 | In order to use this boilerplate, you must install `docker` and `docker-compose` on your system.
10 |
11 | ## **Technology**
12 |
13 | ### **Backend**
14 |
15 | I've configured the GraphQL backend to be scalable, to prevent query and mutation resolvers from becoming overwhelming as more and more types are added the the GraphQL schema.
16 |
17 | I haven't added any subscription resolvers in the boilerplate, but if you would like them to also be scalable, consider following the convention I've been using.
18 |
19 | In `package.json`, I've included a script named _codegen_, which utilizes `graphql-codegen` to generate Typescript types for your GraphQL Schema, and output them to `backend/src/generated/types.ts` and `frontend/src/types.ts`. This script is automatically executed before the Node server runs, but you can manually execute it via `npm run codegen`
20 |
21 | The backend is comprised of the following key technologies:
22 |
23 | - **GraphQL-Yoga**: A fully featured GraphQL server, with most useful features available right out of the box, including subscriptions, GraphQL Playground, etc. I've configured the server to support Typescript. [https://github.com/prisma-labs/graphql-yoga](https://github.com/prisma-labs/graphql-yoga)
24 |
25 | - **Prisma**: A set of database tools, including an ORM, which is utilized for the GraphQL server. In this boilerplate, I'm using the MongoDB connector, but you can feel free to use any of the other [supported connectors](https://www.prisma.io/features/databases). If you are using a different connector, then you will need to edit `prisma/docker-compose.yml`. [https://github.com/prisma/prisma](https://github.com/prisma/prisma)
26 |
27 | ### **Frontend**
28 |
29 | I've also configured the React frontend project to be scalable, using a components & pages directory structure. I've added some basic container + presentational components to demonstrate the connectivity with the GraphQL server, but feel free to remove them.
30 |
31 | The frontend is comprised of the following key technologies:
32 |
33 | - **React**: It should go without saying, but React is a powerful library for creating user interfaces. I've configured the project to support Typescript and CSS Modules, via `create-react-app`, but feel free to eject and make any configuration changes of your own. [https://github.com/facebook/react](https://github.com/facebook/react)
34 |
35 | - **Apollo-Client**: A fully featured GraphQL client to interface with the backend GraphQL server. I specifically utilize [apollo-boost](https://github.com/apollographql/apollo-client/tree/master/packages/apollo-boost), which is a quick and easy, out of the box configuration for the Apollo Client. [https://github.com/apollographql/apollo-client](https://github.com/apollographql/apollo-client)
36 |
37 | - **React-Apollo**: Provides a Higher Order Component (HOC) to provide the Apollo Client instance to all wrapped components, and allow them to be enhanced with Apollo functionalities. `react-apollo` can essentially replace redux in a way, as it can take full responsibility of fetching and caching data as well as updating the UI, which is why Redux is not included in this boilerplate. If you feel that `react-apollo` is simply not enough to manage your state, then consider the React Context API before needlessly adding Redux boilerplate. [https://github.com/apollographql/react-apollo](https://github.com/apollographql/react-apollo)
38 |
39 | ## **How to build**
40 |
41 | ### **Backend**
42 |
43 | - Install dependencies via `npm install`.
44 | - Execute `docker-compose up -d` from within `/prisma`.
45 | - Execute `npm run prisma-deploy` to deploy your Prisma schema + generate the prisma client.
46 | - Execute `npm start` to start the server via `nodemon`.
47 |
48 | ### **Frontend**
49 |
50 | - Install dependencies via `npm install`.
51 | - Execute `npm start` to start the Webpack dev server.
52 |
--------------------------------------------------------------------------------
/backend/src/generated/prisma-client/prisma-schema.ts:
--------------------------------------------------------------------------------
1 | // Code generated by Prisma (prisma@1.34.10). DO NOT EDIT.
2 | // Please don't change this file manually but run `prisma generate` to update it.
3 | // For more information, please read the docs: https://www.prisma.io/docs/prisma-client/
4 |
5 | export const typeDefs = /* GraphQL */ `type AggregateUser {
6 | count: Int!
7 | }
8 |
9 | type BatchPayload {
10 | count: Long!
11 | }
12 |
13 | scalar DateTime
14 |
15 | scalar Long
16 |
17 | type Mutation {
18 | createUser(data: UserCreateInput!): User!
19 | updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User
20 | updateManyUsers(data: UserUpdateManyMutationInput!, where: UserWhereInput): BatchPayload!
21 | upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User!
22 | deleteUser(where: UserWhereUniqueInput!): User
23 | deleteManyUsers(where: UserWhereInput): BatchPayload!
24 | }
25 |
26 | enum MutationType {
27 | CREATED
28 | UPDATED
29 | DELETED
30 | }
31 |
32 | interface Node {
33 | id: ID!
34 | }
35 |
36 | type PageInfo {
37 | hasNextPage: Boolean!
38 | hasPreviousPage: Boolean!
39 | startCursor: String
40 | endCursor: String
41 | }
42 |
43 | type Query {
44 | user(where: UserWhereUniqueInput!): User
45 | users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]!
46 | usersConnection(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): UserConnection!
47 | node(id: ID!): Node
48 | }
49 |
50 | type Subscription {
51 | user(where: UserSubscriptionWhereInput): UserSubscriptionPayload
52 | }
53 |
54 | type User {
55 | id: ID!
56 | username: String!
57 | email: String!
58 | name: String!
59 | createdAt: DateTime!
60 | updatedAt: DateTime!
61 | }
62 |
63 | type UserConnection {
64 | pageInfo: PageInfo!
65 | edges: [UserEdge]!
66 | aggregate: AggregateUser!
67 | }
68 |
69 | input UserCreateInput {
70 | id: ID
71 | username: String!
72 | email: String!
73 | name: String!
74 | }
75 |
76 | type UserEdge {
77 | node: User!
78 | cursor: String!
79 | }
80 |
81 | enum UserOrderByInput {
82 | id_ASC
83 | id_DESC
84 | username_ASC
85 | username_DESC
86 | email_ASC
87 | email_DESC
88 | name_ASC
89 | name_DESC
90 | createdAt_ASC
91 | createdAt_DESC
92 | updatedAt_ASC
93 | updatedAt_DESC
94 | }
95 |
96 | type UserPreviousValues {
97 | id: ID!
98 | username: String!
99 | email: String!
100 | name: String!
101 | createdAt: DateTime!
102 | updatedAt: DateTime!
103 | }
104 |
105 | type UserSubscriptionPayload {
106 | mutation: MutationType!
107 | node: User
108 | updatedFields: [String!]
109 | previousValues: UserPreviousValues
110 | }
111 |
112 | input UserSubscriptionWhereInput {
113 | mutation_in: [MutationType!]
114 | updatedFields_contains: String
115 | updatedFields_contains_every: [String!]
116 | updatedFields_contains_some: [String!]
117 | node: UserWhereInput
118 | AND: [UserSubscriptionWhereInput!]
119 | }
120 |
121 | input UserUpdateInput {
122 | username: String
123 | email: String
124 | name: String
125 | }
126 |
127 | input UserUpdateManyMutationInput {
128 | username: String
129 | email: String
130 | name: String
131 | }
132 |
133 | input UserWhereInput {
134 | id: ID
135 | id_not: ID
136 | id_in: [ID!]
137 | id_not_in: [ID!]
138 | id_lt: ID
139 | id_lte: ID
140 | id_gt: ID
141 | id_gte: ID
142 | id_contains: ID
143 | id_not_contains: ID
144 | id_starts_with: ID
145 | id_not_starts_with: ID
146 | id_ends_with: ID
147 | id_not_ends_with: ID
148 | username: String
149 | username_not: String
150 | username_in: [String!]
151 | username_not_in: [String!]
152 | username_lt: String
153 | username_lte: String
154 | username_gt: String
155 | username_gte: String
156 | username_contains: String
157 | username_not_contains: String
158 | username_starts_with: String
159 | username_not_starts_with: String
160 | username_ends_with: String
161 | username_not_ends_with: String
162 | email: String
163 | email_not: String
164 | email_in: [String!]
165 | email_not_in: [String!]
166 | email_lt: String
167 | email_lte: String
168 | email_gt: String
169 | email_gte: String
170 | email_contains: String
171 | email_not_contains: String
172 | email_starts_with: String
173 | email_not_starts_with: String
174 | email_ends_with: String
175 | email_not_ends_with: String
176 | name: String
177 | name_not: String
178 | name_in: [String!]
179 | name_not_in: [String!]
180 | name_lt: String
181 | name_lte: String
182 | name_gt: String
183 | name_gte: String
184 | name_contains: String
185 | name_not_contains: String
186 | name_starts_with: String
187 | name_not_starts_with: String
188 | name_ends_with: String
189 | name_not_ends_with: String
190 | createdAt: DateTime
191 | createdAt_not: DateTime
192 | createdAt_in: [DateTime!]
193 | createdAt_not_in: [DateTime!]
194 | createdAt_lt: DateTime
195 | createdAt_lte: DateTime
196 | createdAt_gt: DateTime
197 | createdAt_gte: DateTime
198 | updatedAt: DateTime
199 | updatedAt_not: DateTime
200 | updatedAt_in: [DateTime!]
201 | updatedAt_not_in: [DateTime!]
202 | updatedAt_lt: DateTime
203 | updatedAt_lte: DateTime
204 | updatedAt_gt: DateTime
205 | updatedAt_gte: DateTime
206 | AND: [UserWhereInput!]
207 | }
208 |
209 | input UserWhereUniqueInput {
210 | id: ID
211 | username: String
212 | email: String
213 | }
214 | `
--------------------------------------------------------------------------------
/frontend/src/serviceWorker.ts:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | type Config = {
24 | onSuccess?: (registration: ServiceWorkerRegistration) => void;
25 | onUpdate?: (registration: ServiceWorkerRegistration) => void;
26 | };
27 |
28 | export function register(config?: Config) {
29 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
30 | // The URL constructor is available in all browsers that support SW.
31 | const publicUrl = new URL(
32 | (process as { env: { [key: string]: string } }).env.PUBLIC_URL,
33 | window.location.href
34 | );
35 | if (publicUrl.origin !== window.location.origin) {
36 | // Our service worker won't work if PUBLIC_URL is on a different origin
37 | // from what our page is served on. This might happen if a CDN is used to
38 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
39 | return;
40 | }
41 |
42 | window.addEventListener('load', () => {
43 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
44 |
45 | if (isLocalhost) {
46 | // This is running on localhost. Let's check if a service worker still exists or not.
47 | checkValidServiceWorker(swUrl, config);
48 |
49 | // Add some additional logging to localhost, pointing developers to the
50 | // service worker/PWA documentation.
51 | navigator.serviceWorker.ready.then(() => {
52 | console.log(
53 | 'This web app is being served cache-first by a service ' +
54 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
55 | );
56 | });
57 | } else {
58 | // Is not localhost. Just register service worker
59 | registerValidSW(swUrl, config);
60 | }
61 | });
62 | }
63 | }
64 |
65 | function registerValidSW(swUrl: string, config?: Config) {
66 | navigator.serviceWorker
67 | .register(swUrl)
68 | .then(registration => {
69 | registration.onupdatefound = () => {
70 | const installingWorker = registration.installing;
71 | if (installingWorker == null) {
72 | return;
73 | }
74 | installingWorker.onstatechange = () => {
75 | if (installingWorker.state === 'installed') {
76 | if (navigator.serviceWorker.controller) {
77 | // At this point, the updated precached content has been fetched,
78 | // but the previous service worker will still serve the older
79 | // content until all client tabs are closed.
80 | console.log(
81 | 'New content is available and will be used when all ' +
82 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
83 | );
84 |
85 | // Execute callback
86 | if (config && config.onUpdate) {
87 | config.onUpdate(registration);
88 | }
89 | } else {
90 | // At this point, everything has been precached.
91 | // It's the perfect time to display a
92 | // "Content is cached for offline use." message.
93 | console.log('Content is cached for offline use.');
94 |
95 | // Execute callback
96 | if (config && config.onSuccess) {
97 | config.onSuccess(registration);
98 | }
99 | }
100 | }
101 | };
102 | };
103 | })
104 | .catch(error => {
105 | console.error('Error during service worker registration:', error);
106 | });
107 | }
108 |
109 | function checkValidServiceWorker(swUrl: string, config?: Config) {
110 | // Check if the service worker can be found. If it can't reload the page.
111 | fetch(swUrl)
112 | .then(response => {
113 | // Ensure service worker exists, and that we really are getting a JS file.
114 | const contentType = response.headers.get('content-type');
115 | if (
116 | response.status === 404 ||
117 | (contentType != null && contentType.indexOf('javascript') === -1)
118 | ) {
119 | // No service worker found. Probably a different app. Reload the page.
120 | navigator.serviceWorker.ready.then(registration => {
121 | registration.unregister().then(() => {
122 | window.location.reload();
123 | });
124 | });
125 | } else {
126 | // Service worker found. Proceed as normal.
127 | registerValidSW(swUrl, config);
128 | }
129 | })
130 | .catch(() => {
131 | console.log(
132 | 'No internet connection found. App is running in offline mode.'
133 | );
134 | });
135 | }
136 |
137 | export function unregister() {
138 | if ('serviceWorker' in navigator) {
139 | navigator.serviceWorker.ready.then(registration => {
140 | registration.unregister();
141 | });
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/backend/src/generated/prisma-client/index.ts:
--------------------------------------------------------------------------------
1 | // Code generated by Prisma (prisma@1.34.10). DO NOT EDIT.
2 | // Please don't change this file manually but run `prisma generate` to update it.
3 | // For more information, please read the docs: https://www.prisma.io/docs/prisma-client/
4 |
5 | import { DocumentNode } from "graphql";
6 | import {
7 | makePrismaClientClass,
8 | BaseClientOptions,
9 | Model
10 | } from "prisma-client-lib";
11 | import { typeDefs } from "./prisma-schema";
12 |
13 | export type AtLeastOne }> = Partial &
14 | U[keyof U];
15 |
16 | export type Maybe = T | undefined | null;
17 |
18 | export interface Exists {
19 | user: (where?: UserWhereInput) => Promise;
20 | }
21 |
22 | export interface Node {}
23 |
24 | export type FragmentableArray = Promise> & Fragmentable;
25 |
26 | export interface Fragmentable {
27 | $fragment(fragment: string | DocumentNode): Promise;
28 | }
29 |
30 | export interface Prisma {
31 | $exists: Exists;
32 | $graphql: (
33 | query: string,
34 | variables?: { [key: string]: any }
35 | ) => Promise;
36 |
37 | /**
38 | * Queries
39 | */
40 |
41 | user: (where: UserWhereUniqueInput) => UserNullablePromise;
42 | users: (args?: {
43 | where?: UserWhereInput;
44 | orderBy?: UserOrderByInput;
45 | skip?: Int;
46 | after?: String;
47 | before?: String;
48 | first?: Int;
49 | last?: Int;
50 | }) => FragmentableArray;
51 | usersConnection: (args?: {
52 | where?: UserWhereInput;
53 | orderBy?: UserOrderByInput;
54 | skip?: Int;
55 | after?: String;
56 | before?: String;
57 | first?: Int;
58 | last?: Int;
59 | }) => UserConnectionPromise;
60 | node: (args: { id: ID_Output }) => Node;
61 |
62 | /**
63 | * Mutations
64 | */
65 |
66 | createUser: (data: UserCreateInput) => UserPromise;
67 | updateUser: (args: {
68 | data: UserUpdateInput;
69 | where: UserWhereUniqueInput;
70 | }) => UserPromise;
71 | updateManyUsers: (args: {
72 | data: UserUpdateManyMutationInput;
73 | where?: UserWhereInput;
74 | }) => BatchPayloadPromise;
75 | upsertUser: (args: {
76 | where: UserWhereUniqueInput;
77 | create: UserCreateInput;
78 | update: UserUpdateInput;
79 | }) => UserPromise;
80 | deleteUser: (where: UserWhereUniqueInput) => UserPromise;
81 | deleteManyUsers: (where?: UserWhereInput) => BatchPayloadPromise;
82 |
83 | /**
84 | * Subscriptions
85 | */
86 |
87 | $subscribe: Subscription;
88 | }
89 |
90 | export interface Subscription {
91 | user: (
92 | where?: UserSubscriptionWhereInput
93 | ) => UserSubscriptionPayloadSubscription;
94 | }
95 |
96 | export interface ClientConstructor {
97 | new (options?: BaseClientOptions): T;
98 | }
99 |
100 | /**
101 | * Types
102 | */
103 |
104 | export type UserOrderByInput =
105 | | "id_ASC"
106 | | "id_DESC"
107 | | "username_ASC"
108 | | "username_DESC"
109 | | "email_ASC"
110 | | "email_DESC"
111 | | "name_ASC"
112 | | "name_DESC"
113 | | "createdAt_ASC"
114 | | "createdAt_DESC"
115 | | "updatedAt_ASC"
116 | | "updatedAt_DESC";
117 |
118 | export type MutationType = "CREATED" | "UPDATED" | "DELETED";
119 |
120 | export type UserWhereUniqueInput = AtLeastOne<{
121 | id: Maybe;
122 | username?: Maybe;
123 | email?: Maybe;
124 | }>;
125 |
126 | export interface UserWhereInput {
127 | id?: Maybe;
128 | id_not?: Maybe;
129 | id_in?: Maybe;
130 | id_not_in?: Maybe;
131 | id_lt?: Maybe;
132 | id_lte?: Maybe;
133 | id_gt?: Maybe;
134 | id_gte?: Maybe;
135 | id_contains?: Maybe;
136 | id_not_contains?: Maybe;
137 | id_starts_with?: Maybe;
138 | id_not_starts_with?: Maybe;
139 | id_ends_with?: Maybe;
140 | id_not_ends_with?: Maybe;
141 | username?: Maybe;
142 | username_not?: Maybe;
143 | username_in?: Maybe;
144 | username_not_in?: Maybe;
145 | username_lt?: Maybe;
146 | username_lte?: Maybe;
147 | username_gt?: Maybe;
148 | username_gte?: Maybe;
149 | username_contains?: Maybe;
150 | username_not_contains?: Maybe;
151 | username_starts_with?: Maybe;
152 | username_not_starts_with?: Maybe;
153 | username_ends_with?: Maybe;
154 | username_not_ends_with?: Maybe;
155 | email?: Maybe;
156 | email_not?: Maybe;
157 | email_in?: Maybe;
158 | email_not_in?: Maybe;
159 | email_lt?: Maybe;
160 | email_lte?: Maybe;
161 | email_gt?: Maybe;
162 | email_gte?: Maybe;
163 | email_contains?: Maybe;
164 | email_not_contains?: Maybe;
165 | email_starts_with?: Maybe;
166 | email_not_starts_with?: Maybe;
167 | email_ends_with?: Maybe;
168 | email_not_ends_with?: Maybe;
169 | name?: Maybe;
170 | name_not?: Maybe;
171 | name_in?: Maybe;
172 | name_not_in?: Maybe;
173 | name_lt?: Maybe;
174 | name_lte?: Maybe;
175 | name_gt?: Maybe;
176 | name_gte?: Maybe;
177 | name_contains?: Maybe;
178 | name_not_contains?: Maybe;
179 | name_starts_with?: Maybe;
180 | name_not_starts_with?: Maybe;
181 | name_ends_with?: Maybe;
182 | name_not_ends_with?: Maybe;
183 | createdAt?: Maybe;
184 | createdAt_not?: Maybe;
185 | createdAt_in?: Maybe;
186 | createdAt_not_in?: Maybe;
187 | createdAt_lt?: Maybe;
188 | createdAt_lte?: Maybe;
189 | createdAt_gt?: Maybe;
190 | createdAt_gte?: Maybe;
191 | updatedAt?: Maybe;
192 | updatedAt_not?: Maybe;
193 | updatedAt_in?: Maybe;
194 | updatedAt_not_in?: Maybe;
195 | updatedAt_lt?: Maybe;
196 | updatedAt_lte?: Maybe;
197 | updatedAt_gt?: Maybe;
198 | updatedAt_gte?: Maybe;
199 | AND?: Maybe;
200 | }
201 |
202 | export interface UserCreateInput {
203 | id?: Maybe;
204 | username: String;
205 | email: String;
206 | name: String;
207 | }
208 |
209 | export interface UserUpdateInput {
210 | username?: Maybe;
211 | email?: Maybe;
212 | name?: Maybe;
213 | }
214 |
215 | export interface UserUpdateManyMutationInput {
216 | username?: Maybe;
217 | email?: Maybe;
218 | name?: Maybe;
219 | }
220 |
221 | export interface UserSubscriptionWhereInput {
222 | mutation_in?: Maybe;
223 | updatedFields_contains?: Maybe;
224 | updatedFields_contains_every?: Maybe;
225 | updatedFields_contains_some?: Maybe;
226 | node?: Maybe;
227 | AND?: Maybe;
228 | }
229 |
230 | export interface NodeNode {
231 | id: ID_Output;
232 | }
233 |
234 | export interface User {
235 | id: ID_Output;
236 | username: String;
237 | email: String;
238 | name: String;
239 | createdAt: DateTimeOutput;
240 | updatedAt: DateTimeOutput;
241 | }
242 |
243 | export interface UserPromise extends Promise, Fragmentable {
244 | id: () => Promise;
245 | username: () => Promise;
246 | email: () => Promise;
247 | name: () => Promise;
248 | createdAt: () => Promise;
249 | updatedAt: () => Promise;
250 | }
251 |
252 | export interface UserSubscription
253 | extends Promise>,
254 | Fragmentable {
255 | id: () => Promise>;
256 | username: () => Promise>;
257 | email: () => Promise>;
258 | name: () => Promise>;
259 | createdAt: () => Promise>;
260 | updatedAt: () => Promise>;
261 | }
262 |
263 | export interface UserNullablePromise
264 | extends Promise,
265 | Fragmentable {
266 | id: () => Promise;
267 | username: () => Promise;
268 | email: () => Promise;
269 | name: () => Promise;
270 | createdAt: () => Promise;
271 | updatedAt: () => Promise;
272 | }
273 |
274 | export interface UserConnection {
275 | pageInfo: PageInfo;
276 | edges: UserEdge[];
277 | }
278 |
279 | export interface UserConnectionPromise
280 | extends Promise,
281 | Fragmentable {
282 | pageInfo: () => T;
283 | edges: >() => T;
284 | aggregate: () => T;
285 | }
286 |
287 | export interface UserConnectionSubscription
288 | extends Promise>,
289 | Fragmentable {
290 | pageInfo: () => T;
291 | edges: >>() => T;
292 | aggregate: () => T;
293 | }
294 |
295 | export interface PageInfo {
296 | hasNextPage: Boolean;
297 | hasPreviousPage: Boolean;
298 | startCursor?: String;
299 | endCursor?: String;
300 | }
301 |
302 | export interface PageInfoPromise extends Promise, Fragmentable {
303 | hasNextPage: () => Promise;
304 | hasPreviousPage: () => Promise;
305 | startCursor: () => Promise;
306 | endCursor: () => Promise;
307 | }
308 |
309 | export interface PageInfoSubscription
310 | extends Promise>,
311 | Fragmentable {
312 | hasNextPage: () => Promise>;
313 | hasPreviousPage: () => Promise>;
314 | startCursor: () => Promise>;
315 | endCursor: () => Promise>;
316 | }
317 |
318 | export interface UserEdge {
319 | node: User;
320 | cursor: String;
321 | }
322 |
323 | export interface UserEdgePromise extends Promise, Fragmentable {
324 | node: () => T;
325 | cursor: () => Promise;
326 | }
327 |
328 | export interface UserEdgeSubscription
329 | extends Promise>,
330 | Fragmentable {
331 | node: () => T;
332 | cursor: () => Promise>;
333 | }
334 |
335 | export interface AggregateUser {
336 | count: Int;
337 | }
338 |
339 | export interface AggregateUserPromise
340 | extends Promise,
341 | Fragmentable {
342 | count: () => Promise;
343 | }
344 |
345 | export interface AggregateUserSubscription
346 | extends Promise>,
347 | Fragmentable {
348 | count: () => Promise>;
349 | }
350 |
351 | export interface BatchPayload {
352 | count: Long;
353 | }
354 |
355 | export interface BatchPayloadPromise
356 | extends Promise,
357 | Fragmentable {
358 | count: () => Promise;
359 | }
360 |
361 | export interface BatchPayloadSubscription
362 | extends Promise>,
363 | Fragmentable {
364 | count: () => Promise>;
365 | }
366 |
367 | export interface UserSubscriptionPayload {
368 | mutation: MutationType;
369 | node: User;
370 | updatedFields: String[];
371 | previousValues: UserPreviousValues;
372 | }
373 |
374 | export interface UserSubscriptionPayloadPromise
375 | extends Promise,
376 | Fragmentable {
377 | mutation: () => Promise;
378 | node: () => T;
379 | updatedFields: () => Promise;
380 | previousValues: () => T;
381 | }
382 |
383 | export interface UserSubscriptionPayloadSubscription
384 | extends Promise>,
385 | Fragmentable {
386 | mutation: () => Promise>;
387 | node: () => T;
388 | updatedFields: () => Promise>;
389 | previousValues: () => T;
390 | }
391 |
392 | export interface UserPreviousValues {
393 | id: ID_Output;
394 | username: String;
395 | email: String;
396 | name: String;
397 | createdAt: DateTimeOutput;
398 | updatedAt: DateTimeOutput;
399 | }
400 |
401 | export interface UserPreviousValuesPromise
402 | extends Promise,
403 | Fragmentable {
404 | id: () => Promise;
405 | username: () => Promise;
406 | email: () => Promise;
407 | name: () => Promise;
408 | createdAt: () => Promise;
409 | updatedAt: () => Promise;
410 | }
411 |
412 | export interface UserPreviousValuesSubscription
413 | extends Promise>,
414 | Fragmentable {
415 | id: () => Promise>;
416 | username: () => Promise>;
417 | email: () => Promise>;
418 | name: () => Promise>;
419 | createdAt: () => Promise>;
420 | updatedAt: () => Promise>;
421 | }
422 |
423 | /*
424 | The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID.
425 | */
426 | export type ID_Input = string | number;
427 | export type ID_Output = string;
428 |
429 | /*
430 | The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
431 | */
432 | export type String = string;
433 |
434 | /*
435 | DateTime scalar input type, allowing Date
436 | */
437 | export type DateTimeInput = Date | string;
438 |
439 | /*
440 | DateTime scalar output type, which is always a string
441 | */
442 | export type DateTimeOutput = string;
443 |
444 | /*
445 | The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
446 | */
447 | export type Int = number;
448 |
449 | /*
450 | The `Boolean` scalar type represents `true` or `false`.
451 | */
452 | export type Boolean = boolean;
453 |
454 | export type Long = string;
455 |
456 | /**
457 | * Model Metadata
458 | */
459 |
460 | export const models: Model[] = [
461 | {
462 | name: "User",
463 | embedded: false
464 | }
465 | ];
466 |
467 | /**
468 | * Type Defs
469 | */
470 |
471 | export const Prisma = makePrismaClientClass>({
472 | typeDefs,
473 | models,
474 | endpoint: `http://localhost:4466/`
475 | });
476 | export const prisma = new Prisma();
477 |
--------------------------------------------------------------------------------